Spring中@within与@target的一些区别

本文详细探讨了Spring AOP中@within和@target注解的差异,通过实例分析了它们在父类和子类注解处理上的不同,并解释了为何在某些情况下会导致不相关类被代理的问题。最后,通过源码解析揭示了匹配逻辑,有助于理解AOP注解的工作原理。

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

before, myAnnotation.value : son

son.hello()

before, myAnnotation.value : son

father.hello2()

父类有注解,子类无注解:

before, myAnnotation.value : father

father.hello()

before, myAnnotation.value : father

father.hello2()

son.hello()

father.hello2()

父类有注解,子类有注解

before, myAnnotation.value : father

father.hello()

before, myAnnotation.value : father

father.hello2()

before, myAnnotation.value : son

son.hello()

before, myAnnotation.value : son

father.hello2()

我们从上面总结出一套规律:

@within@Before通知方法的myAnnotation参数指的是调用方法所在的类上面的注解,就是这个方法是在哪个类上定义的

@target@Before通知方法的myAnnotation参数指的是调用方法运行时所属于的类上面的注解

我们最后总结一下,如果父类和子类上都标有注解,@within@target的所得到实际注解的区别

| | @within | @target |

| — | — | — |

| 父类方法 | 父类注解 | 父类注解 |

| 子类不重写方法 | 父类注解 | 子类注解 |

| 子类重写方法 | 子类注解 | 子类注解 |

@target 看起来跟合理一点

从上面的分析可以看出,其实用@target更符合我们想要的结果,在某个类上面加一个注解,拦截的时候就会获取这个类上面的注解,跟父类完全没有关系了

但这个时候会遇到一个问题,就是不相关的类都会生从代理类,

例子如下:

public class NormalBean {

public void hello() {

}

}

@Configuration

@EnableAspectJAutoProxy(exposeProxy = true)

public class Config {

@Bean

public Father father() {

return new Father();

}

@Bean

public Son son() {

return new Son();

}

@Bean

public 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 NormalBean normalBean() {

return new NormalBean();

}

}

public class Main {

public static void main(String[] args) {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class,

MyAspect.class);

Father father = context.getBean(“father”, Father.class);

father.hello();

father.hello2();

Son son = context.getBean(Son.class);

son.hello();

son.hello2();

NormalBean normalBean = context.getBean(NormalBean.class);

System.out.println(normalBean.getClass());

}

}

输出:

class cn.eagleli.spring.aop.demo.NormalBean E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIBeebc2a39

可以看出NormalBean自己什么都没做,但却被代理了

我们再把@target换成@within

class cn.eagleli.spring.aop.demo.NormalBean

可以看出使用@within时,不相关的类没有被代理

我们一起来看看为什么

AbstractAutoProxyCreator类中的wrapIfNecessary方法打断点,看看什么情况:

@within

@target

我们从上面的图片就可以理解为什么@target会生成代理类

我们再深入看一下:

@within会走到如下:

public class ExactAnnotationTypePattern extends AnnotationTypePattern {

@Override

public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) {

// …

}

}

我没深入研究,大致意思就是只要这个类或者这个类的祖先们带有这个注解,即匹配成功

@target会走到如下:

public class ThisOrTargetAnnotationPointcut extends NameBindingPointcut {

@Override

protected FuzzyBoolean matchInternal(Shadow shadow) {

if (!couldMatch(shadow)) {

return FuzzyBoolean.NO;

}

ResolvedType toMatchAgainst = (isThis ? shadow.getThisType() : shadow.getTargetType()).resolve(shadow.getIWorld());

annotationTypePattern.resolve(shadow.getIWorld());

if (annotationTypePattern.matchesRuntimeType(toMatchAgainst).alwaysTrue()) {

return FuzzyBoolean.YES;

} else {

// a subtype may match at runtime

return FuzzyBoolean.MAYBE;

}

}

}

public class AspectJExpressionPointcut extends AbstractExpressionPointcut

implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {

@Override

public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {

obtainPointcutExpression();

ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);

// Special handling for this, target, @this, @target, @annotation

// in Spring - we can optimize since we know we have exactly this class,

// and there will never be matching subclass at runtime.

if (shadowMatch.alwaysMatches()) {

return true;

}

else if (shadowMatch.neverMatches()) {

return false;

}

else {

// the maybe case

if (hasIntroductions) {

return true;

}

// A match test returned maybe - if there are any subtype sensitive variables

// involved in the test (this, target, at_this, at_target, at_annotation) then

// we say this is not a match as in Spring there will never be a different

// runtime subtype.

RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);

return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass)); // 这里会返回true

}

}

}

我没深入研究,大致意思是匹配的话就返回YES,否则就返回MAYBE,匹配逻辑是和@within一样的

因此所有不相关的类都会是一个MAYBE的结果,这个结果会让不相关的类最后生成代理类

通知方法中注解参数的值为什么是不一样的

经过调试,最终是在这里获取的:

public final class ReflectionVar extends Var {

static final int THIS_VAR = 0;

static final int TARGET_VAR = 1;

static final int ARGS_VAR = 2;

static final int AT_THIS_VAR = 3;

static final int AT_TARGET_VAR = 4;

static final int AT_ARGS_VAR = 5;

static final int AT_WITHIN_VAR = 6;

static final int AT_WITHINCODE_VAR = 7;

static final int AT_ANNOTATION_VAR = 8;

public Object getBindingAtJoinPoint(

Object thisObject,

Object targetObject,

Object[] args,

Member subject,

Member withinCode,

Class withinType) {

switch( this.varType) {

case THIS_VAR: return thisObject;

case TARGET_VAR: return targetObject;

case ARGS_VAR:

if (this.argsIndex > (args.length - 1)) return null;

return args[argsIndex];

case AT_THIS_VAR:

if (annotationFinder != null) {

return annotationFinder.getAnnotation(getType(), thisObject);

} else return null;

case AT_TARGET_VAR:

if (annotationFinder != null) {

return annotationFinder.getAnnotation(getType(), targetObject);

} else return null;

case AT_ARGS_VAR:

if (this.argsIndex > (args.length - 1)) return null;

if (annotationFinder != null) {

return annotationFinder.getAnnotation(getType(), args[argsIndex]);

} else return null;

case AT_WITHIN_VAR:

if (annotationFinder != null) {

return annotationFinder.getAnnotationFromClass(getType(), withinType);

} else return null;

case AT_WITHINCODE_VAR:

if (annotationFinder != null) {

return annotationFinder.getAnnotationFromMember(getType(), withinCode);

} else return null;

case AT_ANNOTATION_VAR:

if (annotationFinder != null) {

return annotationFinder.getAnnotationFromMember(getType(), subject);

} else return null;

}

return null;

}

}

@within

case AT_WITHIN_VAR:

if (annotationFinder != null) {

return annotationFinder.getAnnotationFromClass(getType(), withinType);

} else return null;

withinType追踪到如下:

public class PointcutExpressionImpl implements PointcutExpression {

private ShadowMatch matchesExecution(Member aMember) {

Shadow s = ReflectionShadow.makeExecutionShadow(world, aMember, this.matchContext);

ShadowMatchImpl sm = getShadowMatch(s);

sm.setSubject(aMember);

sm.setWithinCode(null);

sm.setWithinType(aMember.getDeclaringClass()); // 这里设置withinType

return sm;

}

}

public abstract class AopUtils {

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {

Assert.notNull(pc, “Pointcut must not be null”);

if (!pc.getClassFilter().matches(targetClass)) {

return false;

}

MethodMatcher methodMatcher = pc.getMethodMatcher();

if (methodMatcher == MethodMatcher.TRUE) {

// No need to iterate the methods if we’re matching any method anyway…

return true;

}

IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;

if (methodMatcher instanceof IntroductionAwareMethodMatcher) {

introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;

}

Set<Class<?>> classes = new LinkedHashSet<>();

if (!Proxy.isProxyClass(targetClass)) {

classes.add(ClassUtils.getUserClass(targetClass));

}

classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

for (Class<?> clazz : classes) {

Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);

for (Method method : methods) { // 这里获取所有method

if (introductionAwareMethodMatcher != null ?

introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :

methodMatcher.matches(method, targetClass)) {

return true;

}

}

}

return false;

}

}

@target

case AT_TARGET_VAR:

if (annotationFinder != null) {

return annotationFinder.getAnnotation(getType(), targetObject);

} else return null;

targetObject 追踪到如下:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport

implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值