深入解析Java的Spring框架中的混合事务与bean的区分_java

混合事务
在ORM框架的事务管理器的事务内,使用JdbcTemplate执行SQL是不会纳入事务管理的。
下面进行源码分析,看为什么必须要在DataSourceTransactionManager的事务内使用JdbcTemplate。

1.开启事务
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()方法会以数据源名为Key,ConnectionHolder(保存着连接)为Value,将已经开启事务的数据库连接绑定到一个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变量,这样后续JdbcTemplate就可以用DataSource作为Key,查找到这个数据库连接。

3.执行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,就一定能得到相同的数据库连接,自然就能正确地提交、回滚事务。
 
再以Hibernate为例来说明开篇提到的问题,看看为什么ORM框架的事务管理器不能管理JdbcTemplate。

5 ORM事务管理器
HibernateTransactionManager

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

因为ORM框架都不是直接将DataSource注入到TransactionManager中使用的,而是像上面Hibernate事务管理器一样,使用自己的SessionFactory等对象来操作DataSource。所以尽管可能SessionFactory和JdbcTemplate底层都是一样的数据源,但因为在TransactionSynchronizationManager中绑定时使用了不同的Key(一个是sessionFactory名,一个是dataSource名),所以JdbcTemplate执行时是拿不到ORM事务管理器开启事务的那个数据库连接的。

bean的区分
一个公共工程中的Spring配置文件,可能会被多个工程引用。因为每个工程可能只需要公共工程中的一部分Bean,所以这些工程的Spring容器启动时,需要区分开哪些Bean要创建出来。
1.应用实例
以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容器解析每个Bean定义时,会取出上面Bean配置中j2:cat对应的值,例如dbPageManageror pageSerializer。然后将这部分作为正则表达式与当前的Key(从配置文件中读出)进行匹配。只有匹配上的Bean,才会被Spring容器创建出来。
 
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类中保存的就是当前的Key,并负责将当前Key与每个Bean的进行正则表达式匹配。
registerDynamicAlias的作用是:在Bean匹配成功后,定制的Spring容器会调用此方法为Bean注册别名。详见下面1.3中的源码。

3.定制Spring容器
定制一个Spring容器,重写registerBeanDefinition()方法,在Spring注册Bean时进行拦截。

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,将上面配置的两个Bean(xmlPageManager和dbPageManager)包装起来。将Key配成各自的,实现通过配置当前Key来切换两种实现。别名都配成一个,这样引用他们的Bean就直接引用这个别名就行了。例如下面的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> 

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
, spring
, bean
事务
spring getbean 事务、spring bean、spring bean生命周期、spring bean 配置、spring boot bean,以便于您获取更多的相关知识。

时间: 2024-09-20 16:25:43

深入解析Java的Spring框架中的混合事务与bean的区分_java的相关文章

深入解析Java的Spring框架中bean的依赖注入_java

每一个基于java的应用程序都有一个共同工作来展示给用户看到的内容作为工作的应用几个对象.当编写一个复杂的Java应用程序,应用程序类应该尽可能独立其他Java类来增加重复使用这些类,并独立于其他类别的测试它们,而这样做单元测试的可能性.依赖注入(或有时称为布线)有助于粘合这些类在一起,同时保持他们的独立. 考虑有其中有一个文本编辑器组件的应用程序,要提供拼写检查.标准的代码将看起来像这样:   public class TextEditor { private SpellChecker spe

Java的Spring框架中实现发送邮件功能的核心代码示例_java

Spring中已经封装了邮件操作类,通过spring配置文件可以便捷地注入到controller.action等地方. 下面是配置: <!-- mail sender --> <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl" p:host="${mail.host}" p:port="${mail.por

Java的Spring框架中DAO数据访问对象的使用示例_java

Spring DAO之JDBC  Spring提供的DAO(数据访问对象)支持主要的目的是便于以标准的方式使用不同的数据访问技术, 如JDBC,Hibernate或者JDO等.它不仅可以让你方便地在这些持久化技术间切换, 而且让你在编码的时候不用考虑处理各种技术中特定的异常. 为了便于以一种一致的方式使用各种数据访问技术,如JDBC.JDO和Hibernate, Spring提供了一套抽象DAO类供你扩展.这些抽象类提供了一些方法,通过它们你可以 获得与你当前使用的数据访问技术相关的数据源和其他

深入理解Java的Spring框架中的IOC容器_java

Spring IOC的原型spring框架的基础核心和起点毫无疑问就是IOC,IOC作为spring容器提供的核心技术,成功完成了依赖的反转:从主类的对依赖的主动管理反转为了spring容器对依赖的全局控制. 这样做的好处是什么呢? 当然就是所谓的"解耦"了,可以使得程序的各模块之间的关系更为独立,只需要spring控制这些模块之间的依赖关系并在容器启动和初始化的过程中将依据这些依赖关系创建.管理和维护这些模块就好,如果需要改变模块间的依赖关系的话,甚至都不需要改变程序代码,只需要将更

Java的Spring框架中AOP项目的一般配置和部署教程_java

0.关于AOP面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是Spring框架中的一个重要内容.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. AOP是OOP的延续. 主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等. 主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对

浅析Java的Spring框架中IOC容器容器的应用_java

Spring容器是Spring框架的核心.容器将创建对象,它们连接在一起,配置它们,并从创建到销毁管理他们的整个生命周期.在Spring容器使用依赖注入(DI)来管理组成应用程序的组件.这些对象被称为Spring Beans. 容器获得其上的哪些对象进行实例化,配置和组装通过阅读提供的配置元数据的说明.配置元数据可以通过XML,Java注释或Java代码来表示.下面的图是Spring如何工作的高层次图. Spring IoC容器是利用Java的POJO类和配置元数据的产生完全配置和可执行的系统或

解析Java的Spring框架的基本结构_java

   在java届,有位名叫Rod Johnson的牛人,发现最初的java企业级开发处于混沌状态.    于是,它决心编写一个能够解决问题的通用的基础架构.    因为它深信面向接口编程能够将变化控制到最小,同时也利于扩展和变化.于是,它编写了如下的接口.     在混沌状态最先要创造的是一切对象的母亲BeanFactory,有了它,就能够得到一切它孕育的对象和属性,也就是说首先要造盖亚--大地之母.    有了最初的母亲BeanFactory,johnson想,如果我要得到一组Bean对象而

实例讲解Java的Spring框架中的AOP实现_java

简介面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足. 除了类(classes)以外,AOP提供了 切面.切面对关注点进行模块化,例如横切多个类型和对象的事务管理. (这些关注点术语通常称作 横切(crosscutting) 关注点.) Spring的一个关键的组件就是 AOP框架. 尽管如此,Spring IoC容器并不依赖于AOP,这意味着你可以自由选择是否使用AOP,AOP提供强大的中间件解决方案,这使得Spring IoC容器更加完善.

举例讲解Java的Spring框架中AOP程序设计方式的使用_java

1.什么是AOP AOP是Aspect Oriented Programming的缩写,意思是面向方面编程,AOP实际是GoF设计模式的延续. 2.关于Spring AOP的一些术语: A.切面(Aspect):在Spring AOP中,切面可以使用通用类或者在普通类中以@Aspect 注解(@AspectJ风格)来实现 B.连接点(Joinpoint):在Spring AOP中一个连接点代表一个方法的执行 C.通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作.通