AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
其实不管是AnnotationConfigApplicationContext还是ClassPathXmlApplicationContext,目前我们都可以简单的将它们理解为就是用来创建java对象的,比如调用getBean()就会去创建对。
注意: 此处是不严谨的,getBean()可能也不会去创建对象
在java中,肯定是根据某个类来创建一个对象的.
当我们调用context.getBean(“userService”)的时候,就会去创建一个对象,但是getBean方法内部怎么知道"userService"对应的是UserService这个类呢?
所以我们就可以分析出来,在调用AnnotationConfigApplicationContext的构造方法的时候,也就是第一行代码,会去做一些事情:
大致过程如下:
通过最后一步,我们可以发现,当Spring根据UserService类来创建一个Bean时:
Bean对象创建出来后:
Spring在基于某个类生成Bean的过程中,需要利用该类的构造方法来实例化得到一个对象,但是如果一个类中存在多个构造方法,Spring的判断逻辑如下:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [linc.cool.service.UserService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: linc.cool.service.UserService.<init>()
Spring的设计思想是这样的
AOP就是进行动态代理,在创建一个Bean的过程中,Spring在最后一步回去判断当前正在创建的这个Bean是否需要进行AOP,如果需要就会进行动态.
如何判断当前的Bean对象是否需要进行AOP:
利用cglib进行AOP的大致流程
class UserServiceProxy extends UserService{
UserService target;
@Override
public void test{
// 1.判断当前执行的方法是否存在@Transactional注解
// 2.通过事务管理器创建一个数据库连接conn
// 3.修改数据库连接的autocommit为false,这里就不用自动提交了,我们手动提交,默认是true
conn.autocommit = false;
// 4.调用被代理对象的test(),执行程序员所写的业务逻辑代码,也就是执行sql
target.test();
// 5.执行完了之后如果没有出现异常,则提交,否则回滚
conn.commit(); // 如果异常 conn.rollback();
}
}
当我们在某个方法上加上了@Transactional注解后,就表示这个方法在调用时会开启Spring事务,而这个方法所在的类所对应的Bean对象会是该类的代理对象.
Spring事务的代理对象执行某个方法时的步骤:
事务常见失效的场景:
@Service
public class UserService {
@Transactional
private void add(UserModel userModel) {
saveData(userModel);
updateData(userModel);
}
}
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
}
@Service
public class UserService {
@Transactional
public final void add(UserModel userModel){
saveData(userModel);
updateData(userModel);
}
}
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void add(UserModel userModel) {
userMapper.insertUser(userModel);
updateStatus(userModel);
}
@Transactional
public void updateStatus(UserModel userModel) {
doSameThing();
}
}
Spring事务是否会失效的标准:
@Component
public class UserService {
@Transactional
public void test() {
jdbcTemplate.execute("INSERT INTO `t1` (`id`, `a`, `b`) VALUES (10001, 9999, 9999);");
invalid();
}
// 事务失效 因为最终是普通对象调用了这个方法,并不是这个方法的代理对象执行代理逻辑
// 想要解决这个问题,我们得去考虑要让这个代理对象区调用这个方法即可
@Transactional(propagation = Propagation.NEVER)
public void invalid() {
}
...
}
某个加了@Transaction注解的方法被调用时,要判断到底是不是直接被代理对象调用的,如果是直接调用的事务就不会生效,否则会失效
如何解决事务失效?
@Autowired
private TransactionTemplate transactionTemplate;
transactionTemplate.execute((status) -> {
...
return Boolean.TRUE;
})
@Component
public class UserService {
@Autowired
private UserService userService;
@Transactional
public void update(){
userService.update2();
}
@Transactional(rollbackFor = Exception.class)
public void update2() {
}
}
@Component
public class UserService implements ApplicationContextAware {
ApplicationContext context;
@Transactional
public void update(){
UserService userService = (UserService)context.getBean("userService");
userService.update2();
}
@Transactional(rollbackFor = Exception.class)
public void update2() {
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}
@Component
public class UserService {
@Autowired
private UserService userService;
@Transactional
public void update(){
((UserService) AopContext.currentProxy()).update2();
}
@Transactional(rollbackFor = Exception.class)
public void update2() {
}
}
@Configuration
@EnableTransactionManagement
@ComponentScan("linc.cool")
public class AppConfig {
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
@Bean
public PlatformTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
return sessionFactoryBean.getObject();
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/linc?characterEncoding=utf-8&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
}
因为没有加@Configuration注解的话,JdbcTemplate里面和TransactionManager里面持有的是两个不同的DataSource,所以在我们执行代理逻辑的时候,会通过TransactionManager里面的DataSource去建立连接以及设置它的属性.
然后JdbcTemplate会用它自己的DataSource去建立一个新的连接去执行SQL,它此时的连接和TransactionManager的连接时两个不同的连接,所以就导致JdbcTemplate执行的SQL就自动提交了.
而对于TransactionManager会设置自动提交为false,这个JdbcTemplate却没有设置.
而加上了@Configuration注解后,就能保证两个DataSource是同一个,然后再加上一些逻辑,就可以保证事务生效了.
这里也和Spring的代理模式是有联系的,这个加上@Configuration的类也是一个代理对象.
它会先去看DataSource有没有,没有我们就先去创建,然后TransactionManager直接从Spring容器中拿出来用,JdbcTemplate也是.