最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Spring中使用@within與@target的區(qū)別

 更新時間:2021年09月13日 08:30:56   作者:It''''s my code life.  
這篇文章主要介紹了Spring中使用@within與@target的一些區(qū)別,本文通過項目案例給大家詳細分析,給大家介紹的非常詳細,代碼簡單易懂,需要的朋友可以參考下

項目里用到@within時,出現(xiàn)了一些問題,使用@target就可以解決,但又會出現(xiàn)一些新的問題,因此本文探討了在spring中,使用@within和@target的一些區(qū)別。

背景

項目里有一個動態(tài)切換數(shù)據(jù)源的功能,我們是用切面來實現(xiàn)的,是基于注解來實現(xiàn)的,但是父類的方法是可以切換數(shù)據(jù)源的,如果有一個類直接繼承這個類,調用這個子類時,這個子類是不能夠切換數(shù)據(jù)源的,除非這個子類重寫父類的方法。

模擬項目例子

注解定義:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAnnotation {
    String value() default "me";
}

切面定義:
@Order(-1)
@Aspect
@Component
public class MyAspect {
    @Before("@within(myAnnotation)")
    public void switchDataSource(JoinPoint point, MyAnnotation myAnnotation) {
        System.out.println("before, myAnnotation.value : " + myAnnotation.value());
    }
}

父類Bean:
@MyAnnotation("father")
public class Father {
    public void hello() {
        System.out.println("father.hello()");
    }
    public void hello2() {
        System.out.println("father.hello2()");
    }
}

子類Bean:
@MyAnnotation("son")
public class Son extends Father {
    @Override
    public void hello() {
        System.out.println("son.hello()");
    }
}

配置類:
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class Config {

    @Bean
    public Father father() {
        return new Father();
    }

    @Bean
    public Son son() {
        return new Son();
    }
}

測試類:
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();
    }
}

我們定義了一個@Before通知,方法參數(shù)有point, myAnnotation,方法里輸出了myAnnotation.value的值

下面是輸出結果:

before, myAnnotation.value : father
father.hello()
before, myAnnotation.value : father
father.hello2()
before, myAnnotation.value : son
son.hello()
before, myAnnotation.value : father
father.hello2()

從上面的輸出結果看出:Son類重寫了hello方法,myAnnotation.value的輸出的值是son,hello2方法沒有重寫,myAnnotation.value的輸出的值是father

根據(jù)需求,我們肯定希望調用Son類的所有方法時,都希望myAnnotation.value的輸出的值是son,因此就需要重寫父類的所有public方法

那有沒有辦法不重寫這些方法也能達到相同的效果呢,答案是可以的。

看看使用@within@target的區(qū)別

我們分別在父類和子類上加上注解和去掉注解,一起來看看對應的結果

@within

父類無注解,子類有注解:

father.hello()
father.hello2()
before, myAnnotation.value : son
son.hello()
father.hello2()

父類有注解,子類無注解:

before, myAnnotation.value : father
father.hello()
before, myAnnotation.value : father
father.hello2()
before, myAnnotation.value : father
son.hello()
before, myAnnotation.value : father
father.hello2()

父類有注解,子類有注解(其實就是上面那個例子的結果):

before, myAnnotation.value : father
father.hello()
before, myAnnotation.value : father
father.hello2()
before, myAnnotation.value : son
son.hello()
before, myAnnotation.value : father
father.hello2()

@target

把切面代碼改成如下:

@Order(-1)
@Aspect
@Component
public class MyAspect {
    @Before("@target(myAnnotation)")
    public void switchDataSource(JoinPoint point, MyAnnotation myAnnotation) {
        System.out.println("before, myAnnotation.value : " + myAnnotation.value());
    }
}

我們再一起來看看測試結果:

父類無注解,子類有注解:

father.hello()
father.hello2()
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()

我們從上面總結出一套規(guī)律:
@within@Before通知方法的myAnnotation參數(shù)指的是調用方法所在的類上面的注解,就是這個方法是在哪個類上定義的
@target@Before通知方法的myAnnotation參數(shù)指的是調用方法運行時所屬于的類上面的注解

我們最后總結一下,如果父類和子類上都標有注解,@within@target的所得到實際注解的區(qū)別


@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 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$$EnhancerBySpringCGLIB$$eebc2a39

可以看出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的結果,這個結果會讓不相關的類最后生成代理類

通知方法中注解參數(shù)的值為什么是不一樣的

經(jīng)過調試,最終是在這里獲取的:

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 {

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); // 這里,targetObject就是生成的bean
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

	public SingletonTargetSource(Object target) {
		Assert.notNull(target, "Target object must not be null");
		this.target = target;
	}
}

想用@within,但又想得到想要的注解

@Order(-1)
@Aspect
@Component
public class MyAspect {
    @Before("@within(myAnnotation)")
    public void switchDataSource(JoinPoint point, MyAnnotation myAnnotation) {
        System.out.println(point.getTarget() + " " + point + " " + myAnnotation.value() + " " +
                point.getTarget().getClass().getAnnotation(MyAnnotation.class).value());
    }
}

很簡單,從JoinPoint中得到target,然后從這個類上得到對應的注解即可

此時,父類和子類都加有注解,一起來看看輸出結果:

cn.eagleli.spring.aop.demo.Father@194fad1 execution(void cn.eagleli.spring.aop.demo.Father.hello()) father father
cn.eagleli.spring.aop.demo.Father@194fad1 execution(void cn.eagleli.spring.aop.demo.Father.hello2()) father father
cn.eagleli.spring.aop.demo.Son@14fc5f04 execution(void cn.eagleli.spring.aop.demo.Son.hello()) son son
cn.eagleli.spring.aop.demo.Son@14fc5f04 execution(void cn.eagleli.spring.aop.demo.Father.hello2()) father son

能力有限,只能先探討這么多了,不懂的或者有其他見解的,歡迎一起討論呀~

到此這篇關于Spring中使用@within與@target的一些區(qū)別的文章就介紹到這了,更多相關Spring中使用@within與@target內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java微信公眾號開發(fā)案例

    java微信公眾號開發(fā)案例

    這篇文章主要為大家詳細介紹了java微信公眾號開發(fā)案例,如何接入公眾號,訂閱號怎么樣接收消息,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • 源碼解析JDK 1.8 中的 Map.merge()

    源碼解析JDK 1.8 中的 Map.merge()

    這篇文章主要介紹了JDK 1.8 之 Map.merge()的相關知識,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-10-10
  • 使用spring aop 統(tǒng)一捕獲異常和寫日志的示例demo

    使用spring aop 統(tǒng)一捕獲異常和寫日志的示例demo

    本文通過一個小demo給大家介紹spring AOP 實現(xiàn)的異常捕獲和日志的方法技巧,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-08-08
  • Assert.assertEquals的使用方法及注意事項說明

    Assert.assertEquals的使用方法及注意事項說明

    這篇文章主要介紹了Assert.assertEquals的使用方法及注意事項說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • MyBatis Log Free插件IDE下載與使用方式

    MyBatis Log Free插件IDE下載與使用方式

    這篇文章主要介紹了MyBatis Log Free插件IDE下載與使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • 一文帶你看懂Java8中的lambda表達式和方法引用

    一文帶你看懂Java8中的lambda表達式和方法引用

    Lambda 表達式是 Java 8 引入的一項重要特性,它提供了一種簡潔、清晰且靈活的語法來表示可傳遞的匿名函數(shù),下面就跟隨小編一起學習一下Java8中的lambda表達式和方法引用的相關知識吧
    2023-12-12
  • 基于XML配置Spring的自動裝配過程解析

    基于XML配置Spring的自動裝配過程解析

    這篇文章主要介紹了基于XML配置Spring的自動裝配過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • java實現(xiàn)收藏功能

    java實現(xiàn)收藏功能

    這篇文章主要為大家詳細介紹了java實現(xiàn)收藏功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 使用springMVC通過Filter實現(xiàn)防止xss注入

    使用springMVC通過Filter實現(xiàn)防止xss注入

    這篇文章主要介紹了使用springMVC通過Filter實現(xiàn)防止xss注入的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • mybatis plus自動生成器解析(及遇到的坑)

    mybatis plus自動生成器解析(及遇到的坑)

    這篇文章主要介紹了mybatis-plus自動生成器及遇到的坑,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03

最新評論

库尔勒市| 江门市| 清苑县| 宁德市| 赣州市| 疏附县| 临潭县| 林芝县| 永平县| 南阳市| 花莲县| 航空| 高邮市| 扶绥县| 伊吾县| 盐池县| 莲花县| 安国市| 广平县| 墨脱县| 会同县| 旬邑县| 旬阳县| 洛川县| 汽车| 汕头市| 宜黄县| 太谷县| 凤冈县| 兰考县| 台北县| 饶阳县| 麦盖提县| 寻甸| 中江县| 林西县| 上思县| 陇西县| 克什克腾旗| 临漳县| 贡嘎县|