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 AnnotationConfigApplicationContext
contenedor, 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,@PreDestroy
como 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 ConfigurationClassPostProcessor
para 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 @Configuration
anotación hereda la @Component
anotació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.properties
y coloque el key,value
par clave-valor analizado en propertySourceList
él:
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 @EnableTransactionManagement
hay, @Import
debe 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 TransactionManagementConfigurationSelector
clase implementa la ImportSelector
interfaz y luego ejecuta el selectImports()
método de importación AutoProxyRegistrar
yProxyTransactionManagementConfiguration
Que AutoProxyRegistrar
implementa la ImportBeanDefinitionRegistrar
interfaz e inyecta:
- InfrastructureAdvisorAutoProxyCreator: heredado de
AbstractAdvisorAutoProxyCreator
usado para crear objetos proxy AOP.
Entre ellos se ProxyTransactionManagementConfiguration
encuentra una @Configuration
clase 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 TransactionConfig
clase de configuración, inyecte el siguiente BeanDefinition en BeanDefinitionMap:
2. Mejorar la clase de configuración CGLIB
¡El TransactionConfig
propó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 BeanDefinitionMap
el BeanPostProcessor
inicializado antes de que la inicialización se use aquí para instanciar Bean back!
En este ejemplo, se crean instancias de los siguientes tres BeanPostProcessors:
- internalAutowiredAnnotationProcessor: AutowiredAnnotationBeanPostProcessor, utilizado para procesar @Autowired, @Value, @Inject y @Lookup anotados postprocesador beans (BPP)
- internalCommonAnnotationProcessor: CommonAnnotationBeanPostProcessor, utilizado para procesar anotaciones JSR-250,
@Resource,@PostConstruct,@PreDestroy
como 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 InfrastructureAdvisorAutoProxyCreator
la 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()
BeanFactoryTransactionAttributeSourceAdvisor
BeanFactoryTransactionAttributeSourceAdvisor
ProxyTransactionManagementConfiguration
Dado que el BeanFactoryTransactionAttributeSourceAdvisor
método de fábrica tiene dependencias de parámetros, necesita inyectar dependencias, crear TransactionAttributeSource
yTransactionInterceptor
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 InfrastructureAdvisorAutoProxyCreator
la postProcessAfterInitialization()
mitad del wrapIfNecessary()
método, determine si necesita crear algo de clase proxy.
Aquí solo necesita crear bookService
y bookDao
crear una clase de proxy para las cosas: