一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 詳解Spring中使用@within與@target的區別

詳解Spring中使用@within與@target的區別

2021-12-28 16:31It''''s my code life. Java教程

這篇文章主要介紹了Spring中使用@within與@target的一些區別,本文通過項目案例給大家詳細分析,給大家介紹的非常詳細,代碼簡單易懂,需要的朋友可以參考下

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

背景

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

模擬項目例子

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
注解定義:
@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通知,方法參數有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的輸出的值是sonhello2方法沒有重寫,myAnnotation.value的輸出的值是father

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

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

看看使用@within@target的區別

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

@within

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

?
1
2
3
4
5
father.hello()
father.hello2()
before, myAnnotation.value : son
son.hello()
father.hello2()

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

?
1
2
3
4
5
6
7
8
before, myAnnotation.value : father
father.hello()
before, myAnnotation.value : father
father.hello2()
before, myAnnotation.value : father
son.hello()
before, myAnnotation.value : father
father.hello2()

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

?
1
2
3
4
5
6
7
8
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

把切面代碼改成如下:

?
1
2
3
4
5
6
7
8
9
@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());
    }
}

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

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

?
1
2
3
4
5
6
father.hello()
father.hello2()
before, myAnnotation.value : son
son.hello()
before, myAnnotation.value : son
father.hello2()

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

?
1
2
3
4
5
6
before, myAnnotation.value : father
father.hello()
before, myAnnotation.value : father
father.hello2()
son.hello()
father.hello2()

父類有注解,子類有注解

?
1
2
3
4
5
6
7
8
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更符合我們想要的結果,在某個類上面加一個注解,攔截的時候就會獲取這個類上面的注解,跟父類完全沒有關系了

但這個時候會遇到一個問題,就是不相關的類都會生從代理類,

例子如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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

詳解Spring中使用@within與@target的區別

@target

詳解Spring中使用@within與@target的區別

我們從上面的圖片就可以理解為什么@target會生成代理類

我們再深入看一下:
@within會走到如下:

?
1
2
3
4
5
6
public class ExactAnnotationTypePattern extends AnnotationTypePattern {
    @Override
    public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) {
            // ......
        }
}

我沒深入研究,大致意思就是只要這個類或者這個類的祖先們帶有這個注解,即匹配成功

@target會走到如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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的結果,這個結果會讓不相關的類最后生成代理類

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

經過調試,最終是在這里獲取的:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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:

?
1
2
3
4
case AT_WITHIN_VAR:
    if (annotationFinder != null) {
        return annotationFinder.getAnnotationFromClass(getType(), withinType);
    } else return null;

withinType追蹤到如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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:

?
1
2
3
4
case AT_TARGET_VAR:
    if (annotationFinder != null) {
        return annotationFinder.getAnnotation(getType(), targetObject);
    } else return null;

targetObject 追蹤到如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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,但又想得到想要的注解

?
1
2
3
4
5
6
7
8
9
10
@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的一些區別的文章就介紹到這了,更多相關Spring中使用@within與@target內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/eaglelihh/p/15201208.html

延伸 · 閱讀

精彩推薦
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
主站蜘蛛池模板: 四虎成人免费 | 午夜国产视频 | a级亚洲片精品久久久久久久 | 香蕉国产精品偷在线播放 | 第一国内永久免费福利视频 | ysl千人千色t9t9t9 | 欧美成人禁片在线观看俄罗斯 | 天天色综合三 | 久久精品国产只有精品 | 国内精品久久久久影院男同志 | 国产精品久久99 | 热门小说同人h改编h | 欧美特黄三级在线观看 | 动态图啪啪120秒免费看 | 青草视频免费观看 | 亚洲 综合 欧美在线 热 | tkvk视频 | 久久亚洲精选 | 日本漫画大全之工之口 | 国产精品免费视频能看 | 国产欧美日韩视频在线观看一区二区 | 日韩在线一区 | 天堂日韩| 性柔术18性13处交 | 香蕉成人999视频 | 国产小视频在线播放 | 456亚洲老头视频 | 女女同性做爰xxoo亲吻 | 日韩影院在线 | 第一福利在线观看永久视频 | 欧美一区精品 | 十六一下岁女子毛片免费 | 精品一区二区三区免费视频 | 午夜在线观看免费完整直播网 | 四虎影院观看 | 91东航翘臀女神在线播放 | 69日本xxⅹxxxxx19 | 教室里的激情电影 | 亚洲六月丁香六月婷婷色伊人 | 国产欧美一区视频在线观看 | 妇女澡堂淋浴性 |