spring aop之@DeclareParents总结

本文深入探讨Spring AOP中的引入增强机制,通过@DeclareParents注解为现有类添加新接口及其实现,实现类功能的扩展。示例展示了如何通过代理类配置为一个目标类增加额外的方法,以及Spring内部如何处理引入增强。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 概念

Spring AOP提供的@Before@After@AfterReturning@AfterThrowing@Around只对类的现有方法进行增强处理。如果需要对现有类增加新的方法,有两种方法可实现:

  1. 扩展现有类:实现简单,但如果对多个现有类进行扩展时,需增加多个类
  2. 使用@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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值