基于Spring AOP和CGLIB代理实现引介增强(Introduction Advice)示例
一、Spring AOP相关概念
1. Spring AOP与AspectJ区别
Spring AOP提供跨Spring IOC的简单AOP实现,以解决程序员面临的最常见问题。它并不打算作为一个完整的AOP解决方案——它只能应用于由Spring容器管理的bean。
AspectJ是最初的AOP技术,提供完整的AOP解决方案。它比Spring AOP更健壮,但也明显更复杂。值得注意的是,AspectJ可以跨所有域对象应用。
Spring AOP | AspectJ |
---|---|
用纯Java实现 | 使用Java程序设计语言的扩展实现 |
不需要单独的编译过程 | 需要AspectJ编译器(ajc),除非设置了LTW |
只有运行时编织可用 | 运行时编织不可用。支持编译时、编译后和加载时编织 |
只支持方法级编织 | 可以编织字段,方法,构造函数,静态初始化器,最终类/方法等 |
只能在Spring容器管理的bean上实现 | 可以在所有的域对象上实现 |
只支持方法执行切入点 | 支持全部的切入点 |
为目标对象创建代理,并在这些代理上应用切面 | 切面在应用程序执行之前(在运行之前)直接编织到代码中 |
比AspectJ慢得多 | 更好的性能 |
易于学习和应用 | 相对来说比Spring AOP更复杂 |
2. Spring AOP的过程
在运行时编织中,切面在应用程序执行期间使用目标对象的代理进行编织。
JDK动态代理:Spring AOP的首选方式。只要目标对象实现了一个接口,就会使用JDK动态代理。
CGLIB代理:如果目标对象没有实现接口,那么可以使用CGLIB代理。
3. 引介增强(Introduction Advice)概念
一个Java类,没有实现某个接口,在不修改Java类的情况下,使其具备该接口的功能。
二、实现引介增强(Introduction Advice)示例
1. 一个Java类Person,一个接口EnglishLanguageFun
public class Person {
public void useChaineseLanguage(){
System.out.println("useChaineseLanguage:你好!");
}
}
public interface EnglishLanguageFun {
public void useEnglishLanguage();
}
2. 实现IntroductionInterceptor
IntroductionInterceptor
是AOP Alliance MethodInterceptor 的子接口,允许拦截器实现其他接口,并通过使用该拦截器的代理提供。这是一个基本的 AOP 概念,称为介绍。引入通常是混合的,支持构建复合对象,这些对象可以实现 Java 中多重继承的许多目标。
自定义一个PersonLanguageIntroductionInterceptor
实现IntroductionInterceptor
。同时实现目标接口EnglishLanguageFun
。
public class PersonLanguageIntroductionInterceptor implements IntroductionInterceptor, EnglishLanguageFun {
@Override
public void useEnglishLanguage() {
// 覆写接口方法
System.out.println("useEnglishLanguage:hello!");
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
if (implementsInterface(methodInvocation.getMethod().getDeclaringClass())) {
// 判断类型是EnglishLanguageFun.class时,调用useEnglishLanguage方法
return methodInvocation.getMethod().invoke(this, methodInvocation.getArguments());
}
return methodInvocation.proceed();
}
@Override
public boolean implementsInterface(Class<?> aClass) {
return aClass.isAssignableFrom(EnglishLanguageFun.class);
}
}
3. 基于CGLIB进行引介增强
@Test
public void aopTest(){
// 默认AopProxyFactory实现
DefaultAopProxyFactory defaultAopProxyFactory = new DefaultAopProxyFactory();
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTargetClass(Person.class);
advisedSupport.setTarget(new Person());
// true的目的:为给定类创建一个 CGLIB 代理
advisedSupport.setProxyTargetClass(true);
DefaultIntroductionAdvisor introductionAdvisor = new DefaultIntroductionAdvisor(new PersonLanguageIntroductionInterceptor(), EnglishLanguageFun.class);
advisedSupport.addAdvisor(introductionAdvisor);
AopProxy aopProxy = defaultAopProxyFactory.createAopProxy(advisedSupport);
Person person = (Person) aopProxy.getProxy();
person.useChaineseLanguage();
// 一个Person类,没有实现EnglishLanguageFun接口,在不修改Java类的情况下,使其具备了EnglishLanguageFun接口的功能。
EnglishLanguageFun aopLanguageFun = (EnglishLanguageFun) aopProxy.getProxy();
aopLanguageFun.useEnglishLanguage();
}
单侧执行结果:
useChaineseLanguage:你好!
useEnglishLanguage:hello!
Process finished with exit code 0
其中,setProxyTargetClass需要设置为true,才是CGLIB代理。
advisedSupport.setProxyTargetClass(true);
setProxyTargetClass用来设置是否直接代理目标类,而不仅仅是代理特定接口。
默认值为“false”。将此设置为“true”以强制对 TargetSource 的公开目标类进行代理。
如果该目标类是接口,则将为给定接口创建一个 JDK 代理。如果该目标类是任何其他类,则将为给定类创建一个 CGLIB 代理。