21、事物初始化流程之注解配置方式
21、事物初始化流程之注解配置方式
一,例子准备
BookDao.java
public class BookDao {
@Autowired
JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 减去某个用户的余额
* @param userName
* @param price
*/
@Transactional(propagation = Propagation.SUPPORTS)
public void updateBalance(String userName,int price){
String sql = "update account set balance=balance-? where username=?";
jdbcTemplate.update(sql,price,userName);
}
/**
* 按照图书的id来获取图书的价格
* @param id
* @return
*/
@Transactional(propagation = Propagation.REQUIRED)
public int getPrice(int id){
String sql = "select price from book where id=?";
return jdbcTemplate.queryForObject(sql,Integer.class,id);
}
/**
* 减库存,减去某本书的库存
* @param id
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateStock(int id){
String sql = "update book_stock set stock=stock-1 where id=?";
jdbcTemplate.update(sql,id);
}
}
BookService.java
public class BookService {
@Autowired
BookDao bookDao;
public BookDao getBookDao() {
return bookDao;
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(propagation = Propagation.REQUIRED)
public void checkout(String username,int id){
// 减库存
bookDao.updateStock(id);
// 获取图书价格
int price = bookDao.getPrice(id);
// 更新余额
bookDao.updateBalance(username,price);
}
}
TransactionConfig.java
@Configuration
@PropertySource("classpath:dbconfig.properties")
@EnableTransactionManagement
public class TransactionConfig {
@Value("${jdbc.driverClassName}")
private String driverClassname;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
DruidDataSource data = new DruidDataSource();
data.setDriverClassName(driverClassname);
data.setUrl(url);
data.setUsername(username);
data.setPassword(password);
return data;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public BookDao bookDao() {
return new BookDao();
}
@Bean
public BookService bookService() {
bookDao();
return new BookService();
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
TransactionTest.java
public class TransactionTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TransactionConfig.class);
BookService bean = applicationContext.getBean(BookService.class);
bean.checkout("小明",1);
}
}
二,解析 Configuration 配置类
在创建AnnotationConfigApplicationContext
容器的时候会注入几个 inner 类(前面提过,这里再提一下):
- ConfigurationClassPostProcessor:用于处理@configuration 注解的后置处理器的 bean(BFPP)
- AutowiredAnnotationBeanPostProcessor:用于处理@Autowired,@Value,@Inject 以及@Lookup 注解的后置处理器 bean(BPP)
- CommonAnnotationBeanPostProcessor:用于处理 JSR-250 注解,例如@Resource,@PostConstruct,@PreDestroy 的后置处理器 bean(BPP)
- EventListenerMethodProcessor:用于处理@EventListener 注解的后置处理器的 bean(BFPP)
- DefaultEventListenerFactory:用于生产 ApplicationListener 对象的 EventListenerFactory 对象
注入之后,会在invokeBeanFactoryPostProcessors(beanFactory)
方法中执行ConfigurationClassPostProcessor
来对 Configuration 配置类进行解析
和CGLIB增强
1,配置类的解析
@Configuration
@PropertySource("classpath:dbconfig.properties")
@EnableTransactionManagement
public class TransactionConfig {
@Value("${jdbc.driverClassName}")
private String driverClassname;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
DruidDataSource data = new DruidDataSource();
data.setDriverClassName(driverClassname);
data.setUrl(url);
data.setUsername(username);
data.setPassword(password);
return data;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public BookDao bookDao() {
return new BookDao();
}
@Bean
public BookService bookService() {
bookDao();
return new BookService();
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
1.1,解析@Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
由于@Configuration
注解继承了@Component
注解,所以要对其解析。
这里主要完成对内部类的解析,由于配置类中也可以有配置类的内部类,所以也要去递归解析内部类的配置类。
如下面这样的:
@Configuration
public class A {
@Configuration
class B{
@Configuration
class C{
}
}
}
1.2,解析@PropertySource("classpath:dbconfig.properties")
解析dbconfig.properties
然后把解析出来的key,value
键值对放入到propertySourceList
里面:
后面实例化时会在populateBean()
方法中对其属性赋值。
1.3,解析@Import
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
由于@EnableTransactionManagement
中有@Import
所以也要对其进行解析:
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
/**
* 此处是AdviceMode的作用,默认是用代理,另外一个是ASPECTJ
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {
AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {
determineTransactionAspectClass()};
default:
return null;
}
}
private String determineTransactionAspectClass() {
return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
}
}
发现TransactionManagementConfigurationSelector
类实现了ImportSelector
接口,然后执行selectImports()
方法导入AutoProxyRegistrar
和ProxyTransactionManagementConfiguration
其中AutoProxyRegistrar
实现了ImportBeanDefinitionRegistrar
接口,注入了:
- InfrastructureAdvisorAutoProxyCreator:继承于 AbstractAdvisorAutoProxyCreator 用于创建 AOP 代理对象。
其中ProxyTransactionManagementConfiguration
又是一个@Configuration
修饰的配置类,其中它导入了导入三个重要的类:
- TransactionAttributeSource:用来解析事务注解属性
- TransactionInterceptor:事务拦截器,实现了方法拦截器 MethodInterceptor
- BeanFactoryTransactionAttributeSourceAdvisor:AOP 增强的通知器
1.4,解析@Bean
注入DataSource
,JdbcTemplate
,BookDao
,BookService
,DataSourceTransactionManager
解析完TransactionConfig
配置类注入以下 BeanDefinition 到 BeanDefinitionMap 中:
2,对配置类 CGLIB 增强
对TransactionConfig
创建代理类,主要是为了保证通过@Bean 方式导入的类的单例!
三,实例化 InfrastructureAdvisorAutoProxyCreator
在registerBeanPostProcessors(beanFactory);
方法中主要是对注入到注入到BeanDefinitionMap
中的BeanPostProcessor
进行初始化,这里提前初始化是为了后面实例化 Bean 时使用!
本例子中主要对如下三个 BeanPostProcessor 实例化:
- internalAutowiredAnnotationProcessor:AutowiredAnnotationBeanPostProcessor,用于处理@Autowired,@Value,@Inject 以及@Lookup 注解的后置处理器 bean(BPP)
- internalCommonAnnotationProcessor:CommonAnnotationBeanPostProcessor,用于处理 JSR-250 注解,例如@Resource,@PostConstruct,@PreDestroy 的后置处理器 bean(BPP)
- internalAutoProxyCreator:InfrastructureAdvisorAutoProxyCreator,用于代理对象的创建
四,实例化
1,BeanFactoryTransactionAttributeSourceAdvisor 的创建
在InfrastructureAdvisorAutoProxyCreator
的第一次postProcessAfterInitialization()
中的wrapIfNecessary()
方法中就会创建BeanFactoryTransactionAttributeSourceAdvisor
,因为BeanFactoryTransactionAttributeSourceAdvisor
是 bean 注解的工厂方法,所以要先实例化工厂 factoryBean,也就是实例化ProxyTransactionManagementConfiguration
。
由于BeanFactoryTransactionAttributeSourceAdvisor
工厂方法有参数依赖,所以需要注入依赖,创建TransactionAttributeSource
和TransactionInterceptor
当参数准备好后,最后利用反射来创建BeanFactoryTransactionAttributeSourceAdvisor
2,代理类的创建
所有实例化类都会经过InfrastructureAdvisorAutoProxyCreator
的postProcessAfterInitialization()
中的wrapIfNecessary()
方法,判断是否需要创建事物代理类。
这里只需要为bookService
和bookDao
创建事物代理类: