今天我分享一個技術點,利用Spring初始化+線程接管進行程式啟動後保持會話狀態。 先來一段@test單元測試註解,後臺開發的很熟悉,這是測試局部代碼用的: RunWith和ContextConfiguration的源碼和功能就不細解釋了,不熟悉的可以去翻翻源碼。 1:我來一段@Test單元測試操作 ...
今天我分享一個技術點,利用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: [org.springframework.test.context.web.ServletTestExecutionListener@146ba0ac, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@4dfa3a9d, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@6eebc39e, org.springframework.test.context.support.DirtiesContextTestExecutionListener@464bee09, org.springframework.test.context.transaction.TransactionalTestExecutionListener@f6c48ac, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@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 org.springframework.context.support.GenericApplicationContext@370736d9: 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 org.springframework.context.support.GenericApplicationContext@370736d9: 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 jdbcTemplate= (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.springframework.context.support.ClassPathXmlApplicationContext@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)代碼控制它保持後臺運行會話狀態。