Comprender el código fuente de Spring (21): método de configuración de anotaciones para el proceso de inicialización de las cosas

Uno, ejemplo de preparación

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);
    }
}

En segundo lugar, resuelva la clase de configuración de configuración

Al crear un AnnotationConfigApplicationContextcontenedor, se inyectarán varias clases internas (mencionado anteriormente, y lo mencionaré aquí):

  • ConfigurationClassPostProcessor: el bean utilizado para procesar el postprocesador de la anotación @configuration (BFPP)
  • AutowiredAnnotationBeanPostProcessor: bean postprocesador (BPP) para procesar anotaciones @Autowired, @Value, @Inject y @Lookup
  • CommonAnnotationBeanPostProcessor: se utiliza para procesar anotaciones JSR-250, @Resource,@PostConstruct,@PreDestroycomo beans de postprocesador (BPP)
  • EventListenerMethodProcessor: el bean utilizado para procesar el postprocesador de la anotación @EventListener (BFPP)
  • DefaultEventListenerFactory: objeto EventListenerFactory utilizado para producir objetos ApplicationListener

Después de la implantación, invokeBeanFactoryPostProcessors(beanFactory)realizaremos el método ConfigurationClassPostProcessorpara la configuración de clases de configuración 解析yCGLIB增强

1. Análisis de clases de configuración

@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, análisis@Component

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    
    

Dado que la @Configurationanotación hereda la @Componentanotación, debe analizarse.

Esto es principalmente para completar el análisis de clases internas Dado que la clase de configuración también puede tener clases internas de clases de configuración, es necesario analizar de forma recursiva las clases de configuración de las clases internas.

Como lo siguiente:

@Configuration
public class A {
    
    
    
    @Configuration
    class B{
    
    
        
        @Configuration
        class C{
    
    

        }
    }
}

1.2, análisis@PropertySource("classpath:dbconfig.properties")

Analice dbconfig.propertiesy coloque el key,valuepar clave-valor analizado en propertySourceListél:

Inserte la descripción de la imagen aquí
populateBean()Asignará valores a sus propiedades en el método cuando se cree una instancia más tarde .

1.3, análisis@Import

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
    
    

Porque lo @EnableTransactionManagementhay, @Importdebe analizarse:

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);
	}

}

Descubrí que la TransactionManagementConfigurationSelectorclase implementa la ImportSelectorinterfaz y luego ejecuta el selectImports()método de importación AutoProxyRegistraryProxyTransactionManagementConfiguration

Que AutoProxyRegistrarimplementa la ImportBeanDefinitionRegistrarinterfaz e inyecta:

  • InfrastructureAdvisorAutoProxyCreator: heredado de AbstractAdvisorAutoProxyCreatorusado para crear objetos proxy AOP.

Entre ellos se ProxyTransactionManagementConfigurationencuentra una @Configurationclase de configuración modificada, que importa tres clases importantes:

  • TransactionAttributeSource: utilizado para analizar los atributos de anotación de transacciones
  • TransactionInterceptor: Interceptor de transacciones, que implementa el método interceptor MethodInterceptor
  • BeanFactoryTransactionAttributeSourceAdvisor: notificador mejorado AOP

1.4, analizando @Bean

Inyección DataSource, JdbcTemplate, BookDao, BookService,DataSourceTransactionManager

Después de analizar la TransactionConfigclase de configuración, inyecte el siguiente BeanDefinition en BeanDefinitionMap:

Inserte la descripción de la imagen aquí

2. Mejorar la clase de configuración CGLIB

¡El TransactionConfigpropósito principal de crear clases de proxy es asegurar el singleton de clases importadas por @Bean!

Tres, instanciar InfrastructureAdvisorAutoProxyCreator

En el registerBeanPostProcessors(beanFactory);método se inyecta principalmente en la inyección en BeanDefinitionMapel BeanPostProcessorinicializado antes de que la inicialización se use aquí para instanciar Bean back!

En este ejemplo, se crean instancias de los siguientes tres BeanPostProcessors:

Inserte la descripción de la imagen aquí

  • internalAutowiredAnnotationProcessor: AutowiredAnnotationBeanPostProcessor, utilizado para procesar @Autowired, @Value, @Inject y @Lookup anotados postprocesador beans (BPP)
  • internalCommonAnnotationProcessor: CommonAnnotationBeanPostProcessor, utilizado para procesar anotaciones JSR-250, @Resource,@PostConstruct,@PreDestroycomo beans de postprocesador (BPP)
  • internalAutoProxyCreator: InfrastructureAdvisorAutoProxyCreator, utilizado para crear objetos proxy

Cuatro, instanciación

1, la creación de BeanFactoryTransactionAttributeSourceAdvisor

Se creará en el método InfrastructureAdvisorAutoProxyCreatorla primera vez , porque es un método de fábrica con anotaciones de bean, por lo que el factoryBean de fábrica debe ser instanciado primero, es decir, instanciado .postProcessAfterInitialization()wrapIfNecessary()BeanFactoryTransactionAttributeSourceAdvisorBeanFactoryTransactionAttributeSourceAdvisorProxyTransactionManagementConfiguration

Dado que el BeanFactoryTransactionAttributeSourceAdvisormétodo de fábrica tiene dependencias de parámetros, necesita inyectar dependencias, crear TransactionAttributeSourceyTransactionInterceptor

Cuando los parámetros estén listos, finalmente use la reflexión para crearBeanFactoryTransactionAttributeSourceAdvisor

2. Creación de clase de proxy

Todas las instancias de la clase pasarán por InfrastructureAdvisorAutoProxyCreatorla postProcessAfterInitialization()mitad del wrapIfNecessary()método, determine si necesita crear algo de clase proxy.

Aquí solo necesita crear bookServicey bookDaocrear una clase de proxy para las cosas:

Inserte la descripción de la imagen aquí

Cinco, resumen

Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/u013277209/article/details/114886302
Recomendado
Clasificación