目录
1. 简介
2. 开启事务切面:
传统方式:
纯注解的方式:
3. 事务切面
3.1 事务开启类搜集
3.2 将@Bean方法实例化BeanDefinition进行注册
3.3 Spring 事务切面postprocessor实例化成BeanDefinition并注册
3.4 将支持事务切面生成advisor接口注册
4. 生成事务切面的代理对象,并且将所有的advisor对象实例化Bean对象
5. 详解advisor生成,即@Bean注解的方法是如何被实例化的呢?
上一篇我们已经分析了Spring AOP,其实AOP乍看起来挺难的,读懂代码以后其实还是挺简单的。本篇则是对AOP的进一步分析,即事务切面。
开始本篇之前,我们需要先了解纯注解的方式实例化BeanDefinition的过程,因为现在spring boot和spring cloud比较火,我在分析spring的时候会使用纯注解的方式进行讲解。因此,很有必要了解纯注解的方式实例化BeanDefinition。不了解的话可以先看我的另一篇博客Spring_纯注解实现AOP技术铺垫 (5)_spring aop注解_chen_yao_kerr的博客-CSDN博客
classpath:config/core/core.properties
100
classpath:xml/*.xml
至于传统方式如何去看源码,我之前也介绍过了。就是根据标签tx找到对应源码包,然后找到spring.handlers文件,如下图:
首先需要数据源类:
package com.xuexi.jack.datasource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
import java.util.HashMap;
import java.util.Map;
/*
* @PropertySource类似于
*
classpath:config/core/core.properties
*
* */
@Configuration
@PropertySource("classpath:config/core/core.properties")
public class DataSourceConfiguration {
@Value("${jdbc.driverClassName}")
private String driverClass;
@Value("${jdbc.url:jdbc}")
private String jdbcUrl;
@Value("${jdbc.username}")
private String user;
@Value("${jdbc.password}")
private String password;
@Resource
Environment environment;
@Bean
public DataSource dynamicDataSource() {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
try {
comboPooledDataSource.setDriverClass(driverClass);
comboPooledDataSource.setJdbcUrl(jdbcUrl);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
comboPooledDataSource.setMinPoolSize(10);
comboPooledDataSource.setMaxPoolSize(100);
comboPooledDataSource.setMaxIdleTime(1800);
comboPooledDataSource.setAcquireIncrement(3);
comboPooledDataSource.setMaxStatements(1000);
comboPooledDataSource.setInitialPoolSize(10);
comboPooledDataSource.setIdleConnectionTestPeriod(60);
comboPooledDataSource.setAcquireRetryAttempts(30);
comboPooledDataSource.setBreakAfterAcquireFailure(false);
comboPooledDataSource.setTestConnectionOnCheckout(false);
comboPooledDataSource.setAcquireRetryDelay(100);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
Map
需要开启事务的类以及管理数据源的bean,参照传统方式:
package com.xuexi.jack.transaction;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.support.TransactionTemplate;
import javax.sql.DataSource;
@Component
@EnableTransactionManagement(proxyTargetClass = false)
@MapperScan(basePackages = {"com.xuexi.jack.dao"},annotationClass = Repository.class)
public class EnableTransactionManagementBean {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
/*
* 这样也可以
* */
@Bean
public PlatformTransactionManager annotationDrivenTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dtm = new DataSourceTransactionManager();
dtm.setDataSource(dataSource);
return dtm;
}
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager platformTransactionManager) {
TransactionTemplate transactionTemplate = new TransactionTemplate();
transactionTemplate.setTransactionManager(platformTransactionManager);
return transactionTemplate;
}
}
这里,我们关注一下@EnableTransactionManagement(proxyTargetClass = false)注解:它import进来 TransactionManagementConfigurationSelector。这个类就是注入事务切面的关键信息的。
看一下 TransactionManagementConfigurationSelector类:
这2个类具体是干什么的呢?
AutoProxyRegistrar:它是负责注册 支持spring事务切面的postprocessor的,简单点就是注册InfrastructureAdvisorAutoProxyCreator的
ProxyTransactionManagementConfiguration 这个类又是干嘛的呢?它就是 创建事务切面的。说的简单点,就是创建类似于Spring AOP中的Advice切面,负责进行拦截、增强事务的:
关于以上2个类,下面还会有更多分析,目前解说这么多,怕一下子说太多了,老铁不理解。
Spring的事务切面,也是AOP。我们接着Spring_纯注解实现AOP技术铺垫 (5)_spring aop注解_chen_yao_kerr的博客-CSDN博客
进行讲解。在这篇文章的第4节,我们知道纯注解的方式实例化BeanDefinition的入口方法是invokeBeanFactoryPostProcessors()。今天,还是从这个方法进行分析。
继续debug跟进上图的第3步,我们知道,它是用扫描的方式拿到所有的class文件,然后对class文件进行过滤,找到有注解的class对象封装成BeanDefinition。以下内容之前都分析过,简单贴图看一下:
这上面的截图,其实就是很普通的实例化BeanDefinition操作。
在上方的截图中,倒数第3张的方法是 doProcessConfigurationClass(**),通过这个方法进入扫描class和封装并注册beanDefinition的。在我们注册完beanDefinition以后,我们返回到这个方法体中,紧接着就来到了方法 processImports(*****);源码如下:
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
}
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
Set componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
Set beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
接下来,一个不得不说的重点方法:
processImports(**): 以开启事务切面的类 EnableTransactionManagementBean为例,再次贴一下源码:
package com.xuexi.jack.transaction;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.support.TransactionTemplate;
import javax.sql.DataSource;
@Component
@EnableTransactionManagement(proxyTargetClass = false)
@MapperScan(basePackages = {"com.xuexi.jack.dao"},annotationClass = Repository.class)
public class EnableTransactionManagementBean {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
/*
* 这样也可以
* */
@Bean
public PlatformTransactionManager annotationDrivenTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dtm = new DataSourceTransactionManager();
dtm.setDataSource(dataSource);
return dtm;
}
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager platformTransactionManager) {
TransactionTemplate transactionTemplate = new TransactionTemplate();
transactionTemplate.setTransactionManager(platformTransactionManager);
return transactionTemplate;
}
}
它用到了2个注解 @MapperScan 和 @EnableTransactionManagement。 他们分别import进来2个类MapperScannerRegistrar 和 TransactionManagementConfigurationSelector。
而这个方法就是负责处理这两个类的。今天我们分析的是事务,那么就重点关注如何去处理TransactionManagementConfigurationSelector的。
第一次进入,它就是TransactionManagementConfigurationSelector对象的,然后根据这个对象,拿到它的两个支持spring事务切面的2个类AutoProxyRegistrar 和ProxyTransactionManagementConfiguration, 然后进行递归调用。
第二次进入这个方法,会对以上2个类进行遍历。
AutoProxyRegistrar实现了 ImportBeanDefinitionRegistrar接口,因此会对这个类进行实例化并放入缓存 importBeanDefinitionRegistrars。请记住这个缓存,后面会用
ProxyTransactionManagementConfiguration会进入以下逻辑:
首先通过this.importStack.registerImport(***)方法,将实例化后的bean放入缓存imports,然后调用processConfigurationClass(**)方法,又是递归调用。这是我们的主方法,在内部有调用processImports方法才走到这个地方的,现在有调用了processConfigurationClass,很明显是递归。
此时,我们知道,在 EnableTransactionManagementBean对象中使用到了注解@EnableTransactionManagement。 而这个注解负责对spring 事务切面提供支持的,他有2个支持的类:AutoProxyRegistrar 和ProxyTransactionManagementConfiguration。
在实例化ProxyTransactionManagementConfiguration对象以后,我们会对EnableTransactionManagementBean类中含有@Bean注解的方法进行搜集,并封装成BeanMethod对象放入缓存。
此时,整个processImports方法就说完了。
至此,整个processConfigurationClass方法也就结束了。
此处需要注意的一点,无论是根据basePackage包扫描到的类,还是AutoProxyRegistrar 和ProxyTransactionManagementConfiguration,都会放在一个总的缓存中:如下:
以上已经完成了BeanDefinition的注册工作了,接下来关注的是beanDefinition的属性设置
看一下loadBeanDefinitionsForConfigurationClass方法:
/**
* Read a particular {@link ConfigurationClass}, registering bean definitions
* for the class itself and all of its {@link Bean} methods.
*/
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
EnableTransactionManagementBean是开启我们的Spring事务切面的。而当我们执行上图的的loadbeanDefinition(*****)方法的时候,他会调用loadBeanDefinitionsForConfigurationClass方法。它会拿到EnableTransactionManagementBean类中有@Bean注解的方法,之前说到他们被封装成了BeanMethod类。而此处,我们会拿到这些BeanMethod对象封装成BeanDefinition然后进行注册。具体的封装和注册在 loadBeanDefinitionsForBeanMethod(***)方法中完成。
文章开头,我们介绍了纯注解方式开启Spring事务切面,我们说到@EnableTransactionManagement的时候,提到它import进来了TransactionManagementConfigurationSelector类。 如果我们开启的是JDK的动态代理,解JDK支持的Spring AOP操作,提到了2个支持spring事务切面的2个类,AutoProxyRegistrar和TransactionManagementConfigurationSelector:
AutoProxyRegistrar 放入缓存 importBeanDefinitionRegistrars
TransactionManagementConfigurationSelector放入缓存import
而此处的loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars())方法,就是调用缓存进行处理的。即调用AutoProxyRegistrar类的。
源码如下:
private void loadBeanDefinitionsFromRegistrars(Map registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry));
}
1. AutoProxyRegistrar:
调用它的registerBeanDefinitions方法,其实核心一点就是注册支持事务切面的postprocessor的,即 InfrastructureAdvisorAutoProxyCreator 接口。
此处,需要思考一个问题。 如果Spring AOP 功能 和 Spring事务切面同时开启,而且他们会同时拦截到同一个业务类,那么这个业务类实例肯定是一个代理类。那么这个代理类到底是由Spring AOP生成的动态代理类呢? 还是由Spring 事务切面生成的代理类呢?
其实,答案已经在源码中了。
Spring AOP提供支持的postprocessor是AnnotationAwareAspectJAutoProxyCreator。
Spring事务切面提供支持的postprocessor是InfrastructureAdvisorAutoProxyCreator。
这两个类都实现了AbstractAutoProxyCreator类,而无论是InfrastructureAdvisorAutoProxyCreator还是AnnotationAwareAspectJAutoProxyCreator生成的代理类,最终都是在AbstractAutoProxyCreator内部完成的。
其次。在我们注册InfrastructureAdvisorAutoProxyCreator的过程中,我们会判断该接口是否已经存在。如果存在相同的接口,即都为AbstractAutoProxyCreator类型的接口,那么我们会比较他们的优先级.
那么,又是如何去定义他们的优先级的呢?看一下findPriorityForClass方法:
private static int findPriorityForClass(Class> clazz) {
return APC_PRIORITY_LIST.indexOf(clazz);
}
原来,所谓的优先级,就是获取对应元素所在的下标索引位置。
InfrastructureAdvisorAutoProxyCreator 下标为 0.
AspectJAwareAdvisorAutoProxyCreator 下标为1
AnnotationAwareAspectJAutoProxyCreator 下标为2
因此,这个问题的最终答案是: 如果Spring AOP 和事务切面同时开启,那么业务类的代理对象是有AnnotationAwareAspectJAutoProxyCreator生成的,即Spring AOP功能负责生成的。
但是,最终都是由AbstractAutoProxyCreator类生成的。
2. MapperScannerRegistrar
在 EnableTransactionManagementBean 类中,我们还是用到了支持mybatis的注解@MapperScan。在这个注解中import了 MapperScannerRegistrar类。
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars())方法,也会调用涉及到mybatis的MapperScannerRegistrar类。但这不是我们今天的重点,简单了解一下即可,等到分享mybatis的时候会重点讲。
3. 接口可以实例化吗?
答案是可以的,在纯注解的mybatis连接数据库中,我们都是定义接口的,如下:
package com.xuexi.jack.dao;
import com.xuexi.jack.pojo.ConsultConfigArea;
import com.xuexi.jack.pojo.ZgGoods;
import com.xuexi.jack.pojo.ZgTicket;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;
@Repository
public interface CommonMapper {
@Select("select * from consult_configarea where areaCode=#{areaCode}")
List queryAreaByAreaCode(Map param);
@Insert("insert into consult_configarea(AREACODE,AREANAME,STATE) values(#{areaCode},#{areaName},#{state})")
int addArea(ConsultConfigArea area);
@Insert("insert into zg_goods (goodCode, goodName, count\n" +
"\t\t)\n" +
"\t\tvalues (#{goodCode,jdbcType=VARCHAR}, #{goodName,jdbcType=VARCHAR},\n" +
"\t\t#{count,jdbcType=INTEGER}\n" +
"\t\t)")
int addGood(ZgGoods zgGoods);
@Select("select * from zg_goods where goodCode=#{goodCode}")
List queryGoodsByGoodCode(String goodCode);
@Select("select * from zg_goods")
List queryAll();
@Update("update zg_ticket set version=versoin+1 where ticketId = #{ticketId} and version = #{version}")
int updateLock(Map map);
@Select("select * from zg_ticket where ticketId = #{ticketId}")
List queryTicketById(String ticketId);
@Update("update zg_ticket set ticketCount=ticketCount-#{ticketCount} where ticketId = #{ticketId} and ticketCount >= #{ticketCount}")
int updateTicket(ZgTicket zgTicket);
}
实例化beanDefinition的过程:
至此,关于Spring事务切面实例化BeanDefinition的操作全部讲完了。简单概括一下
1. 首先就是普通的实例化BeanDefinition的操作,我们会把开启Spring事务切面的类也给实例化成BeanDefinition
2. 针对开启事务的BeanDefinition,我们会搜集有@Bean注解的方法,并把他们实例化成 BeanMethod对象
3.针对开启事务的注解,我们会把他们使用的注解进行解析,找到import引入的类,提前给实例化成Bean,并且把这些bean提供的AutoProxyRegistrar也给实例化成Bean
4. 调用loadBeanDefinitions方法,把BeanMethod转成BeanDefinition并注册
5. 最后调用已经实例化好的AutoProxyRegistrar的 registerBeanDefinitions方法,完成事务切面的postprocessor接口的注册,即实例化BeanDefinition并注册。
我们之前说过,所有的beanDefinition都会放入最终的总缓存中。此处,我们还是关注loadBeanDefinitions方法:
此处,我们需要关注的是生成切面Advisor的接口,即ProxyTransactionManagementConfiguration对应的BeanDefinition对象。
还是同样的方法 loadBeanDefinitionsForConfigurationClass(***)
上图中,我们会判断当前 ConfigurationClass 对象是不是通过@import注解引入的。如果是的话执行将它注册成BeanDefinition对象。具体步骤如下:
至此,ProxyTransactionManagementConfiguration也被注册成BeanDefinition了。
注册完以后,我们会把ProxyTransactionManagementConfiguration内部含有@Bean注解的方法也给实例化成BeanDefinition对象并进行注册。
此时,所有的BeanDefinition准备完毕。
首先,我们需要一个事务类:
package com.xuexi.jack.service.area;
import com.xuexi.jack.pojo.ConsultConfigArea;
import java.util.Map;
public interface AreaService {
String queryAreaFromDB(Map param);
String queryAreaFromRedisOne(Map param);
String queryAreaFromRedisTow(Map param);
int addArea(ConsultConfigArea area);
}
package com.xuexi.jack.service.area;
import com.xuexi.jack.pojo.ConsultConfigArea;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Map;
@PropertySource("classpath:config/core/core.properties")
@Service
public class AreaServiceImpl implements AreaService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Value("${jdbc.driverClassName}")
private String driverClass;
@Value("${jdbc.url:jdbc}")
private String jdbcUrl;
@Value("${jdbc.username}")
private String user;
@Value("${jdbc.password}")
private String password;
/* @Autowired
private CommonMapper commonMapper;
@Autowired
AreaService areaService;*/
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false, rollbackFor = RuntimeException.class)
@Override
public String queryAreaFromDB(Map param) {
/* logger.info("================从mysql里面查询数据 事务1========================");
List areas = commonMapper.queryAreaByAreaCode(param);
new Thread(() -> areaService.queryAreaFromRedisOne(null)).start();
areaService.queryAreaFromRedisOne(null);*/
System.out.println("开始调用 queryAreaFromDB=====");
return "OK";
}
@Transactional
@Override
public String queryAreaFromRedisOne(Map param) {
logger.info("================从mysql里面查询数据 事务2========================");
return "OK";
}
@Override
public String queryAreaFromRedisTow(Map param) {
return null;
}
@Transactional(propagation = Propagation.NESTED)
@Override
public int addArea(ConsultConfigArea area) {
/*int i = commonMapper.addArea(area);*/
return 1;
}
}
这里需要强调的一点是,advisor是可能会提前实例化成Bean对象的。因为我们spring肯定会管理很多的类,而每一个对象实例化成Bean对象以后,我们都会调用 initializeBean 方法判断当前的Bean是否需要生成代理类。因此,在Bean实例化完以后第一次调用initializeBean 方法的时候,会调用事务切面支持的postprocessor进行找切面的工作,我们会拿到所有的advisor的beanDefinition对象进行实例化Bean的操作。
因此,在我们调用事务管理的业务类的时候,advisor其实已经是实例化好的Bean对象了,可以直接拿到Bean对象。下面,我们看一下具体的代码:
也就是说,在实例化完我们自定义的扫描类 ComponentScanBean时候,会调用initializeBean 方法,判断当前类是否需要生成代理对象的时候,就会把所有的advisor全部实例化成Bean对象。看一下ComponentScanBean的源码:
package com.xuexi.jack.bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Service;
@Service
@ComponentScan(basePackages = {"com.xuexi"})
public class ComponentScanBean {
// @Bean
// public void xx() {
// System.out.println("==");
// }
}
看看具体的实例化advisor过程:
1. 首先是实例化Bean完以后调用initializeBean,判断是否需要生成代理类
2. 进入initializeBean方法内部调用动态代理的postprocessor接口,这里需要关注的是InfrastructureAdvisorAutoProxyCreator
3. InfrastructureAdvisorAutoProxyCreator是 AbstractAutoProxyCreator的子类,因此会直接调用父类的 postProcessAfterInitialization 方法:
4. 而 wrapIfNecessary 方法,就是我们找advisor 和 生成代理类的方法。代理类的生成我就不重点说了,不懂的可以看我另一篇Spring AOP的博客,里面说的很清楚Spring之AOP技术 (6)_chen_yao_kerr的博客-CSDN博客
接下来,我们针对找事务切面advisor的过程梳理一下。
5. 看一下具体的找事务切面的advisor的过程
debug进入这个方法
继续进入方法:
上一节我们说过,含有@Bean注解的方法,我们也会实例化成BeanDefinition对象的,而此处就是遍历所有的BeanDefinitionNames,然后找到是advisor类型的beanDefinition,返回他们的Name。
接下来,还是findAdvisorBeans 这个方法,代码继续往下走。 我们会遍历这些name,然后将这些name对象的BeanDefinition实例化成Bean对象,也就是提前把所有的advisor对象都实例化成Bean了,这就是所有的切面。
至此,我们所有的advisor全部被实例化好了。
等到我们实例化含有事务注解的业务类的时候,也就是上方贴出的AreaServiceImpl对象的时候,我们还是会调用initializeBean方法,判断是否需要生成代理类。此时,我们还是会进入AbstractAutoProxyCreator的 wrapIfNecessary方法,此时就可以直接拿到刚刚实例化好的advisor然后生成代理对象了。
上一节说到调用getBean方法进行实例化advisor操作,而具体是怎么做的呢?
其实,我们拿到的advisor方法的具体名称是org.springframework.transaction.config.internalTransactionAdvisor,然后进行实例化的。
按照这个方法往下跟,我们会创建Bean,但是在创建的时候,我们会判断是否有factoryMethod, 如果有,就会进入处理factoryMethod的相关方法,这个知识点我们之前是没有讲过的,重点:
还是这个方法内部,继续往下走:
此处,我们就是我们实例化含有@Bean注解方法的核心代码处,它使用到了factoBean。
debug进入 instantiate方法,这个方法,看看它到底干了啥?
进入这个方法内部,我们发现了反射调用:
原来是通过反射调用的方式,去调用我们工厂类对应的生成advisor的、含有@Bean注解的方法。这也解释了我们之前什么要提前实例化工厂类(ProxyTransactionManagementConfiguration)。
接下来就是进入方法体内部执行了,可能里面还会有很多调用。此处,我们就以最简单的例子进行说明,不扯其他的了。
最后返回advisor对象进行封装交给spring进行管理。
至此,Spring事务切面,从BeanDefinition 到 实例化Bean 的流程就全部讲解完毕了。下一章,我们分享事务的使用。