Spring事务管理---上

2022-06-12 14:16:01 浏览数 (1)

  • 源码探究

本文涉及到的AOP组件,例如ProxyFactory等,如果不清楚的建议先去翻看我之前的AOP系列文章


编程式事务管理

回顾一下上面这张图:

  • 通过Spring进行编程式事务管理有两种方式,要么直接使用PlatformTransactionManager,要么使用更方便的TransactionTemplate。

对于编程式事务而言,更推荐使用TransactionTemplate进行编程式事务管理。


使用PlatformTransactionManager进行编程式事务管理

PlatformTransactionManager接口定义了事务界定的基本操作,我们可以直接使用它来进行编程式事务管理。

代码语言:javascript复制
public class TransactionMain {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        test();
    }

    private static void test() {
        DataSource dataSource = getDS();
        JdbcTransactionManager jtm = new JdbcTransactionManager(dataSource);
        //JdbcTransactionManager根据TransactionDefinition信息来进行一些连接属性的设置
        //包括隔离级别和传播行为等
        DefaultTransactionDefinition transactionDef = new DefaultTransactionDefinition();
        //开启一个新事务---此时autocommit已经被设置为了false,并且当前没有事务,这里创建的是一个新事务
        TransactionStatus ts = jtm.getTransaction(transactionDef);
        //进行业务逻辑操作
        try {
            update(dataSource);
            jtm.commit(ts);
        }catch (Exception e){
            jtm.rollback(ts);
            System.out.println("发生异常,我已回滚");
        }
    }

    private static void update(DataSource dataSource) throws Exception {
        JdbcTemplate jt = new JdbcTemplate();
        jt.setDataSource(dataSource);
        jt.update("UPDATE Department SET Dname="大忽悠" WHERE id=6");
        throw new Exception("我是来捣乱的");
    }
}

只要为TransactionManager提供合适的PlatformTransactionManager实现,然后结合TransactionDefinition开启事务,并结合TransactionStatus来回滚或者提交事务,就可以完成针对当前对象的整个事务管理。

PlatformTransactionManager虽然帮助我们用抽象事务操作屏蔽了不同事务管理的API差异,但是如果在程序中大量使用PlatformTransactionManager来进行事务管理,那还是会有很多重复代码。

这个时候就可以借鉴一下Spring的JDBCTemplate的设计思想,使用模板方法模式加callBack相互结合的方式,对直接使用PlatformTransactionManager进行事务管理的代码封装,这就有了更加方便的编程式事务管理方式,即使用TransactionTemplate的编程式事务管理。


使用TransactionTemplate进行编程式事务管理

TransactionTemplate负责对PlatformTransactionManager的固定事务操作和异常处理进行模板化封装。开发人员更多地关注与通过相应的Callback接口提供具体的事务界定内容即可。

Spring为TransactionTemplate提供了两个回调接口,分别为TransactionCallback和TransactionCallbackWithoutResult,二者的区别在于是否需要返回执行结果。

使用TransactionTemplate进行事务管理的代码如下:

代码语言:javascript复制
public class TransactionMain {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        test();
    }

    private static void test() {
        DataSource dataSource = getDS();
        JdbcTransactionManager jtm = new JdbcTransactionManager(dataSource);
        TransactionTemplate tt = new TransactionTemplate(jtm);
        tt.execute((transactionStatus)->{
            JdbcTemplate jt = new JdbcTemplate();
            jt.setDataSource(dataSource);
            int update = jt.update("UPDATE Department SET Dname="大忽悠" WHERE id=6");
            return update;
        });
    }

    private static void update(DataSource dataSource) {
        JdbcTemplate jt = new JdbcTemplate();
        jt.setDataSource(dataSource);
        jt.update("UPDATE Department SET Dname="大忽悠" WHERE id=6");
    }
}

可以稍微看看他的excute模板方法是如何实现的:

代码语言:javascript复制
	public <T> T execute(TransactionCallback<T> action) throws TransactionException {
		Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

		if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
			return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
		}
		else {
			TransactionStatus status = this.transactionManager.getTransaction(this);
			T result;
			try {
			//执行业务代码
				result = action.doInTransaction(status);
			}
			//出现异常就回滚
			catch (RuntimeException | Error ex) {
				// Transactional code threw application exception -> rollback
				rollbackOnException(status, ex);
				throw ex;
			}
			catch (Throwable ex) {
				// Transactional code threw unexpected exception -> rollback
				rollbackOnException(status, ex);
				throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
			}
			//没出现异常就提交,然后返回结果即可
			this.transactionManager.commit(status);
			return result;
		}
	}

我们可以使用CallBack接口公开的TransactionStatus将事务标记为rollbackonly。

TransactionTemplate在commit的时候,会先检测rollbackonly是否被设置了,如果被设置了,可以改为回滚事务。

代码语言:javascript复制
	public final void commit(TransactionStatus status) throws TransactionException {
		if (status.isCompleted()) {
			throw new IllegalTransactionStateException(
					"Transaction is already completed - do not call commit or rollback more than once per transaction");
		}

		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		if (defStatus.isLocalRollbackOnly()) {
			if (defStatus.isDebug()) {
				logger.debug("Transactional code has requested rollback");
			}
			processRollback(defStatus, false);
			return;
		}

		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
			if (defStatus.isDebug()) {
				logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
			}
			processRollback(defStatus, true);
			return;
		}

		processCommit(defStatus);
	}

对于事务操作中可能抛出的checked exception异常,如果想回滚事务,又不想以unchecked exception的形式向上层传播的话,我们可以通过拿到TransactionStatus,燃烧设置rollbacknoly字段来达到一箭双雕的目的。


编程创建基于SavePoint的嵌套事务

TransactionStatus不仅可以在事务处理期间通过setRollbackOnly()方法来干预事务的状态。

如果需要,作为savepointManager,它也可以帮助我们使用Savepoint机制来创建嵌套事务。

先复习一下mysql中savepoint的用法吧:

代码语言:javascript复制
SET autocommit=0;
START TRANSACTION;
DELETE FROM test1 WHERE  NAME='大忽悠';
SAVEPOINT a;#设置保存点
DELETE FROM test1 WHERE  NAME='小朋友';
ROLLBACK TO a;#回滚到保存点

SAVEPOINT的好处在于可以选择回滚一部分事务,举个例子: 如果我们要进行转账,从一个账号中取出前,如果向主账户转账失败,那么就将钱转入备用账户,这个例子,可以用sql语句写成下面这样:

代码语言:javascript复制
SET autocommit=0;
START TRANSACTION;
UPDATE account SET money=money-100 WHERE name="取款账户";
SAVEPOINT a;#设置保存点
UPDATE account SET money=money 100 WHERE name="主账户";
ROLLBACK TO a;#回滚到保存点
#如果向临时账户转账失败了,那么回滚整个事务
UPDATE account SET money=money 100 WHERE name="临时账户";
#转账成功
commit;
#向临时账户转账都失败的话
rollback;

下面用代码演示一下savepoint的使用:

代码语言:javascript复制
public class TransactionMain {
    private static DataSource dataSource = getDS();
    private static TransactionTemplate tt = new TransactionTemplate(new JdbcTransactionManager(dataSource));
    private static JdbcTemplate jt = new JdbcTemplate(dataSource);

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        test();
    }

    private static void test() {
        tt.execute((transactionStatus)->{
            try {
                withDraw();
                Object savepoint = transactionStatus.createSavepoint();
                try{
                    saveMoney("主账户");
                    System.out.println(1/0);
                }catch (Exception e){
                    transactionStatus.rollbackToSavepoint(savepoint);
                    saveMoney("备用账户");
                }finally {
                    transactionStatus.releaseSavepoint(savepoint);
                }
            }catch (Exception e){
                transactionStatus.setRollbackOnly();
            }
            return null;
        });
    }

    private static void withDraw() {
        jt.update("UPDATE account SET money=money-100 WHERE `name`='取款账户'");
    }

    private static void saveMoney(String name) {
        jt.update("UPDATE account SET money=money 100 WHERE `name`= '" name "'");
    }
}

如果转账期间抛出的是unchecked exception,最外层的捕捉是没有必要的,因为TransactionTempalte将自动回滚事务。

注意: 不同的传播行为加上多次尝试去创建事务,可能会导致取款和存款的操作不在同一个事务中,这样就违反了事务的ACID属性。

一般情况下借助TransactionStatus的Savepoint来实现嵌套事务并非唯一方式,

更推荐使用结合PROPAGATION_NESTED传播行为的声明式事务管理方式。


声明式事务管理

编程式事务的缺点在于事务代码与业务代码相互耦合,如果要解除这种耦合关系关系,就必须将事务代码与业务代码拆分开来。

其实我们完成可以将事务代码抽象成一个模板方法,然后将业务方法放置在指定中间位置处执行即可,这里可以将业务方法以回调接口形式公开出来,就像TransactionTemplate一样。

也可以通过代理的思想,被业务对象进行动态代理,而将这部分事务模板diam放在拦截器中去执行。

显然,后者更加解耦,因为可以让程序员完全与事务代码说拜拜,因此这种方法也被称为声明式事务,因为我们还是需要声明一下当前事务方法需要什么事务支持,例如:设置一下隔离级别,超时时间,传播行为等。


模拟声明式事务

模拟之前先思考下面几个问题:

  • 针对每个对象业务方法的拦截,需要知道该方法是否需要事务支持,如果需要,针对该事务的TransactionDefintion相关信息又从哪里获得?
  • 如果调用方法过程中抛出异常,如果对这些异常进行处理,哪些异常抛出需要回滚,哪些不需要呢?

我们需要提供某种方式来记载业务方法与对应的事务信息之间的映射关系,拦截器只要查询这种映射关系,就可以知道要不要为当前业务方法创建事务。

如果要创建事务,则以业务方法作为标记到映射关系中查找创建事务需要的信息,然后创建事务。

一般这种映射关系都是在配置文件中或通过注解提供的,这种映射关系,一般被叫做驱动事务的元数据(Metadata)。

至于事务执行过程中对哪些事务需要回滚进行判断,则可以通过:

也就是TransactionDefintion的子类TransactionAttribute,TransactionAttribute中新增了rollbackOn方法,可以通过这个属性的设置告诉拦截器出现什么异常时应该回滚。


上面铺垫了那么多,下面实战一下:

  • 业务对象准备
代码语言:javascript复制
public class TestDao {
    private final JdbcTemplate jt;

    public TestDao(JdbcTemplate jt) {
        this.jt = jt;
    }

    public void update() throws Exception {
        jt.update("UPDATE Department SET Dname="大忽悠" WHERE id=6");
        throw new RuntimeException("我是来捣乱的");
    }
}

public class TestService {
    private final TestDao testDao;

    public TestService(TestDao testDao) {
        this.testDao = testDao;
    }

    @TestTransactional
    public void update() throws Exception {
        testDao.update();
    }
}
  • 拦截器准备
代码语言:javascript复制
@Component("ti")
@RequiredArgsConstructor
public class TransactionInterceptor implements MethodInterceptor {
    private final PlatformTransactionManager tm;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        TransactionDefinition td = getTransactionDefintionFormMethod(method);
        TransactionStatus ts = tm.getTransaction(td);
        Object res=null;
        try{
            res=invocation.proceed();
        }catch (Throwable t){
            if(needRollbackOn(t,td)){
                tm.rollback(ts);
            }else{
                tm.commit(ts);
            }
            throw t;
        }
        tm.commit(ts);
        return res;
    }


    private boolean needRollbackOn(Throwable t, TransactionDefinition td) {
       return ((TransactionAttribute)td).rollbackOn(t);
    }

    /**
     * 默认回滚unchecked ex
     */
    private TransactionDefinition getTransactionDefintionFormMethod(Method method) {
      return new DefaultTransactionAttribute();
    }
}
  • 配置类
代码语言:javascript复制
@Configuration
public class TestConfig implements BeanFactoryAware {
    private BeanFactory beanFactory;
    @Bean
    public DataSource dataSource(){
       return getDS();
    }

    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());
    }

    @Bean
    public PlatformTransactionManager tm(){
       return new JdbcTransactionManager(dataSource());
    }

    @Bean
    public TestDao testDao(){
        return new TestDao(jdbcTemplate());
    }

    @Bean
    public TestService testService(){
        return new TestService(testDao());
    }
    
    //生成代理对象
    @Bean("ts")
    public TestService serviceProxy(){
        ProxyFactoryBean proxyFactoryBean=new ProxyFactoryBean();
        proxyFactoryBean.setTarget(testService());
        //ProxyFactoryBean需要和IOC搭配使用,因此需要给他设置一个IOC容器
        //这里给出的是拦截器在IOC中的beanName
        proxyFactoryBean.setInterceptorNames("ti");
        proxyFactoryBean.setBeanFactory(beanFactory);
        return (TestService) proxyFactoryBean.getObject();
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
               this.beanFactory=beanFactory;
    }
}
  • 测试
代码语言:javascript复制
@SpringBootApplication
public class TransactionMain {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        ConfigurableApplicationContext app = SpringApplication.run(TransactionMain.class, args);
        TestService ts = (TestService) app.getBean("ts");
        try {
            ts.update();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这边经过测试是会进行回滚的,大家可以自行尝试


小结

上面只是一个简单的模拟,实际的拦截器设计会比上面复杂一些。

Spring提供了用户声明事务管理的一切设施(org.springframework.transaction.interceptor.TransactionInterceptor),对于我们来说,所要做的只是决定使用XML元数据驱动,还是使用注解元数据驱动的声明式事务管理。


XML元数据驱动的声明式事务

Spring允许我们在IOC容器的配置文件中直接指定事务相关的元数据,我们可以使用以下四种方式在IOC容器配置文件中指定事务需要的元数据。

  • ProxyFactory或者ProxyFactoryBean TransactionInterceptor
  • 使用"一站式"的TransactionProxyFactoryBean
  • 使用BeanNameAutoProxyCreator
  • 使用Spring 2.x的声明事务配置方式。

当然,这里我采用的是配置类的方式,就不用XML形式,本质都是一样,只不过解析器不同罢了。


ProxyFactory或者ProxyFactoryBean TransactionInterceptor

ProxyFactory或者ProxyFactoryBean就是Spring AOP提供的用于生成代理对象的工厂类,不同的是后者必须和IOC容器结合使用,而前者不需要

Spring读源码系列之AOP–04—proxyFactory创建代理对象

XML元数据驱动的声明式事务,本质就如下面那副图描绘的那样:

通过ProxyFactoryBean生成代理对象和相关拦截器链的准备工作,然后此时就完成了事务管理这一横切关注点到业务方法的织入工作。

举例说明如下:

  • 业务对象
代码语言:javascript复制
@RequiredArgsConstructor
public class TestService {
    private final JdbcTemplate jt;

    public void update() throws Exception {
        jt.update("UPDATE stu SET name="hhh" WHERE id=1");
        throw new RuntimeException("我是来捣乱的");
    }
}
  • 配置类
代码语言:javascript复制
@Configuration(value = "tc")
public class TransactionConfig {
    /**
     * 数据源准备
     */
    @Bean
    public DataSource dataSource(){
        return getDS();
    }

    @Bean
    public JdbcTemplate jdbcTemplate(){
       return new JdbcTemplate(dataSource());
    }

    @Bean
    public PlatformTransactionManager tm(){
        return new JdbcTransactionManager(dataSource());
    }

    @Bean("ti")
    public TransactionInterceptor transactionInterceptor(){
        MethodMapTransactionAttributeSource mapAttrSource = new MethodMapTransactionAttributeSource();
        //指定执行的方法名为update时,对应的事务相关属性为DefaultTransactionAttribute
        mapAttrSource.addTransactionalMethod("org.transaction.TestService.update",new DefaultTransactionAttribute());
        return new TransactionInterceptor(tm(),mapAttrSource);
    }

    @Bean
    public TestService testService(){
        return new TestService(jdbcTemplate());
    }

    @Bean("proxyService")
    public Object proxyTestService(){
        TestService target = testService();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.setProxyTargetClass(true);
        proxyFactory.addAdvice(transactionInterceptor());
        return proxyFactory.getProxy();
    }
}
  • 启动类
代码语言:javascript复制
@SpringBootApplication
public class TransactionMain {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        ConfigurableApplicationContext app = SpringApplication.run(TransactionMain.class, args);
        TestService testService = (TestService) app.getBean("proxyService");
        try {
            testService.update();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意

上面过程由几点需要注意:

  • JDBCTemplate和TransactionManager的DataSource必须是同一个,因为前面讲过TransactionSynchronizationManager会将当前事务的Connection绑定到当前线程上去,但是是用一个map集合进行存放和查找的
代码语言:javascript复制
   //key是dataSource,value是ConnectionHolder
	private static final ThreadLocal<Map<Object, Object>> resources =
			new NamedThreadLocal<>("Transactional resources");

DataSourceUtils获取连接时,会先去TransactionSynchronizationManager查找当前线程中是否已经存在该DataSource创建的连接了,如果存在了,就直接用,否则再通过这个DataSource获取一个新的,然后绑定到当前线程上去

代码语言:javascript复制
	public static Connection doGetConnection(DataSource dataSource) throws SQLException {
		Assert.notNull(dataSource, "No DataSource specified");

		ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
		....

TransactionManager的获取一个事务的方法,一开始会调用doGetTransaction,也就是去查询TransactionSynchronizationManager当前DataSource是否存在对应的ConnectionHolder,如果存在当前已经存在事务了,然后根据不同的传播行为进行处理。

代码语言:javascript复制
	@Override
	public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException {

		// Use defaults if no transaction definition given.
		TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());

		Object transaction = doGetTransaction();
		....

因此,如果TransactionManager和JDBCTemplate的dataSource不同,那么二者处于不同的connection下,自然TransactionManager提供的事务支持JDBCTemplat也就无从享受了


  • TransactionInterceptor会拦截代理对象的所有方法执行,因此目前存在一个问题就是如何获取不同方法执行事务的管理的不同的事务信息呢?

上图已经给出了解决方法,就是给TransactionInterceptor添加TransactionAttributeSource,TransactionAttributeSource内部维护着每个方法对应的事务信息,如果没给出,那么就是默认的事务属性配置

TransactionAttributeSource不同的实现类用不同的方法存储了从不同位置获取的事务管理元数据信息:

NameMatchTransactionAttributeSource就是将方法名作为key,TransactionAttribute作为value进行存储。

MethodMapTransactionAttributeSource就是以对应每个方法的Method作为key,TransactionAttribute作为value进行存储。

而AnnotationTransactionAttributeSource是从注解中获取对应的每个业务方法的事务管理信息。


源码探究

这里我们来看看TransactionInterceptor的相关源码,看看他们具体是怎么实现的:

TransactionAttributeSource源码很简单,这里大家就自行翻看吧,为了篇幅不太长,这里就略过不提了

TransactionInterceptor源码

TransactionInterceptor大部分核心逻辑都在父类TransactionAspectSupport中实现,并且因为父类实现了BeanFactoryAware接口,因此可以获取到当前app的IOC容器,也就是说TransactionInterceptor可以直接去IOC中查找自己想要的Bean.

TransactionInterceptor还实现了MethodInterceptor接口,即它是aop中的拦截器。

下面随着debug的流程,一起来看看TransactionInterceptor的核心方法invoke的实现吧:

代码语言:javascript复制
	public Object invoke(MethodInvocation invocation) throws Throwable {
		//拿到目标对象类型
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		//调用父类TransactionAspectSupport的invokeWithinTransaction方法
		return invokeWithinTransaction(
         //被拦截的方法
         invocation.getMethod(), 
         //目标对象class类型
         targetClass,
         //回调接口 
         new CoroutinesInvocationCallback() {
			@Override
			@Nullable
			//继续执行拦截器链
			public Object proceedWithInvocation() throws Throwable {
				return invocation.proceed();
			}
			//获取目标对象
			@Override
			public Object getTarget() {
				return invocation.getThis();
			}
			//获取方法入参数组
			@Override
			public Object[] getArguments() {
				return invocation.getArguments();
			}
		});
	}

可以看到拦截器执行的核心逻辑全部放在了父类的invokeWithinTransaction方法中,下面来看看:

代码语言:javascript复制
	protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {

		//获取设置到当前拦截器上的TransactionAttributeSource---就是获取拦截器的成员变量transactionAttributeSource
		TransactionAttributeSource tas = getTransactionAttributeSource();
		//从TransactionAttributeSource中获取当前方法关联的TransactionAttribute
		//如果我们没有往TransactionAttributeSource中保存当前方法对应的TransactionAttribute,那么这里会返回null
		//表示当前方法不需要事务支持
		//当然,具体返回结果还是要看TransactionAttributeSource的子类实现逻辑,上面说的是MethodMapTransactionAttributeSource
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
		//根据TransactionAttribute来寻找决定对应的TransactionManager---怎么找的,下面会分析该方法源码
		final TransactionManager tm = determineTransactionManager(txAttr);
        //处理ReactiveTransactionManager---即和响应式编程相关的部分---这里暂不讨论
		if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
            .....
		}
        //判断TransactionManager是否是PlatformTransactionManager的子类
        //如果是则转换为PlatformTransactionManager后返回,否则抛出异常
		PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
		//如果设置了TransactionAttribute的descriptor属性,那么该方法就返回该属性的值
		//否则返回方法所在类全类名.当前方法名
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
        //如果没能获取到当前方法对应的TransactionAttribute 或者当前TransactionManager不是CallbackPreferringPlatformTransactionManager就会进入下面这个分支
		if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
		    //该方法主要做的就是根据ATTR和TM是否为空,判断事务是否需要开启
		    //但是不管是否开启事务,都会创建一个TransactionInfo后返回
		    //并且会把TransactionInfo绑定到当前线程上
			TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
             
			Object retVal;
			try {
				//执行过滤器链
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				//transactionAttribute.rollbackOn(ex)来判断当前异常是否需要进行回滚
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
			//当前事务执行结束后,需要将oldTransactionInfo(先前事务信息)绑定到该线程上去
				cleanupTransactionInfo(txInfo);
			}
             
            //这部分逻辑不需要管,只有在Vavr library被引入的情况下才需要考虑
			if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
				....
			}
            
            //如果没有抛出异常,那么就进入最后的提交阶段    
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}
         //下面这个分支也不需要管,一般情况下不会走进这个分支
         //该分支是用于TransactionManager为CallbackPreferringPlatformTransactionManager的情况
		else {
			 ...
			return result;
		}
	}

这里我们先来看看上面方法中出现的determineTransactionManager方法:

代码语言:javascript复制
protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
		// Do not attempt to lookup tx manager if no tx attributes are set
		if (txAttr == null || this.beanFactory == null) {
		//如果我们没有设置拦截器中的tm变量值,那么这里返回null,asPlatformTransactionManager方法判断时会抛出异常
			return getTransactionManager();
		}
        
        //TransactionAttribute的qualifier属性
        //或者是当前拦截器成员变量transactionManagerBeanName的值
        //利用这两个值,先尝试去容器中获取到对应的TM
		String qualifier = txAttr.getQualifier();
		if (StringUtils.hasText(qualifier)) {
			return determineQualifiedTransactionManager(this.beanFactory, qualifier);
		}
		else if (StringUtils.hasText(this.transactionManagerBeanName)) {
			return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
		}
		else {
		//获取成员变量transactionManager
			TransactionManager defaultTransactionManager = getTransactionManager();
	    //如果我们没有手动指定当前拦截器关联的tm,那么上面获取的是null		
			if (defaultTransactionManager == null) {
			//那就去IOC中获取
				defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
				if (defaultTransactionManager == null) {
					defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
					this.transactionManagerCache.putIfAbsent(
							DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
				}
			}
			return defaultTransactionManager;
		}
	}

getTransactionManager方法返回的就是我们设置到拦截器中的tm,如果没有设置,那么返回的是null

代码语言:javascript复制
	@Nullable
	public TransactionManager getTransactionManager() {
		return this.transactionManager;
	}

下一个方法就是createTransactionIfNecessary,尝试去创建一个事务:

代码语言:javascript复制
	protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

		// If no name specified, apply method identification as transaction name.
		if (txAttr != null && txAttr.getName() == null) {
		//给txAttr套层代理皮,目前该代理皮并没有什么作用
			txAttr = new DelegatingTransactionAttribute(txAttr) {
				@Override
				public String getName() {
					return joinpointIdentification;
				}
			};
		}

		TransactionStatus status = null;
		//在ATTR和TM都不为空的情况下,才表明当前方法需要事务支持,否则不开启事务
		if (txAttr != null) {
			if (tm != null) {
			//开启一个事务
				status = tm.getTransaction(txAttr);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Skipping transactional joinpoint ["   joinpointIdentification  
							"] because no transaction manager has been configured");
				}
			}
		}
		//准备一个TransactionInfo然后返回---不管是否需要事务,都会创建TI
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}

再聊prepareTransactionInfo方法前,我们最好先来看看TransactionInfo是啥玩意:

代码语言:javascript复制
//TransactionInfo主要封装了下面五个属性
	protected static final class TransactionInfo {
        //当前事务关联的TM    
		@Nullable
		private final PlatformTransactionManager transactionManager;
        //当前事务关联的ATTR 
		@Nullable
		private final TransactionAttribute transactionAttribute;
        //连接点定义,默认是方法所在类的全类名.方法名 
		private final String joinpointIdentification;
       //当前事务状态TS
		@Nullable
		private TransactionStatus transactionStatus;
       //绑定到当前线程的旧的TI  
		@Nullable
		private TransactionInfo oldTransactionInfo;

//除了上面五个属性外,还有两个比较重要的方法:
          
		private void bindToThread() {
			//将当前TI绑定到当前线程,然后保存旧的TI
			this.oldTransactionInfo = transactionInfoHolder.get();
			transactionInfoHolder.set(this);
		} 
       
       
		private void restoreThreadLocalStatus() {
			//当前事务执行结束,当前线程绑定的事务信息恢复到旧TI
			transactionInfoHolder.set(this.oldTransactionInfo);
		}
代码语言:javascript复制
	private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
			new NamedThreadLocal<>("Current aspect-driven transaction");

prepareTransactionInfo方法源码如下:

代码语言:javascript复制
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, String joinpointIdentification,
			@Nullable TransactionStatus status) {
        //新建TI,保存TM,ATTR和连接点
		TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
		//ATTR不为空说明需要事务支持
		if (txAttr != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Getting transaction for ["   txInfo.getJoinpointIdentification()   "]");
			}
			//保存TS
			txInfo.newTransactionStatus(status);
		}
		else {
		//txATTR为空说明不需要事务支持
			if (logger.isTraceEnabled()) {
				logger.trace("No need to create transaction for ["   joinpointIdentification  
						"]: This method is not transactional.");
			}
		}

		// We always bind the TransactionInfo to the thread, even if we didn't create
		// a new transaction here. This guarantees that the TransactionInfo stack
		// will be managed correctly even if no transaction was created by this aspect.
		//绑定TI到当前线程,即使当前不需要事务支持,也需要绑定
		txInfo.bindToThread();
		return txInfo;
	}

如果执行过程中抛出异常,会判断是否进行rollback:

代码语言:javascript复制
	protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for ["   txInfo.getJoinpointIdentification()  
						"] after exception: "   ex);
			}
			//根据transactionAttribute.rollbackOn(ex)的返回结果决定是否需要进行回滚
			if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
				try {
					txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					throw ex2;
				}
			}
			else {
				// We don't roll back on this exception.
				// Will still roll back if TransactionStatus.isRollbackOnly() is true.
				try {
				//如果抛出的异常不需要回滚,那么就正常提交
					txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					throw ex2;
				}
			}
		}
	}

事务正常提交逻辑:

代码语言:javascript复制
	protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for ["   txInfo.getJoinpointIdentification()   "]");
			}
			//正常提交事务--当然TM在commit的时候,还会判断TS的rollbackonly是否被设置了
			txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
		}
	}

使用"一站式"的TransactionProxyFactoryBean

TransactionProxyFactoryBean是专门面向事务管理的ProxyFactoryBean。

他直接将TransactionInterceptor纳入自身进行管理。

使用TransactionProxyFactoryBean代理ProxyFactoryBean进行声明式事务管理,不需要单独声明TransactionInterceptor的bean定义,有关事务的元数据,事务管理器等新型,全都通过TransactionProxyFactoryBean的bean定义指定就可以。

下面就来看看具体使用案例吧,案例和上面一样,但是别的都不需要改变,只需要改变配置类即可:

代码语言:javascript复制
@Configuration(value = "tc")
public class TransactionConfig {
    /**
     * 数据源准备
     */
    @Bean
    public DataSource dataSource(){
        return getDS();
    }

    @Bean
    public JdbcTemplate jdbcTemplate(){
       return new JdbcTemplate(dataSource());
    }

    @Bean
    public PlatformTransactionManager tm(){
        return new JdbcTransactionManager(dataSource());
    }

    @Bean
    public TestService testService(){
        return new TestService(jdbcTemplate());
    }

    @Bean("proxyService")
    public TransactionProxyFactoryBean transactionProxyFactoryBean(){
        TestService target = testService();
        TransactionProxyFactoryBean tpfc = new TransactionProxyFactoryBean();
        tpfc.setTarget(target);
        tpfc.setTransactionManager(tm());
        MethodMapTransactionAttributeSource mapAttrSource = new MethodMapTransactionAttributeSource();
        //指定执行的方法名为update时,对应的事务相关属性为DefaultTransactionAttribute
        mapAttrSource.addTransactionalMethod("org.transaction.TestService.update",new DefaultTransactionAttribute());
        tpfc.setTransactionAttributeSource(mapAttrSource);
        return tpfc;
    }
}

源码探究

这里来看一下TransactionProxyFactoryBean具体是如何实现的:

代码语言:javascript复制
public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
		implements BeanFactoryAware {
    //内部拥有一个TransactionInterceptor实例
	private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
   //当需要获取可应用上当前方法上的拦截器时,通过该pointcut判断当前拦截器是否可以应用上去 
	@Nullable
	private Pointcut pointcut;


	/**
	 * 设置tm,实际上是设置到了拦截器里面
	 */
	public void setTransactionManager(PlatformTransactionManager transactionManager) {
		this.transactionInterceptor.setTransactionManager(transactionManager);
	}

	/**
	 * 默认会构造一个NameMatchTransactionAttributeSource
	 */
	public void setTransactionAttributes(Properties transactionAttributes) {
		this.transactionInterceptor.setTransactionAttributes(transactionAttributes);
	}

	/**
	 * 我们自己传入一个TransactionAttributeSource 
	 */
	public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
		this.transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);
	}


	public void setPointcut(Pointcut pointcut) {
		this.pointcut = pointcut;
	}
    
    //aware接口自动回调  
	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		this.transactionInterceptor.setBeanFactory(beanFactory);
	}

	/**
	 * 创建一个advisor
	 */
	@Override
	protected Object createMainInterceptor() {
	//对TM和TransactionAttributeSource是否为空进行校验
		this.transactionInterceptor.afterPropertiesSet();
	//判断生成什么类型的pointcut	
		if (this.pointcut != null) {
			return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
		}
		else {
			// Rely on default pointcut.
			return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
		}
	}

	/**
	 * 代理对象会实现TransactionalProxy接口,可以防止事务元数据被重复解析
	 */
	@Override
	protected void postProcessProxyFactory(ProxyFactory proxyFactory) {
		proxyFactory.addInterface(TransactionalProxy.class);
	}

}

我们再来看一下它的父类AbstractSingletonProxyFactoryBean的源码:

代码语言:javascript复制
public abstract class AbstractSingletonProxyFactoryBean extends ProxyConfig
		implements FactoryBean<Object>, BeanClassLoaderAware, InitializingBean {
//这些属性就不解释了,都比较常见		
	@Nullable
	private Object target;
	@Nullable
	private Class<?>[] proxyInterfaces;
	@Nullable
	private Object[] preInterceptors;
	@Nullable
	private Object[] postInterceptors;
	private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
	@Nullable
	private transient ClassLoader proxyClassLoader;
	@Nullable
	private Object proxy;

	@Override
	public void afterPropertiesSet() {
		if (this.target == null) {
			throw new IllegalArgumentException("Property 'target' is required");
		}
		if (this.target instanceof String) {
			throw new IllegalArgumentException("'target' needs to be a bean reference, not a bean name as value");
		}
		if (this.proxyClassLoader == null) {
			this.proxyClassLoader = ClassUtils.getDefaultClassLoader();
		}
        
        //通过proxyFactory来生成代理对象     
		ProxyFactory proxyFactory = new ProxyFactory();
         //advisorAdapterRegistry.wrap()方法负责将interceptor都转换为advisor
		if (this.preInterceptors != null) {
			for (Object interceptor : this.preInterceptors) {
				proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
			}
		}

		//回调子类的createMainInterceptor()来创建一个advisor
		proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));

		if (this.postInterceptors != null) {
			for (Object interceptor : this.postInterceptors) {
				proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
			}
		}
        
        //将当前类的proxyConfig配置进行复制  
		proxyFactory.copyFrom(this);
        //目标对象设置  
		TargetSource targetSource = createTargetSource(this.target);
		proxyFactory.setTargetSource(targetSource);
        //如果用户设置了代理对象需要实现的接口,然后就不进行自动探测了
		if (this.proxyInterfaces != null) {
			proxyFactory.setInterfaces(this.proxyInterfaces);
		}
		//isProxyTargetClass为true,说明需要采用cglib
		//如果没有指定采用cglib,那么将当前目标对象实现的所有接口设置为代理对象要实现的接口
		else if (!isProxyTargetClass()) {
			// Rely on AOP infrastructure to tell us what interfaces to proxy.
			Class<?> targetClass = targetSource.getTargetClass();
			if (targetClass != null) {
				proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
			}
		}
        //钩子接口---上面子类添加了一个TransactionalProxy接口给代理对象去实现
		postProcessProxyFactory(proxyFactory);
        //生成代理对象  
		this.proxy = proxyFactory.getProxy(this.proxyClassLoader);
	}


  //两个留给子类实现的钩子方法
	protected void postProcessProxyFactory(ProxyFactory proxyFactory) {}
	protected abstract Object createMainInterceptor();

//不重要的方法全部省略
...
}

TransactionProxyFactoryBean中如果我们没有指定pointcut,默认会创建TransactionAttributeSourceAdvisor

TransactionAttributeSourceAdvisor里面比较重要的在于他给出了一个TransactionAttributeSourcePointcut的默认实现,别的和DefaultPointCut差不多

代码语言:javascript复制
	private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		@Nullable
		protected TransactionAttributeSource getTransactionAttributeSource() {
		//返回了拦截器中的TransactionAttributeSource
			return (transactionInterceptor != null ? transactionInterceptor.getTransactionAttributeSource() : null);
		}
	};

我们下面来看看TransactionAttributeSourcePointcut 的具体代码实现:

代码语言:javascript复制
abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

	protected TransactionAttributeSourcePointcut() {
	//设置的类过滤器是TransactionAttributeSourceClassFilter---从这里我们可以看出默认添加的TransactionalProxy接口的作用
		setClassFilter(new TransactionAttributeSourceClassFilter());
	}
        
   //方法级别过滤
	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		TransactionAttributeSource tas = getTransactionAttributeSource();
		//从这里可以看出,如果一个方法想要被事务拦截器拦截
		//那么就必须在tas中添加相关映射,否则方法过滤器这关就通过不了
		return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
	}
    
    ....
    
    //子类重写该方法
	@Nullable
	protected abstract TransactionAttributeSource getTransactionAttributeSource();


	/**
	 * 类过滤器
	 */
	private class TransactionAttributeSourceClassFilter implements ClassFilter {

		@Override
		public boolean matches(Class<?> clazz) {
		//如果是目标对象是继承下面三个接口其中一个,则直接跳过
			if (TransactionalProxy.class.isAssignableFrom(clazz) ||
					TransactionManager.class.isAssignableFrom(clazz) ||
					PersistenceExceptionTranslator.class.isAssignableFrom(clazz)) {
				return false;
			}
			//除了CompositeTransactionAttributeSource和AnnotationTransactionAttributeSource
			//其他的TransactionAttributeSource实现,isCandidateClass方法默认都返回true
			TransactionAttributeSource tas = getTransactionAttributeSource();
			return (tas == null || tas.isCandidateClass(clazz));
		}
	}
  ...
}

至于pointcut的过滤方法是会在DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice方法中被调用,用来过滤掉无法应用与当前方法上的拦截器的,具体参考下面这篇文章:

Spring读源码系列之AOP–03—aop底层基础类学习


使用BeanNameAutoProxyCreator

如果对BeanNameAutoProxyCreator感兴趣的,可以先看看下面这篇文章:

Spring读源码系列之AOP–07—aop自动代理创建器(拿下AOP的最后一击)

对于TransactionProxyFactoryBean如果需要为多个bean进行代理,需要声明多次,因为他是一个工厂bean,一次只能创建一个代理对象,但是想一下,如果需要事务支持的对象非常多,那么一个个去声明不得累死?

BeanNameAutoProxyCreator会根据事先设定好的一组BeanNames(支持正则匹配),去容器中找到这些bean,然后给其创建代理对象

并且由于BeanNameAutoProxyCreator是个bean的后置处理器,它会将原本的对象替换为代理对象放入容器中,这样我们直接通过原本的beanName就可以拿到被代理的对象。


我们先来看看有了BeanNameAutoProxyCreator后,代码该怎么写吧:

  • 配置类需要变化,别的不需要
代码语言:javascript复制
@Configuration(value = "tc")
public class TransactionConfig {
    /**
     * 数据源准备
     */
    @Bean
    public DataSource dataSource(){
        return getDS();
    }

    @Bean
    public JdbcTemplate jdbcTemplate(){
       return new JdbcTemplate(dataSource());
    }

    @Bean
    public PlatformTransactionManager tm(){
        return new JdbcTransactionManager(dataSource());
    }

    @Bean
    public TestService testService(){
        return new TestService(jdbcTemplate());
    }

    @Bean("ti")
    public TransactionInterceptor transactionInterceptor(){
        MethodMapTransactionAttributeSource mapAttrSource = new MethodMapTransactionAttributeSource();
        //指定执行的方法名为update时,对应的事务相关属性为DefaultTransactionAttribute
        mapAttrSource.addTransactionalMethod("org.transaction.TestService.update",new DefaultTransactionAttribute());
        return new TransactionInterceptor(tm(),mapAttrSource);
    }

    @Bean
    public BeanNameAutoProxyCreator transactionProxyFactoryBean(){
        BeanNameAutoProxyCreator proxyCreator = new BeanNameAutoProxyCreator();
        proxyCreator.setBeanNames("testService");
        proxyCreator.setInterceptorNames("ti");
        return proxyCreator;
    }
}

源码探究

这里还是带着大家来看看BeanNameAutoProxyCreator执行的一个核心流程:

上面说过BeanNameAutoProxyCreator是一个后置处理器,那么这里看看他提供了哪些重要的回调接口并且这些回调接口在哪里被调用的吧:

如果对Spring的getBean和三级缓存流程不清楚的,可以看一下下面这篇文章,快速温习一下:

Spring三级缓存


BeanNameAutoProxyCreator的核心后置处理器接口都在父类中实现,我们这里着重分析一下其父类的源码:

AbstractAutoProxyCreator中有下面几个后置处理器接口,我们按照getBean的时间流程一一给出:


  • postProcessBeforeInstantiation: bean实例化之前
代码语言:javascript复制
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
	//生成当前bean对应的缓存key
		Object cacheKey = getCacheKey(beanClass, beanName);
    // 如果targetSourcedBeans不存在当前bean
		if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
		//advisedBeans集合包含当前bean,说明当前bean已经被处理过了
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			//对于一些基础设施和内部类,直接跳过
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
			//设置为FALSE,表示这些都是不需要被代理的类
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}

		//如果我们指定了自定义的TargetSource,那么在这里会直接创建代理对象,然后返回,形成短路
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		if (targetSource != null) {
			if (StringUtils.hasLength(beanName)) {
				this.targetSourcedBeans.add(beanName);
			}
			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
			this.proxyTypes.put(cacheKey, proxy.getClass());
			//直接短路
			return proxy;
		}
         //返回null,不会形成短路
		return null;
	}

一般情况下,除非你自己指定自定义的targetSource,否则不会形成短路


  • getEarlyBeanReference—实例化之后,将当前对象加入三级缓存中

该回调接口目的在于及时存在循环引用的条件下,依然注入的是代理对象,而不是原始对象,详情请看三级缓存那篇文章

代码语言:javascript复制
	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		//为当前bean构造一个缓存key
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		//加入提前被代理的bean引用集合---这里earlyProxyReferences集合中保存的是原始Bean
		this.earlyProxyReferences.put(cacheKey, bean);
		//判断当前bean是否需要被代理
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

wrapIfNecessary决定当前被提早暴露的bean是否需要被代理,那么判断条件是什么呢?

代码语言:javascript复制
	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		//targetSourcedBeans集合中存在,也表明是已经被代理过的
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		//如果advisedBeans集合存在当前bean的缓存信息,并且上一次判断后,说明当前bean不需要被代理
		//那么直接返回
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		//如果是一些基础类或者内部使用的类,则不进行代理
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		//获取可以应用到当前bean的相关拦截器--getAdvicesAndAdvisorsForBean由子类实现
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		//如果返回的拦截器数组为空,说明不需要代理,否则进行代理
		if (specificInterceptors != DO_NOT_PROXY) {
		//创建代理,然后缓存后返回
		//Boolean.TRUE表名当前bean被判断过是需要进行代理的
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}
        //返回原bean,未被代理
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

  • postProcessAfterInitialization: bean的初始化方法被调用后执行
代码语言:javascript复制
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	//bean==null,说明applyBeanPostProcessorsBeforeInitialization返回的是null
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			// remove方法返回被移除的value,上面说了它记录的是原始bean
			// 若被循环引用了,那就是执行了上面的`getEarlyBeanReference`方法,所以此时remove返回值肯定是==bean的(注意此时方法入参的bean还是原始对象)
			// 若没有被循环引用,getEarlyBeanReference()不执行 所以remove方法返回null,所以就进入if执行此处的创建代理对象方法
		//只有在不存在循环引用的条件下,才会进入if语句
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		//返回原始bean
		return bean;
	}

再来看看该类中创建代理对象的具体方法createProxy:

代码语言:javascript复制
	protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {
       
		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}
        //最终都是靠PeoxyFactory完成的代理对象创建
		ProxyFactory proxyFactory = new ProxyFactory();
		//复制当前自动代理创建器父类ProxyConfig的配置信息
		proxyFactory.copyFrom(this);
        //是否需要采用cglib代理
		if (proxyFactory.isProxyTargetClass()) {
			// Explicit handling of JDK proxy targets (for introduction advice scenarios)
			//当前目前对象是否已经被jdk动态代理过了,如果是的话,说明当前是双重代理
			if (Proxy.isProxyClass(beanClass)) {
				// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
				for (Class<?> ifc : beanClass.getInterfaces()) {
				//设置需要继承的接口
					proxyFactory.addInterface(ifc);
				}
			}
		}
		else {
			// No proxyTargetClass flag enforced, let's apply our default checks...
			//当前类是否要采用cglib代理--这里是通过beanDefiniton中的PRESERVE_TARGET_CLASS_ATTRIBUTE属性进行判断的
			//一般都不会设置,因此返回false
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
			//如果有接口需要继承就添加相关接口进集合,然后采用jdk动态代理
			//否则设置setProxyTargetClass(true),采用jdk动态代理
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
       //为当前bean构建可用的切面---该方法一会分析
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		//回调钩子---目前没用
		customizeProxyFactory(proxyFactory);
      
		proxyFactory.setFrozen(this.freezeProxy);
		//当前这些切面类是否都已经提前对当前类进行了过滤匹配
		//本类默认返回false,有些继承的子类返回true
		//该属性设置为true的时候,在代理类目标方法执行时,可以省去对当前配置类中切面的类匹配
		//直接进行方法维度匹配即可
		//具体参考: DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// Use original ClassLoader if bean class not locally loaded in overriding class loader
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
		}
		//创建代理
		return proxyFactory.getProxy(classLoader);
	}

buildAdvisors构建切面数组的过程如下:

代码语言:javascript复制
protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
		// Handle prototypes correctly...
		//解析手动设置interceptorNames数组,里面保存的是拦截器在IOC中的名字
		//这里从IOC中获取,然后返回
		//我们手动设置的也是全局拦截器,默认会应用在所有被当前自动代理器选择到的bean上
		Advisor[] commonInterceptors = resolveInterceptorNames();

		List<Object> allInterceptors = new ArrayList<>();
		if (specificInterceptors != null) {
		//specificInterceptors是自动代理创建器选出的特意针对当前bean的特定拦截器
			if (specificInterceptors.length > 0) {
				// specificInterceptors may equal PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS
				allInterceptors.addAll(Arrays.asList(specificInterceptors));
			}
			//全局拦截器添加
			//applyCommonInterceptorsFirst参数可以决定
			//全局拦截器加在特定拦截器前面还是最后
			if (commonInterceptors.length > 0) {
				if (this.applyCommonInterceptorsFirst) {
					allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
				}
				else {
					allInterceptors.addAll(Arrays.asList(commonInterceptors));
				}
			}
		}
		if (logger.isTraceEnabled()) {
			int nrOfCommonInterceptors = commonInterceptors.length;
			int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
			logger.trace("Creating implicit proxy for bean '"   beanName   "' with "   nrOfCommonInterceptors  
					" common interceptors and "   nrOfSpecificInterceptors   " specific interceptors");
		}

		Advisor[] advisors = new Advisor[allInterceptors.size()];
		for (int i = 0; i < allInterceptors.size(); i  ) {
		//转换后返回
			advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
		}
		return advisors;
	}

这里再回到子类BeanNameAutoProxyCreator中,看一下它实现的父类抽象方法,该方法用来查询当前bean的特定拦截器有哪些:

代码语言:javascript复制
	@Override
	@Nullable
	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

		return (
  //当前beanName是否在指定的beanNames数组中,如果在的话,返回PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS ,是一个空的拦截器数组.说明BeanNameAutoProxyCreator没有为bean指定特定拦截器的能力,只能指定全局拦截器
  //否则返回DO_NOT_PROXY,不对当前bean进行代理
  isSupportedBeanName(beanClass, beanName) ?
				PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS : DO_NOT_PROXY);
	}

0 人点赞