现象 :接口实现类中有两个,doMethod1()
调用了doMethod2()
,此时对于AOP
,如果现在外部对象调用doMethod1()
方法的时候,会发现只有doMethod1()
方法执行被拦截,AOP
生效,而doMethod1()
内部调用doMethod2()
时并没有被拦截;外部对象单独调用doMethod1()
时会被拦截。
java
@Service
public class JdkProxyDemoServiceImpl implements IJdkProxyService {
@Override
public void doMethod1() {
doMethod2();
System.out.println("JdkProxyServiceImpl.doMethod1()");
}
@Override
public String doMethod2() {
System.out.println("JdkProxyServiceImpl.doMethod2()");
return "hello world";
}
}
分析: 拦截器的实现原理就是动态代理,实现AOP
机制。Spring
的代理实现有两种:一是基于 JDK Dynamic Proxy
技术而实现的;二是基于 CGLIB
技术而实现的。如果目标对象实现了接口,在默认情况下Spring
会采用JDK
的动态代理实现AOP
java
//JDK动态代理生成的JdkProxyDemoServiceImpl 的代理类
@Service
public class JdkProxyDemoServiceProxy implements IJdkProxyService {
private IJdkProxyService iJdkProxyService;
public void setIJdkProxyService(IJdkProxyService iJdkProxyService) {
this.iJdkProxyService= iJdkProxyService;
}
public void doMethod1() {
//前置通知
doBefore()
iJdkProxyService.doMethod1();
}
public String doMethod2() {
//前置通知
doBefore()
return iJdkProxyService.doMethod2();
}
private void doBefore() {
System.out.println(" 前置通知...");
}
}
当使用时,从IOC容器中获取的Bean对象都是代理对象,而不是Bean对象本身,由于this关键字应用的并不是该Bean对象的对象,而是其本身,因此此时Spring AOP是不能拦截到这些被嵌套调用的方法的。
java
//当获取Bean对象使用时
//spring容器创建代理对象
IJdkProxyService iJdkProxyService = new IJdkProxyServiceImpl();
JdkProxyDemoServiceProxy serviceProxy = new JdkProxyDemoServiceProxy ()
serviceProxy.setIJdkProxyService(iJdkProxyService);
IJdkProxyService iJdkProxyService = (IJdkProxyService) serviceProxy;
iJdkProxyService.doMethod1();
解决:
1、修改类,把内部自调用改掉。
2、将this.doMethod2()
替换为:((IJdkProxyService ) AopContext.currentProxy()).doMethod2();
此时需要修改spring
的aop
配置:
// 指示是否创建基于子类(CGLIB)的代理,而不是创建基于标准Java接口的代理。 默认值是{@code false}。
@EnableAspectJAutoProxy(proxyTargetClass = true)
3、使用SpringUtil.getBean("iJdkProxyService ").doMethod2();
java
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public SpringUtil springUtil() {
return new SpringUtil();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}