Spring初始化ApplicationContext线程托管实际运用架构构思

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring初始化ApplicationContext线程托管实际运用架构构思相关的知识,希望对你有一定的参考价值。

今天我分享一个技术点,利用Spring初始化+线程接管进行程序启动后保持会话状态。

 

先来一段@test单元测试注解,后台开发的很熟悉,这是测试局部代码用的:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")

RunWith和ContextConfiguration的源码和功能就不细解释了,不熟悉的可以去翻翻源码。

1:我来一段@Test单元测试操作数据的代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class RunTest {

    @Autowired
    CustomerServices custServer;//services层,注入了dao层
    
    @Test
    public void test(){
        Customer customer = new Customer();
        customer.setCname("余根海");
        customer.setCinfo("我是一条鱼!");
        customer.setCage("25");
        custServer.insertTest(customer);
    }
}

(1) 跑一遍单元测试,INFO日志输出:

16:34:30,296  INFO DefaultTestContextBootstrapper:259 - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
16:34:30,313  INFO DefaultTestContextBootstrapper:185 - Using TestExecutionListeners: [or[email protected]146ba0ac, org.springframework.test[email protected]4dfa3a9d, org.springframewor[email protected]6eebc39e, org.springfra[email protected]464bee09, org.springframew[email protected]f6c48ac, org.sp[email protected]13deb50e]
16:34:30,390  INFO XmlBeanDefinitionReader:317 - Loading XML bean definitions from class path resource [applicationContext.xml]
16:34:30,685  INFO GenericApplicationContext:578 - Refreshing [email protected]0736d9: startup date [Mon Aug 07 16:34:30 CST 2017]; root of context hierarchy
16:34:30,777  INFO PropertyPlaceholderConfigurer:172 - Loading properties file from class path resource [init.properties]
16:34:30,970  INFO MLog:212 - MLog clients using slf4j logging.
16:34:31,123  INFO C3P0Registry:212 - Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
16:34:31,296  INFO Version:37 - HHH000412: Hibernate Core {5.0.8.Final}
16:34:31,298  INFO Environment:213 - HHH000206: hibernate.properties not found
16:34:31,299  INFO Environment:317 - HHH000021: Bytecode provider name : javassist
16:34:31,336  INFO Version:66 - HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
16:34:31,416  INFO AbstractPoolBackedDataSource:212 - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 5, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 30000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1bqudj89p1g46yu313x3lsl|41ab013, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1bqudj89p1g46yu313x3lsl|41ab013, idleConnectionTestPeriod -> 30, initialPoolSize -> 5, jdbcUrl -> jdbc:mysql://127.0.0.1:3306/yugh?useUnicode=true&characterEncoding=utf-8, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 30, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 5, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
16:34:31,633  INFO Dialect:156 - HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
16:34:31,671  INFO LobCreatorBuilderImpl:98 - HHH000423: Disabling contextual LOB creation as JDBC driver reported JDBC version [3] less than 4
16:34:32,027  INFO SchemaUpdate:105 - HHH000228: Running hbm2ddl schema update
16:34:32,120  INFO HibernateTransactionManager:357 - Using DataSource [com.mchange.v2.c3p0.ComboPooledDataSource[ identityToken -> 1bqudj89p1g46yu313x3lsl|41ab013, dataSourceName -> 1bqudj89p1g46yu313x3lsl|41ab013 ]] of Hibernate SessionFactory for HibernateTransactionManager
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into customer (cage, cinfo, cname, id) values (?, ?, ?, ?)
16:34:32,487  INFO GenericApplicationContext:960 - Closing [email protected]0736d9: startup date [Mon Aug 07 16:34:30 CST 2017]; root of context hierarchy

 

(2) 可以看到@Test单元测试初始化了一遍Spring和Spring文件里的所有配置,因为要操作数据对象,在大部分后台单元测试中,目的都是数据交互。

@Test总结:通过@Test单元测试可以得到初始化Spring以及加载完毕已经配置到Spring文件中所有正确的配置参数,虽然它运行是短暂的。

请忽略上面的Hibernate,虽然上面测试是hibernate作为持久层,但本文所有知识点都是Spring,包括用Spring的数据控制替换其他持久层。

2:得到了初始化的目的,下面就得用线程接管初始化后的操作,几个关键点:(1)初始化spring文件  (2)事务用谁,怎么用,能否成功?(3)线程何时去接管?

静态对象是保持对象唯一,保持初始化的唯一,因为用@TEST单元测试时候,注解RunWith是把测试类和方法名都反射了,已经得到了真实的功能类入口,而我们自己初始化Spring启动程序不用@TEST注解就要定义全局静态对象。

(1):初始化Spring文件,文件加载方式是我常用的:

 

public static final ApplicationContext CONTEXT = new ClassPathXmlApplicationContext("applicationContext.xml");

 

(2):事务采用Spring的:dataSource是数据库的bean

<bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
    </bean>

写下获取事务的静态对象:

static final PlatformTransactionManager MANAGER = (PlatformTransactionManager)CONTEXT.getBean("transactionManager");

(3):数据控制对象jdbctemplate,前身jdbcAccessor,源码用的基于sql的DataSource:

public static final JdbcTemplate JDBC_TEMPLATE = (JdbcTemplate)CONTEXT.getBean("jdbcTemplate");
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
    </bean>

(4) 到这里的配置菜单有:初始化Spring文件 + 事务 + 数据控制对象,虽然全部依赖Spring,但已经是一个很优秀的纯后台项目架构了,虽然在持久层没有hibernate那么强大各种操作方式。

 

最后来一段Spring初始化日志:

09:43:35,326  INFO ClassPathXmlApplicationContext:578 - Refreshing org[email protected]7ce6a65d: startup date [Tue Aug 08 09:43:35 CST 2017]; root of context hierarchy
09:43:35,368  INFO XmlBeanDefinitionReader:317 - Loading XML bean definitions from class path resource [applicationContext.xml]
09:44:21,994  INFO PropertyPlaceholderConfigurer:172 - Loading properties file from class path resource [init.properties]
09:44:24,847  INFO MLog:212 - MLog clients using slf4j logging.
09:44:25,007  INFO C3P0Registry:212 - Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
09:44:33,517  INFO AbstractPoolBackedDataSource:212 - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 5, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 30000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1bqudj89p1h4zfam1rricvn|62e8f862, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1bqudj89p1h4zfam1rricvn|62e8f862, idleConnectionTestPeriod -> 30, initialPoolSize -> 5, jdbcUrl -> jdbc:mysql://127.0.0.1:3306/yugh?useUnicode=true&characterEncoding=utf-8, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 30, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 5, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]

总结构思:(1)利用代码初始加载Spring文件 ——>  (2)主入口定义一个main方法,各种校验运行后,运行 ——> (3)调用静态的初始化代码,开始初始 ——> (4) 初始完毕后调用数据控制 ——> (5)通过一系列后台操作后,线程开始接管 ——> (6)代码控制它保持后台运行会话状态。

 

以上是关于Spring初始化ApplicationContext线程托管实际运用架构构思的主要内容,如果未能解决你的问题,请参考以下文章

Spring的beanFactory与ApplicationContext区别

Spring + Hibernate + JPA [关闭]

Spring Web 项目Junit测试报错问题

ContextLoader和ContextLoaderListener

ContextLoader和ContextLoaderListener

面试阿里,字节,美团必看的Spring的Bean管理详解