Spring-AOP
现象:接口实现类中有两个,doMethod1()
调用了doMethod2()
,此时对于AOP
,如果现在外部对象调用doMethod1()
方法的时候,会发现只有doMethod1()
方法执行被拦截,AOP
生效,而doMethod1()
内部调用doMethod2()
时并没有被拦截;外部对象单独调用doMethod1()
时会被拦截。
@Service
public class JdkProxyDemoServiceImpl implements IJdkProxyService {@Overridepublic void doMethod1() {doMethod2();System.out.println("JdkProxyServiceImpl.doMethod1()");}@Overridepublic String doMethod2() {System.out.println("JdkProxyServiceImpl.doMethod2()");return "hello world";}
}
分析: 拦截器的实现原理就是动态代理,实现AOP
机制。Spring
的代理实现有两种:一是基于 JDK Dynamic Proxy
技术而实现的;二是基于 CGLIB
技术而实现的。如果目标对象实现了接口,在默认情况下Spring
会采用JDK
的动态代理实现AOP
//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是不能拦截到这些被嵌套调用的方法的。
//当获取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();
@Component
public class SpringUtil implements ApplicationContextAware {private static ApplicationContext applicationContext;public SpringUtil springUtil() {return new SpringUtil();}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {if(SpringUtil.applicationContext == null) {SpringUtil.applicationContext = applicationContext;}}//获取applicationContextpublic 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返回指定的Beanpublic static <T> T getBean(String name,Class<T> clazz){return getApplicationContext().getBean(name, clazz);}}