跳至主要內容

21、事物初始化流程之注解配置方式

安图新大约 4 分钟

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()方法导入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创建事物代理类:

 
 

五,总结