场景
无论spring的注解(如@Async
, @Transactional
), 还是自定义的注解, 不生效的场景一般出现于以下2种
1.在同一个类中, 无注解方法调用有注解的方法, 会导致注解不生效
2.在同一个类中, 有注解方法调用另外一个有注解的方法, 也会导致注解不生效
原因
由于spring的aop使用了动态代理, 但同一个类内部调用不使用动态代理, 而是真实对象this,因此无法进入切面, 导致注解不生效
解决
以下方法二选一, 推荐使用方法2, 因为它遵循了"面向接口编程"的规则
方法1: 使用
AopContext.currentProxy()
获取当前代理, 调用另外一个注解的方法 (记得为@EnableAspectJAutoProxy
配置添加属性exposeProxy=true
)
方法2: 将另一个注解的方法, 抽到一个新的类中, 通过新类来调用该方法
代码示例
提问1: 如下代码, 请问调用aopAnnotationTest()
方法, 事务能否正常回滚?
public void aopAnnotationTest() {
System.out.println("我是无注解方法,我要去调用有注解的方法");
insert();
}
@Transactional(rollbackFor = Exception.class)
public void insert() {
PersonDO personDO = new PersonDO("饱饱");
personDAO.save(personDO);
throw new PoException();
}
回答: 当然不能正常回滚.
改进: 使用方法1
修改配置
@EnableAspectJAutoProxy(exposeProxy = true)
使用AopContext.currentProxy()
调用本类方法
public void aopAnnotationTest() {
System.out.println("我是无注解方法,我要去调用有注解的方法");
PersonService personService = (PersonService) AopContext.currentProxy();
personService.insert();
}
@Transactional(rollbackFor = Exception.class)
public void insert() {
PersonDO personDO = new PersonDO("饱饱");
personDAO.save(personDO);
throw new PoException();
}
提问2: 如下代码, 调用insert()
方法, insertOrUpdate()
方法能否异步执行?
@Transactional(rollbackFor = Exception.class)
public void insert() {
System.out.printf("你的名字:%s\n", Thread.currentThread().getName());
PersonDO personDO = new PersonDO("饱饱");
personDAO.save(personDO);
// throw new PoException();
updatePerson("饱饱");
}
@Async
public void updatePerson(String name) {
System.out.printf("我的名字:%s\n", Thread.currentThread().getName());
PersonDO existPerson = personDAO.findFirstByName(name);
if (existPerson == null) {
existPerson = new PersonDO(name);
} else {
existPerson.setAge(3);
}
personDAO.save(existPerson);
}
回答: 不能, 最终还是同步执行
改进: 使用方法2
service类
@Service
public class PersonService {
@Autowired
private PersonDAO personDAO;
@Resource
private AsyncOperatePerson asyncOperatePerson;
@Transactional(rollbackFor = Exception.class)
public void insert() {
System.out.printf("你的名字:%s\n", Thread.currentThread().getName());
PersonDO personDO = new PersonDO("饱饱");
personDAO.save(personDO);
asyncOperatePerson.insertOrUpdate("饱饱");
}
}
将insertOrUpdate()
方法抽到新的类中
@Component
public class AsyncOperatePerson {
@Resource
private PersonDAO personDAO;
@Async
public void insertOrUpdate(String name) {
System.out.printf("我的名字:%s\n", Thread.currentThread().getName());
PersonDO existPerson = personDAO.findFirstByName(name);
if (existPerson == null) {
existPerson = new PersonDO(name);
} else {
existPerson.setAge(3);
}
personDAO.save(existPerson);
}
}
网友评论