Java8中的可重复注解@Repeatable和可继承标记注解@Inherited组合使用注意细节

被标记@Inherited注解的注解,子类可以继承该注解,
注解继承规则:
      1 类注解会被继承,子类有相同注解将覆盖父类注解,其他父类注解仍然继承
      2 子类从写方法,方法注解不会被继承
      3 接口上的注解不被继承
     根据这个规则也可以理解Spring aop中被jdk动态的代理的类无法直接获取到注解,因为jdk的动态代理是基于实现接口的代理,而cglib是基于继承的方式实现代理,因此被cglib动态代理的类可以获取到注解(但是cglib代理类从反编译得到的代码看也重写了目标的方法,为什么还能获取到注解,这个未研究明白,希望知道的大佬能留言互相学习)
 
使用@Repeatable(value = UserRoles.class)表示@UserRole注解可以重复使用
多个标记的@UserRole注解结果存放在 @UserRoles 的value中,@UserRoles即为容器注解
注意点:
1 可重复注解上的注解设置(@Target,@Retention,@Inherited...)需要是容器注解设置的子集,设置相同即可
 
2 当同一个位置只标记有一个可重复注解时,无法通过容器注解类型获取到,如
   通过class.getAnnotation(UserRoles.class) 返回的是null
   通过class.getAnnotations()得到的是单个可重复注解
   如果注解可继承,子类继承的是单个可重复注解.
   当子类上标有单个可重复注解时被认为是相同注解,会覆盖父类的单个可重复注解注解
   当子类上标有多个可重复注解时被认为是容器注解,不会覆盖父类的单个可重复注解注解,出现注意点5的情况
 
3 当同一个位置被标记多个可重复注解时,无法通过可重复注解类型获取到,如
   通过class.getAnnotation(UserRole.class) 返回的时null
   通过class.getAnnotations()得到的是单个容器注解
   如果注解可继承,子类继承的是容器注解.
   当子类标标有单个可重复注解时被认为是不同注解,不会覆盖父类容器注解,出现注意点5的情况
   当子类标标有多个可重复注解时被认为是相同注解,子类会覆盖父类容器注解
 
4 无论同一个位置被标记一个或多个可重复注解,都可以通过class.getDeclaredAnnotationsByType(UserRole.class)
得到类自己申明的可重复注解的数组类型,注解类容相同的不会被去重,数组顺序和注解标记顺序相同
 
5 如果是子类,可能出现容器注解和可重复注解同时出现的情况.

示例代码:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(value = UserRoles.class)
@Inherited
public @interface UserRole {
    String value();
}
//容器注解的设置应该大于等于可重复注解的设置
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UserRoles {
    //固定格式必须是使用 value()
    //返回值为可重复使用注解的数组类型
    UserRole[] value();
}
@UserRole("ceo")
@UserRole("ceo")
@UserRole("father")
@UserRole("husband")
@UserRole("son")
@RequestMapping("parent")
@Cacheable
@CachePut
public class UserInfoService {

    @Cacheable(cacheNames = "cache1")
    public void say1() {

    }

    @UserRole("ceo")
    @UserRole("ceo")
    @Cacheable
    public String say2(String name) {
        return "hello" + name;
    }
}
@Controller("child")
@UserRole("ceo")
// @UserRole("ceo2")
public class UserInfoServiceChild extends UserInfoService {
    @Cacheable(cacheNames = "cache1")
    public void say1() {

    }

    @Override
    public String say2(String name) {
        return "hello" + name;
    }
}
public class InheritedTest {

    @Test
    public void test1() {
        //container
        //同一个类中,如果出现多个可重复注解,getAnnotations()得到的是容器注解类型,不会出现可重复注解
        //同一个类中,如果出现单个可重复注解,getAnnotations()得到的是可重复注解类型,不会出现容器注解类型
        //如果是子类继承的情况下可重复注解和容器注解可能会同时出现
        Annotation[] parentAnnotations = UserInfoService.class.getAnnotations();
        Arrays.stream(parentAnnotations).forEach((anno) -> System.out.println(anno.annotationType().getName()));

        boolean containerAnno = UserInfoService.class.isAnnotationPresent(UserRoles.class);
        System.out.println(containerAnno);

        boolean repeatableAnno = UserInfoService.class.isAnnotationPresent(UserRole.class);
        System.out.println(repeatableAnno);
    }

    /**
     * 获取类自己申明的指定的注解,无论是多个或单个可重复注解都可以获取到
     */
    @Test
    public void test2() {
        UserRole[] declaredAnnotationsByType = UserInfoService.class.getDeclaredAnnotationsByType(UserRole.class);
        for (UserRole userRole : declaredAnnotationsByType) {
            System.out.println(userRole.value());
        }
    }

    @Test
    public void test3() {
        //继承注解测试
        Class<UserInfoServiceChild> childClass = UserInfoServiceChild.class;
        Annotation[] parentAnnotations = childClass.getAnnotations();
        Arrays.stream(parentAnnotations).forEach((anno) -> System.out.println(anno.annotationType().getName()));

        //判断是否存在指定的注解,包括从父类继承的
        boolean containerAnno = childClass.isAnnotationPresent(UserRoles.class);
        System.out.println(containerAnno);

        boolean repeatableAnno = childClass.isAnnotationPresent(UserRole.class);
        System.out.println(repeatableAnno);

        boolean cachePutAnno = childClass.isAnnotationPresent(CachePut.class);
        System.out.println(cachePutAnno);
    }

    //方法注解继承,如果方法被子类重写则注解无法继承
    @Test
    public void test4() {
        Method say2 = ReflectionUtils.findMethod(UserInfoService.class, "say2", String.class);
        Annotation[] annotations = say2.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation.annotationType().getName());
        }
        System.out.println("-----------");

        Method say2Child = ReflectionUtils.findMethod(UserInfoServiceChild.class, "say2", String.class);
        Annotation[] annotationsChild = say2Child.getAnnotations();
        for (Annotation annotation : annotationsChild) {
            System.out.println(annotation.annotationType().getName());
        }
    }

    //接口注解测试
    @Test
    public void test5() {
        Annotation[] annotations = ServiceImpl.class.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation.annotationType());
        }
    }
}

猜你喜欢

转载自blog.csdn.net/u013202238/article/details/85214038