被标记@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 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());
}
}
}