我们知道Spring对于AOP实现有两种方法,如果类是接口实现类,则采用JDK动态代理实现AOP。如果类没有实现接口,则采用cglib生成子类做增强,以实现代理类的目的,其实运行时找到的是这个代理类,而不是原类。
所以在一个代理类中调用内部的方法,是不是再走AOP逻辑的,那有什么好的方法可以解决这种问题吗?
基于上面的思路有两种解决办法
方法一:直接从BeanFactory中获取再次代理Bean 示例代码如下:
1@Service 2public class DemoService implements IDemoService { 3 4 @Autowired 5 private ApplicationContext context; 6 7 public String demoHello(){ 8 System.out.println("hello"); 9 final IDemoService bean = context.getBean(IDemoService.class); 10 bean.normalHello(); 11 return "hello"; 12 } 13}
即继续调用代理类的normalHello()方法。
方法二:从AopContext中获取代理Bean
SpringAop有一个配置参数exposeProxy 如果配置为true ,可以让代理对象的方法被调用时把代理对象放入AopContext,这个配置的定义如下(ps:看看这个配置的注释说明):
1//org.springframework.context.annotation.EnableAspectJAutoProxy 2/** 3 * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal} 4 * for retrieval via the {@link org.springframework.aop.framework.AopContext} class. 5 * Off by default, i.e. no guarantees that {@code AopContext} access will work. 6 * @since 4.3.1 7 */ 8boolean exposeProxy() default false;
所以代码还可以这样写:启动类EnableAspectJAutoProxy注解改为
1@EnableAspectJAutoProxy(exposeProxy = true) 2DemoService 类demoHello()方法改为: 3 4 public String demoHello(){ 5 System.out.println("hello"); 6 final IDemoService bean = (IDemoService) AopContext.currentProxy(); 7 bean.normalHello(); 8 return "hello"; 9 }
exposeProxy 属性如何生效的?
在这里拓展一点点,关于Aop有两个很重要的方法, 代理类增强方法被调用时会进入这两个个方法中的其中一个,基于代理方式:
1CglibAopProxy.DynamicAdvisedInterceptor#intercept //Cglib代理方式 2JdkDynamicAopProxy#invoke //JDK 代理方式
这两个被代理类的方法在执行的时候都有这么一段代码:
1if (this.advised.exposeProxy) { 2 // Make invocation available if necessary. 3 oldProxy = AopContext.setCurrentProxy(proxy); 4 setProxyContext = true; 5}
因为进入了intercept或者invoke方法,那么表示正在调用proxy这个代理类的某个方法,那么就把代理类放入Aop 上下文。
当然最好的一种方法,是将不同的功能放到不同的类里面,这样调用起来就不是调用当前类了。