首頁>技術>

相信每一個程式設計師,對於spring註解這個知識點一定不陌生,我這邊也整理了一些常用的,以備不時之需

這不是打算年後準備跳槽了啊,所以最近摸魚比較多一些,老大默許了,我覺得我老大還是很好的。也在網上看了一些資料,但是,我發現很多講解註解的時候,對於一些可以直接點選原始碼檢視的內容講解的佔多數,但是授人以魚不如授人以漁,尤其是最近再某平臺看到一篇文章,大致內容是:為什麼現在的程式設計師都不去學習原始碼呢?

其中有一句話說的很好:原始碼很複雜,閱讀很浪費時間並且短時間內看不到什麼效果,無論基於什麼樣的原因,放棄閱讀原始碼始終不是一個明智的選擇,因為你失去了一個跟大師學習的機會

這一點在我最近看原始碼的過程中真的是體現的淋漓盡致,經常晚上太累了,然後就算了,不看了,但是,臨睡前,平板開啟看一下,又覺得真香,所以,針對註解,我今天以幾個比較經典的註解,帶大家看一下註解的實現過程再原始碼中是怎麼展現的,我想會對大家再原始碼的閱讀過程中起一點作用的

好了,話不多說,看正文

@AliasFor
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface AliasFor {	@AliasFor("attribute")	String value() default "";	@AliasFor("value")	String attribute() default "";	Class<? extends Annotation> annotation() default Annotation.class;}

AliasFor這個註解很奇怪,value的別名是attribute,attribute的別名是value

那麼它的行為在哪裡被定義的呢?在AnnotationTypeMapping中我們可以找到答案

// 這裡使用了AnnotationsScanner的getDeclaredAnnotation方法來獲取所有的AliasFor註解的方法// AnnotationsScanner 是spring中的非公開抽象類,在我們的程式碼中不能直接進行使用// Spring中沒有提供子類private Map<Method, List<Method>> resolveAliasedForTargets() {    Map<Method, List<Method>> aliasedBy = new HashMap<>();    for (int i = 0; i < this.attributes.size(); i++) {        Method attribute = this.attributes.get(i);        AliasFor aliasFor = AnnotationsScanner.getDeclaredAnnotation(attribute, AliasFor.class);        if (aliasFor != null) {            Method target = resolveAliasTarget(attribute, aliasFor);            aliasedBy.computeIfAbsent(target, key -> new ArrayList<>()).add(attribute);        }    }    return Collections.unmodifiableMap(aliasedBy);}// 為了簡潔,我將原始碼中其餘部分省略掉了,可以看到,這裡使用用反射得到的Method的getAnnotation方法得到例項private Method resolveAliasTarget(Method attribute, AliasFor aliasFor, boolean checkAliasPair) {    // ... ...    Method target = AttributeMethods.forAnnotationType(targetAnnotation).get(targetAttributeName);    // ... ...    if (isAliasPair(target) && checkAliasPair) {        AliasFor targetAliasFor = target.getAnnotation(AliasFor.class);        if (targetAliasFor != null) {            Method mirror = resolveAliasTarget(target, targetAliasFor, false);            if (!mirror.equals(attribute)) {                throw new AnnotationConfigurationException(String.format(                    "%s must be declared as an @AliasFor %s, not %s.",                    StringUtils.capitalize(AttributeMethods.describe(target)),                    AttributeMethods.describe(attribute), AttributeMethods.describe(mirror)));            }        }    }    return target;}

透過學習@AliasFor,我們知道了可以透過先活動Method,再獲得其修飾的註解的方法。

根據這樣的方法,我們可以使用下面的程式碼,找到類DockingHandlers中所有被註解@DockIngMessage修飾的方法

// DockIngMessage 是自定義的註解Method[] methods = DockingHandlers.class.getMethods();for (Method method : methods) {    DockIngMessage dockIngMessage = method.getAnnotation(DockIngMessage.class);    if (dockIngMessage != null) {        System.out.println(dockIngMessage.name());    }}
@Bean
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Bean {	@AliasFor("name")	String[] value() default {};	@AliasFor("value")	String[] name() default {};    	@Deprecated	Autowire autowire() default Autowire.NO;    	boolean autowireCandidate() default true;	String initMethod() default "";	String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;}

@Bean註解是Spring中用得比較廣泛的註解之一,來看看Spring原始碼中是怎麼查詢@Bean註解的

public static boolean isBeanAnnotated(Method method) {    return AnnotatedElementUtils.hasAnnotation(method, Bean.class);}

使用了AnnotatedElementUtils工具類,那麼我們就可以把上面的程式碼改造一下

Method[] methods = DockingHandlers.class.getMethods();for (Method method : methods) {    if (AnnotatedElementUtils.hasAnnotation(method, DockIngMessage.class)) {        DockIngMessage dockIngMessage = AnnotatedElementUtils.getMergedAnnotation(method,DockIngMessage.class);        System.out.println(dockIngMessage.name());    }}// 相比於判斷 != null , 這樣的寫法相對優雅了許多

至於Bean到底是怎麼生效的,我們需要留到以後研究Spring容器的時候再討論

@Controller
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Controller {	@AliasFor(annotation = Component.class)	String value() default "";}

在Controller的test裡面有這麼一段程式碼

@Testpublic void testWithComponentAnnotationOnly() {    ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);    provider.addIncludeFilter(new AnnotationTypeFilter(Component.class));    provider.addExcludeFilter(new AnnotationTypeFilter(Repository.class));    provider.addExcludeFilter(new AnnotationTypeFilter(Service.class));    provider.addExcludeFilter(new AnnotationTypeFilter(Controller.class));    Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);    assertThat(candidates.size()).isEqualTo(3);    assertThat(containsBeanClass(candidates, NamedComponent.class)).isTrue();    assertThat(containsBeanClass(candidates, ServiceInvocationCounter.class)).isTrue();    assertThat(containsBeanClass(candidates, BarComponent.class)).isTrue();    assertThat(containsBeanClass(candidates, FooServiceImpl.class)).isFalse();    assertThat(containsBeanClass(candidates, StubFooDao.class)).isFalse();    assertThat(containsBeanClass(candidates, NamedStubDao.class)).isFalse();}

也就是說,可以利用掃包的方式來獲取某個包下被某個註解修飾的類。

總結

查詢某註解修飾的所有類就使用 ClassPathScanningCandidateComponentProvider 進行掃描。

查詢某註解修飾的方法,就先找到那個類,然後得到所有的方法,使用AnnotatedElementUtils.hasAnnotation判斷方法是否被某註解修飾即可

下面是一個簡單的例子

ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);provider.addIncludeFilter(new AnnotationTypeFilter(DockingAnnotation.class));Set<BeanDefinition> candidates = provider.findCandidateComponents("package_name");for (BeanDefinition definition : candidates){    try {        Class clz = Class.forName(definition.getBeanClassName());        Method[] methods = clz.getMethods();        for (Method method : methods){            if (AnnotatedElementUtils.hasAnnotation(method,DockIngMessage.class)){                DockIngMessage dockIngMessage = AnnotatedElementUtils.getMergedAnnotation(method,DockIngMessage.class);                System.out.println(dockIngMessage.name());            }        }    } catch (ClassNotFoundException e) {        e.printStackTrace();    }}

13
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • HTML5 新屬性(全域性屬性)