1. 概念
Spring AOP提供的@Before
、@After
、@AfterReturning
、@AfterThrowing
、@Around
只对类的现有方法进行增强处理。如果需要对现有类增加新的方法,有两种方法可实现:
- 扩展现有类:实现简单,但如果对多个现有类进行扩展时,需增加多个类
- 使用
@DeclareParents
注解实现:实现复杂,可使用通配符匹配
本文介绍@DeclareParents
注解实现的增强。
2. 通过@DeclareParents
注解实现
假设我们的目标类是一个女人,她的核心方法为喜欢帅哥,但是我们又要为该方法添加一个新的功能,建立一个新的雌性类,该类中的方法为非常喜欢吃,把此功能添加到原目标类中。
1. 创建接口
定义一个target
public interface Person {
void likePerson();
}
@Component
public class Women implements Person {
@Override
public void likePerson() {
System.out.println("我是女生,我喜欢帅哥");
}
}
定义一个proxy
public interface Animal {
void eat();
}
@Component
public class FemaleAnimal implements Animal {
@Override
public void eat() {
System.out.println("我是雌性,我比雄性更喜欢吃零食");
}
}
2. 代理类配置
@Component
@Aspect
public class AspectConfig {
/**
* "+"表示person的所有子类;defaultImpl 表示默认需要添加的新的类
*/
@DeclareParents(value = "com.good.apo.demo4.service.Person+", defaultImpl = FemaleAnimal.class)
public Animal animal;
}
3. Bean的配置类
@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class AnnotationConfig {
}
4. 测试类
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = (Person) ctx.getBean("women");
person.likePerson();
Animal animal = (Animal) person;
animal.eat();
}
输出结果:
我是女生,我喜欢帅哥
我是雌性,我比雄性更喜欢吃零食
3. 引入增强源码解析
Spring AOP中对于引入增强的处理都是独立的,以注解的方式来介绍
首先解析切面类时,对@DeclareParents注解进行处理,在ReflectiveAspectJAdvisorFactory的getAdvisors方法中
// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
创建DeclareParentsAdvisor的方法中获取引入增强的接口对象,要作用的类表达式(type-matching)以及增强的具体实现。
private Advisor getDeclareParentsAdvisor(Field introductionField) {
DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
if (declareParents == null) {
// Not an introduction field
return null;
}
if (DeclareParents.class.equals(declareParents.defaultImpl())) {
// This is what comes back if it wasn't set. This seems bizarre...
// TODO this restriction possibly should be relaxed
throw new IllegalStateException("defaultImpl must be set on DeclareParents");
}
return new DeclareParentsAdvisor(
introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
}
在DeclareParentsAdvisor的构造方法中实例化了引入增强的Advice类DelegatePerTargetObjectIntroductionInterceptor。
public DeclareParentsAdvisor(Class<?> interfaceType, String typePattern, Class<?> defaultImpl) {
this(interfaceType, typePattern, defaultImpl,
new DelegatePerTargetObjectIntroductionInterceptor(defaultImpl, interfaceType));
}
private DeclareParentsAdvisor(Class<?> interfaceType, String typePattern,
Class<?> implementationClass, Advice advice) {
this.introducedInterface = interfaceType;
ClassFilter typePatternFilter = new TypePatternClassFilter(typePattern);
// Excludes methods implemented.
ClassFilter exclusion = new ClassFilter() {
@Override
public boolean matches(Class<?> clazz) {
return !(introducedInterface.isAssignableFrom(clazz));
}
};
this.typePatternClassFilter = ClassFilters.intersection(typePatternFilter, exclusion);
this.advice = advice;
}
当实际调用引入增强的方法时,CGLIB使用CglibMethodInvocation进行链式调用所有Advisor封装成的拦截器MethodInterceptor,执行invoke方法。引入增强的Advice类DelegatePerTargetObjectIntroductionInterceptor,它同时实现了MethodInterceptor接口。
public Object invoke(MethodInvocation mi) throws Throwable {
if (isMethodOnIntroducedInterface(mi)) {
Object delegate = getIntroductionDelegateFor(mi.getThis());
// Using the following method rather than direct reflection,
// we get correct handling of InvocationTargetException
// if the introduced method throws an exception.
// 反射调用引入增强实现类中的方法
Object retVal = AopUtils.invokeJoinpointUsingReflection(delegate, mi.getMethod(), mi.getArguments());
// Massage return value if possible: if the delegate returned itself,
// we really want to return the proxy.
// 处理流式调用返回this的情况
if (retVal == delegate && mi instanceof ProxyMethodInvocation) {
retVal = ((ProxyMethodInvocation) mi).getProxy();
}
return retVal;
}
return doProceed(mi);
}
可以看到最终是用反射的方式直接调用引入增强的实现类,从而达到目的。
参考:
https://blog.youkuaiyun.com/u010502101/article/details/76944753
https://my.oschina.net/u/2377110/blog/1532127