21、Spring源码分析:事物初始化流程之注解配置方式

一,例子准备

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()方法导入AutoProxyRegistrarProxyTransactionManagementConfiguration

其中AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,注入了:

  • InfrastructureAdvisorAutoProxyCreator:继承于AbstractAdvisorAutoProxyCreator用于创建AOP代理对象。

其中ProxyTransactionManagementConfiguration又是一个@Configuration修饰的配置类,其中它导入了导入三个重要的类:

  • TransactionAttributeSource:用来解析事务注解属性
  • TransactionInterceptor:事务拦截器,实现了方法拦截器MethodInterceptor
  • BeanFactoryTransactionAttributeSourceAdvisor:AOP增强的通知器
1.4,解析@Bean

注入DataSourceJdbcTemplateBookDaoBookServiceDataSourceTransactionManager

解析完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工厂方法有参数依赖,所以需要注入依赖,创建TransactionAttributeSourceTransactionInterceptor

当参数准备好后,最后利用反射来创建BeanFactoryTransactionAttributeSourceAdvisor

2,代理类的创建

所有实例化类都会经过InfrastructureAdvisorAutoProxyCreatorpostProcessAfterInitialization()中的wrapIfNecessary()方法,判断是否需要创建事物代理类。

这里只需要为bookServicebookDao创建事物代理类:

*

五,总结

*