国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

深入解析Java的Spring框架中的混合事務(wù)與bean的區(qū)分

2019-11-26 14:39:18
字體:
供稿:網(wǎng)友

混合事務(wù)
在ORM框架的事務(wù)管理器的事務(wù)內(nèi),使用JdbcTemplate執(zhí)行SQL是不會(huì)納入事務(wù)管理的。
下面進(jìn)行源碼分析,看為什么必須要在DataSourceTransactionManager的事務(wù)內(nèi)使用JdbcTemplate。

1.開啟事務(wù)
DataSourceTransactionManager

     protected void doBegin(Object transaction,TransactionDefinition definition) {          DataSourceTransactionObjecttxObject = (DataSourceTransactionObject) transaction;          Connection con = null;           try {              if(txObject.getConnectionHolder() == null ||                        txObject.getConnectionHolder().isSynchronizedWithTransaction()){                   ConnectionnewCon = this.dataSource.getConnection();                   if(logger.isDebugEnabled()) {                        logger.debug("AcquiredConnection [" + newCon + "] for JDBC transaction");                   }                   txObject.setConnectionHolder(newConnectionHolder(newCon), true);              }               txObject.getConnectionHolder().setSynchronizedWithTransaction(true);              con =txObject.getConnectionHolder().getConnection();               IntegerpreviousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con,definition);              txObject.setPreviousIsolationLevel(previousIsolationLevel);               // Switch to manualcommit if necessary. This is very expensive in some JDBC drivers,              // so we don't wantto do it unnecessarily (for example if we've explicitly              // configured theconnection pool to set it already).              if(con.getAutoCommit()) {                   txObject.setMustRestoreAutoCommit(true);                   if(logger.isDebugEnabled()) {                        logger.debug("SwitchingJDBC Connection [" + con + "] to manual commit");                   }                   con.setAutoCommit(false);              }              txObject.getConnectionHolder().setTransactionActive(true);               int timeout =determineTimeout(definition);              if (timeout !=TransactionDefinition.TIMEOUT_DEFAULT) {                   txObject.getConnectionHolder().setTimeoutInSeconds(timeout);              }               // Bind the sessionholder to the thread.              if(txObject.isNewConnectionHolder()) {                   TransactionSynchronizationManager.bindResource(getDataSource(),txObject.getConnectionHolder());              }          }           catch (Exception ex) {              DataSourceUtils.releaseConnection(con,this.dataSource);              throw newCannotCreateTransactionException("Could not open JDBC Connection fortransaction", ex);          }     }

doBegin()方法會(huì)以數(shù)據(jù)源名為Key,ConnectionHolder(保存著連接)為Value,將已經(jīng)開啟事務(wù)的數(shù)據(jù)庫連接綁定到一個(gè)ThreadLocal變量上。

2.綁定連接

     public static void bindResource(Objectkey, Object value) throws IllegalStateException {          Object actualKey =TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);          Assert.notNull(value,"Value must not be null");          Map<Object, Object> map = resources.get();          // set ThreadLocal Map ifnone found          if (map == null) {              map = newHashMap<Object, Object>();              resources.set(map);          }          Object oldValue = map.put(actualKey, value);          // Transparently suppress aResourceHolder that was marked as void...          if (oldValue instanceofResourceHolder && ((ResourceHolder) oldValue).isVoid()) {              oldValue = null;          }          if (oldValue != null) {              throw newIllegalStateException("Already value [" + oldValue + "] for key[" +                        actualKey+ "] bound to thread [" + Thread.currentThread().getName() +"]");          }          if (logger.isTraceEnabled()){              logger.trace("Boundvalue [" + value + "] for key [" + actualKey + "] to thread[" +                        Thread.currentThread().getName()+ "]");          }     }

resources變量就是上面提到的ThreadLocal變量,這樣后續(xù)JdbcTemplate就可以用DataSource作為Key,查找到這個(gè)數(shù)據(jù)庫連接。

3.執(zhí)行SQL
JdbcTemplate

     public Objectexecute(PreparedStatementCreator psc, PreparedStatementCallback action)              throwsDataAccessException {           Assert.notNull(psc,"PreparedStatementCreator must not be null");          Assert.notNull(action,"Callback object must not be null");          if (logger.isDebugEnabled()){              String sql =getSql(psc);              logger.debug("Executingprepared SQL statement" + (sql != null ? " [" + sql +"]" : ""));          }           Connection con = DataSourceUtils.getConnection(getDataSource());          PreparedStatement ps = null;          try {              Connection conToUse= con;              if(this.nativeJdbcExtractor != null &&                        this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()){                   conToUse =this.nativeJdbcExtractor.getNativeConnection(con);              }              ps =psc.createPreparedStatement(conToUse);              applyStatementSettings(ps);              PreparedStatementpsToUse = ps;              if(this.nativeJdbcExtractor != null) {                   psToUse =this.nativeJdbcExtractor.getNativePreparedStatement(ps);              }              Object result =action.doInPreparedStatement(psToUse);              handleWarnings(ps);              return result;          }          catch (SQLException ex) {              // ReleaseConnection early, to avoid potential connection pool deadlock              // in the case whenthe exception translator hasn't been initialized yet.              if (psc instanceofParameterDisposer) {                   ((ParameterDisposer)psc).cleanupParameters();              }              String sql =getSql(psc);              psc = null;              JdbcUtils.closeStatement(ps);              ps = null;              DataSourceUtils.releaseConnection(con,getDataSource());              con = null;              throwgetExceptionTranslator().translate("PreparedStatementCallback", sql,ex);          }          finally {              if (psc instanceofParameterDisposer) {                   ((ParameterDisposer)psc).cleanupParameters();              }              JdbcUtils.closeStatement(ps);              DataSourceUtils.releaseConnection(con,getDataSource());          }     }


4.獲得連接
DataSourceUtils

    public static Connection doGetConnection(DataSourcedataSource) throws SQLException {          Assert.notNull(dataSource,"No DataSource specified");           ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);          if (conHolder != null&& (conHolder.hasConnection() ||conHolder.isSynchronizedWithTransaction())) {              conHolder.requested();              if(!conHolder.hasConnection()) {                   logger.debug("Fetchingresumed JDBC Connection from DataSource");                   conHolder.setConnection(dataSource.getConnection());              }              returnconHolder.getConnection();          }          // Else we either got noholder or an empty thread-bound holder here.           logger.debug("FetchingJDBC Connection from DataSource");          Connection con =dataSource.getConnection();           if (TransactionSynchronizationManager.isSynchronizationActive()){              logger.debug("Registeringtransaction synchronization for JDBC Connection");              // Use sameConnection for further JDBC actions within the transaction.              // Thread-boundobject will get removed by synchronization at transaction completion.              ConnectionHolderholderToUse = conHolder;              if (holderToUse ==null) {                   holderToUse= new ConnectionHolder(con);              }              else {                   holderToUse.setConnection(con);              }              holderToUse.requested();              TransactionSynchronizationManager.registerSynchronization(                        newConnectionSynchronization(holderToUse, dataSource));              holderToUse.setSynchronizedWithTransaction(true);              if (holderToUse !=conHolder) {                   TransactionSynchronizationManager.bindResource(dataSource,holderToUse);              }          }           return con;     }

由此可見,DataSourceUtils也是通過TransactionSynchronizationManager獲得連接的。所以只要JdbcTemplate與DataSourceTransactionManager有相同的DataSource,就一定能得到相同的數(shù)據(jù)庫連接,自然就能正確地提交、回滾事務(wù)。
 
再以Hibernate為例來說明開篇提到的問題,看看為什么ORM框架的事務(wù)管理器不能管理JdbcTemplate。

5 ORM事務(wù)管理器
HibernateTransactionManager

if(txObject.isNewSessionHolder()) {      TransactionSynchronizationManager.bindResource(getSessionFactory(),txObject.getSessionHolder()); } 

因?yàn)镺RM框架都不是直接將DataSource注入到TransactionManager中使用的,而是像上面Hibernate事務(wù)管理器一樣,使用自己的SessionFactory等對(duì)象來操作DataSource。所以盡管可能SessionFactory和JdbcTemplate底層都是一樣的數(shù)據(jù)源,但因?yàn)樵赥ransactionSynchronizationManager中綁定時(shí)使用了不同的Key(一個(gè)是sessionFactory名,一個(gè)是dataSource名),所以JdbcTemplate執(zhí)行時(shí)是拿不到ORM事務(wù)管理器開啟事務(wù)的那個(gè)數(shù)據(jù)庫連接的。


bean的區(qū)分
一個(gè)公共工程中的Spring配置文件,可能會(huì)被多個(gè)工程引用。因?yàn)槊總€(gè)工程可能只需要公共工程中的一部分Bean,所以這些工程的Spring容器啟動(dòng)時(shí),需要區(qū)分開哪些Bean要?jiǎng)?chuàng)建出來。
1.應(yīng)用實(shí)例
以Apache開源框架Jetspeed中的一段配置為例:page-manager.xml
 

 <bean name="xmlPageManager"class="org.apache.jetspeed.page.psml.CastorXmlPageManager"init-method="init" destroy-method="destroy">  <meta key="j2:cat" value="xmlPageManager orpageSerializer" />  <constructor-arg index="0">   <ref bean="IdGenerator"/>  </constructor-arg>  <constructor-arg index="1">   <refbean="xmlDocumentHandlerFactory" />  </constructor-arg>  …… </bean>  <bean id="dbPageManager"class="org.apache.jetspeed.page.impl.DatabasePageManager"init-method="init" destroy-method="destroy">  <meta key="j2:cat" value="dbPageManager orpageSerializer" />  <!-- OJB configuration file resourcepath -->  <constructor-arg index="0">   <value>JETSPEED-INF/ojb/page-manager-repository.xml</value>  </constructor-arg>  <!-- fragment id generator -->  <constructor-arg index="1">   <ref bean="IdGenerator"/>  </constructor-arg>  …… </bean>

2.Bean過濾器
JetspeedBeanDefinitionFilter在Spring容器解析每個(gè)Bean定義時(shí),會(huì)取出上面Bean配置中j2:cat對(duì)應(yīng)的值,例如dbPageManageror pageSerializer。然后將這部分作為正則表達(dá)式與當(dāng)前的Key(從配置文件中讀出)進(jìn)行匹配。只有匹配上的Bean,才會(huì)被Spring容器創(chuàng)建出來。
 
JetspeedBeanDefinitionFilter

  public boolean match(BeanDefinition bd)  {    String beanCategoriesExpression = (String)bd.getAttribute(CATEGORY_META_KEY);    boolean matched = true;    if (beanCategoriesExpression != null)    {      matched = ((matcher != null)&& matcher.match(beanCategoriesExpression));    }    return matched;}   public void registerDynamicAlias(BeanDefinitionRegistry registry, String beanName,BeanDefinition bd)  {    String aliases =(String)bd.getAttribute(ALIAS_META_KEY);    if (aliases != null)    {      StringTokenizer st = newStringTokenizer(aliases, " ,");      while (st.hasMoreTokens())      {        String alias = st.nextToken();        if (!alias.equals(beanName))        {          registry.registerAlias(beanName, alias);        }      }    }  }

match()方法中的CATEGORY_META_KEY的值就是j2:cat,matcher類中保存的就是當(dāng)前的Key,并負(fù)責(zé)將當(dāng)前Key與每個(gè)Bean的進(jìn)行正則表達(dá)式匹配。
registerDynamicAlias的作用是:在Bean匹配成功后,定制的Spring容器會(huì)調(diào)用此方法為Bean注冊(cè)別名。詳見下面1.3中的源碼。

3.定制Spring容器
定制一個(gè)Spring容器,重寫registerBeanDefinition()方法,在Spring注冊(cè)Bean時(shí)進(jìn)行攔截。

public class FilteringXmlWebApplicationContextextends XmlWebApplicationContext{  private JetspeedBeanDefinitionFilterfilter;    publicFilteringXmlWebApplicationContext(JetspeedBeanDefinitionFilter filter, String[]configLocations, Properties initProperties, ServletContext servletContext)  {    this(filter, configLocations,initProperties, servletContext, null);  }    publicFilteringXmlWebApplicationContext(JetspeedBeanDefinitionFilter filter, String[]configLocations, Properties initProperties, ServletContext servletContext,ApplicationContext parent)  {    super();    if (parent != null)    {      this.setParent(parent);    }    if (initProperties != null)    {      PropertyPlaceholderConfigurer ppc =new PropertyPlaceholderConfigurer();      ppc.setIgnoreUnresolvablePlaceholders(true);      ppc.setSystemPropertiesMode(PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_FALLBACK);      ppc.setProperties(initProperties);      addBeanFactoryPostProcessor(ppc);    }    setConfigLocations(configLocations);    setServletContext(servletContext);    this.filter = filter;  }    protected DefaultListableBeanFactorycreateBeanFactory()  {    return new FilteringListableBeanFactory(filter,getInternalParentBeanFactory());  }} public classFilteringListableBeanFactory extends DefaultListableBeanFactory{  private JetspeedBeanDefinitionFilterfilter;    public FilteringListableBeanFactory(JetspeedBeanDefinitionFilterfilter, BeanFactory parentBeanFactory)  {    super(parentBeanFactory);    this.filter = filter;    if (this.filter == null)    {      this.filter = newJetspeedBeanDefinitionFilter();    }    this.filter.init();  }   /**   * Override of the registerBeanDefinitionmethod to optionally filter out a BeanDefinition and   * if requested dynamically register anbean alias   */  public void registerBeanDefinition(StringbeanName, BeanDefinition bd)      throws BeanDefinitionStoreException  {    if (filter.match(bd))    {      super.registerBeanDefinition(beanName, bd);      if (filter != null)      {        filter.registerDynamicAlias(this, beanName, bd);      }    }  }}

4.為Bean起別名
使用BeanReferenceFactoryBean工廠Bean,將上面配置的兩個(gè)Bean(xmlPageManager和dbPageManager)包裝起來。將Key配成各自的,實(shí)現(xiàn)通過配置當(dāng)前Key來切換兩種實(shí)現(xiàn)。別名都配成一個(gè),這樣引用他們的Bean就直接引用這個(gè)別名就行了。例如下面的PageLayoutComponent。
 
page-manager.xml

<bean class="org.springframework.beans.factory.config.BeanReferenceFactoryBean">  <meta key="j2:cat"value="xmlPageManager" />  <meta key="j2:alias"value="org.apache.jetspeed.page.PageManager" />  <propertyname="targetBeanName" value="xmlPageManager" /> </bean>  <bean class="org.springframework.beans.factory.config.BeanReferenceFactoryBean">  <meta key="j2:cat"value="dbPageManager" />  <meta key="j2:alias"value="org.apache.jetspeed.page.PageManager" />  <propertyname="targetBeanName" value="dbPageManager" /> </bean>  <bean id="org.apache.jetspeed.layout.PageLayoutComponent"  class="org.apache.jetspeed.layout.impl.PageLayoutComponentImpl">  <meta key="j2:cat"value="default" />  <constructor-arg index="0">   <refbean="org.apache.jetspeed.page.PageManager" />  </constructor-arg>  <constructor-arg index="1">   <value>jetspeed-layouts::VelocityOneColumn</value>  </constructor-arg> </bean> 

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 辽中县| 兖州市| 宁化县| 余江县| 巫溪县| 临泉县| 城步| 宜阳县| 安福县| 石阡县| 玉门市| 枣庄市| 军事| 丰台区| 岫岩| 同心县| 阳西县| 金湖县| 祥云县| 嘉鱼县| 大悟县| 长沙市| 文安县| 雷波县| 扎囊县| 湄潭县| 巴彦淖尔市| 庄河市| 北碚区| 天祝| 张北县| 南丰县| 合江县| 隆昌县| 大方县| 上虞市| 曲水县| 长宁区| 尖扎县| 巴林左旗| 弥渡县|