Spring Aop 失效的一种可能原因

本文探讨了Spring AOP在@Transactional和@DataSource注解失效的情况,详细分析了问题出现的原因,包括新事务未启动和动态数据源切换失败。通过示例代码和调试结果,指出只有通过代理类调用的方法才能被AOP增强,避免直接使用new或this关键字调用。建议使用注解或抽象类来确保获取到代理对象。

Spring Aop 失效的一种可能原因

一、问题描述

1.@Transactional(propagation = Propagation.REQUIRES_NEW)失效并没有开启新的事务
2.自定义注解 @DataSource(SLAVE1) 动态数据源切换失效

二、分析和定位原因

一、示例代码
@Service
public class TestAopInvalid {
    @DataSource
    public void a() {
        b();
        TestAopInvalid bean = SpringContextHolder.getBean(TestAopInvalid.class);
        bean.c();
    }
    @DataSource
    public void b() {
    }
    @DataSource
    public void c() {
    }
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource {
    String value() default "";
}
@Component
public class SpringContextHolder implements ApplicationContextAware {
	private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextHolder.applicationContext = applicationContext;
    }
    public static <T> T getBean(Class<T> requiredType) {
        assertApplicationContext();
        return applicationContext.getBean(requiredType);
    }
}
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DataSourceAspect {
	@Pointcut("@annotation(com.xxx.DataSource)")
	public void dataSourcePointCut() {}
	@Around("dataSourcePointCut()")
	public Object around(ProceedingJoinPoint point) throws Throwable {
			return point.proceed();
	}
}
public static void main(String[] args) {
   ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
   TestAopInvalid call = context.getBean(TestAopInvalid.class);
   call.a();
   System.exit(0);
}
二、在DataSourceAspect的around方法打断点debug结果如下

方法TestAopInvalid.a()经过了AOP增强,进入了AOP切面类的断点
方法TestAopInvalid.b()没有经过AOP增强
方法TestAopInvalid.c()经过了AOP增强,进入了AOP切面类的断点

三、总结和规避

通过注解@Autowired、@Resource、@Qualifier、@Import、@Bean和ApplicationContext.getBean()、xml
配置拿到的对象如果有被AOP增强,返回的一定是代理类的对象,方法会被增强。

另一种比如直接new的对象、或者用this关键字调用,比如同一类的a方法调用该类下的另一个方法b就是this关键字隐式调用,方法不会被增强。

重点看方法b和c被调用的方式的区别

public void a() {
    b();
    TestAopInvalid bean = SpringContextHolder.getBean(TestAopInvalid.class);
    bean.c();
}
一句话想要被AOP增强就要拿到代理类的对象去调用。
你可以写一个这样的抽象类,然后继承他:
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public abstract class ProxyHolder<T extends ProxyHolder<T>>{
    protected T proxy;
    public ProxyHolder() {
        this.proxy=proxy();
    }
    
    @SuppressWarnings({ "unchecked", "hiding" })
    private  <T> T proxy(){
        Type[] types = this.getClass().getGenericInterfaces();
        ParameterizedType type = (ParameterizedType)types[0];
        Class<T> tClass = (Class<T>)type.getActualTypeArguments()[0];
        return (T) SpringContextHolder.getBean(tClass);
    }
}
@Service
@Lazy
public class TestAopInvalid extends ProxyHolder<TestAopInvalid>{
    @DataSource
    public void a() {
        proxy.b();
        TestAopInvalid bean = SpringContextHolder.getBean(TestAopInvalid.class);
        bean.c();
    }
    @DataSource
    public void b() {
    }
    @DataSource
    public void c() {
    }
}
或者像lombok的@Slf4j一样使用注解实现proxy对象的注入,这个需要用到AnnotationProcessor。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值