spring AopContext.currentProxy() 容器注解或自定义注解不生效

场景

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

你可能感兴趣的:(spring AopContext.currentProxy() 容器注解或自定义注解不生效)