定义注解
元注解:
- @Target标记当前注解的作用范围
- @Retention标记当前注解在什么情况下生效。注解的生命周期。SOURCE:源码中保存,编译的时候移除。CLASS:源码,编译后class文件保留,jvm加载的时候移除。RUNNING:源码,class文件,jvm中都保存。
- @Inherited当前注解是否可以被(继承)。例如:如果一个类上标注了带有@Inherited注解,然后它的子类继承它,也具有这个注解。
- @Documented是否可以记录到文档
通过@Interface定义注解。并标注元注解。
package com.example.AnnotionTest;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestAnnotation {
String name() default "";
}
通过@Interface定义注解。可继承的注解。标注元注解@Inherited
。定义一个father类,一个son类。son类继承father类。
package com.example.AnnotionTest;
import java.lang.annotation.*;
@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface TestAnnotation2 {
String value();
}
定义一个Father类,被标注@TestAnnotation2注解。@TestAnnotation2注解中有@Inherited注解。
package com.example.AnnotionTest;
@TestAnnotation2(value = "I'm Father")
public class Father {
}
定义一个Son类,继承Father类。看看Son类是否能获取注解@TestAnnotation2
package com.example.AnnotionTest;
public class Son extends Father {
public static void main(String[] args) {
Son son = new Son();
TestAnnotation2 annotation = son.getClass().getAnnotation(TestAnnotation2.class);
System.out.println("son上获取的注解:" + annotation);
}
}
定义一个注解,标注其他注解。属性可以通过注解@AliasFor
进行关联。
package com.example.AnnotionTest;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@TestAnnotation2(value = "")
@Documented
public @interface TestAnnotation3 {
String address() default "";
@AliasFor(annotation = TestAnnotation2.class,attribute = "value")
String value();
}
测试过滤一个类中标注注解的方法
重点关注@TestAnnotation3,@TestAnnotation2注解。通过AnnotatedElementUtils.getMergedAnnotation方法的获取情况。
package com.example.AnnotionTest;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import java.lang.reflect.Method;
import java.util.Map;
public class AnnotionTest2 {
@TestAnnotation
public void test1(){
System.out.println("我是test1");
}
@TestAnnotation2(value = "test2")
public void test2(){
System.out.println("我是test2");
}
@TestAnnotation3(value = "test3")
public void test3(){
System.out.println("我是test3");
}
public static void main(String[] args) {
try {
Map<Method, TestAnnotation> methodTestAnnotationMap = MethodIntrospector.selectMethods(AnnotionTest2.class, new MethodIntrospector.MetadataLookup<TestAnnotation>() {
@Override
public TestAnnotation inspect(Method method) {
return AnnotatedElementUtils.getMergedAnnotation(method, TestAnnotation.class);
}
});
Map<Method, TestAnnotation2> methodTestAnnotationMap2 = MethodIntrospector.selectMethods(AnnotionTest2.class, new MethodIntrospector.MetadataLookup<TestAnnotation2>() {
@Override
public TestAnnotation2 inspect(Method method) {
return AnnotatedElementUtils.getMergedAnnotation(method, TestAnnotation2.class);
}
});
Map<Method, TestAnnotation3> methodTestAnnotationMap3 = MethodIntrospector.selectMethods(AnnotionTest2.class, new MethodIntrospector.MetadataLookup<TestAnnotation3>() {
@Override
public TestAnnotation3 inspect(Method method) {
return AnnotatedElementUtils.getMergedAnnotation(method, TestAnnotation3.class);
}
});
System.out.println(methodTestAnnotationMap);
System.out.println(methodTestAnnotationMap2);
System.out.println(methodTestAnnotationMap3);
}catch(Exception e) {
System.out.println(e);
}
}
}
可以发现。过滤标注注解的方法。对于@TestAnnotation2,可以获取到标注@TestAnnotation2,@TestAnnotation3注解标注的方法
。说明,AnnotatedElementUtils.getMergedAnnotation方法获取注解标注的方法,会向下取它的子注解
。
将AnnotatedElementUtils.getMergedAnnotation替换成AnnotationUtils.getAnnotation。输入结果如下。也可以过滤出来方法,但是注解上的值是空。因为AnnotatedElementUtils支持属性覆盖
。
AnnotatedElementUtils与AnnotationUtils的区别
spring为开发人员提供了两个搜索注解的工具类,分别是AnnotatedElementUtils和AnnotationUtils。
AnnotationUtils
用于处理注解,处理元注解,桥接方法(编译器为通用声明生成)以及超方法(用于可选注解继承)的常规使用程序方法。
AnnotatedElements
用于在AnnotatedElements上查找注解,元注解和可重复注解的常规实用程序方法。AnnotatedElementUtils为Spring的元注解编程模型定义了公共API,并支持注解属性覆盖
。如果您不需要支持注解属性覆盖,请考虑使用AnnotationUtils。
支持@Inherited
属性覆盖
指的是注解的一个成员覆盖另一个成员,最后两者成员属性值一致。
获取类中的注解属性
idea快捷键查看一个类的所有方法
快捷键:ALT + 7
。
MethodIntrospector
MethodIntrospector
是Spring-core包中的一个工具类。它定义了搜索元数据相关的方法。通常用于查找注解的方法。
MetadataLookup
是一个函数式接口。对于给定的方法执行查找并返回关联的元数据。
AnnotatedElementUtils
AnnotatedElementUtils
注解元素工具类。它为Spring的元注解变成模型定义了公共API。并支持注解属性覆盖。如果不需要支持注解属性覆盖,请考虑使用AnnotionUtils
。
AnnotatedElementUtils提供了两类查找。
①get语义的查找
get语义的查找可查找直接定义在AnnotatedElement(比如方法,类等)上的,或继承来的注解。
②find语义的查找
如果AnnotatedElement是类,则搜索这个类的超类和接口。
如果AnnotatedElement是方法,则搜索这个方法的桥接方法,以及超类和接口中的方法。
get和find方法都支持@Inherited
getMergedAnnotationAttributes
getMergedAnnotation
getAllMergedAnnotations
getMergedRepeatableAnnotations
findMergedAnnotationAttributes
findMergedAnnotation
findAllMergedAnnotations
findMergedRepeatableAnnotations
xxl-job中的应用
Map<Method, XxlJob> annotatedMethods = null; // referred to :org.springframework.context.event.EventListenerMethodProcessor.processBean
try {
annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
new MethodIntrospector.MetadataLookup<XxlJob>() {
@Override
public XxlJob inspect(Method method) {
return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);
}
});
} catch (Throwable ex) {
logger.error("xxl-job method-jobhandler resolve error for bean[" + beanDefinitionName + "].", ex);
}
在xxl-job的源码中。通过MethodIntrospector的selectMethods方法查找一个类的元数据。AnnotatedElementUtils.findMerged
Annotation()方法查找xxl-job注解标注的Method。
总结
新增注解A上标注@Inherited。A标注父类,子类继承父类,那么在spring中子类可以通过AnnotationUtils或者AnnotatedElmentUtils获取上标注在父类上的注解A。
AnnotationUtils与AnnotatedElmentUtils的区别,AnnotatedElmentUtils支持属性覆盖,其他的暂不了解。
补充。@Inherited
通过jdk原生注解api,和spring提供的api。验证注解@Inherited。
jdk原生api
注解不标注@Inherited注解。定义接口上,通过jdk原生api获取。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestOne {
String oneName() default "";
String one() default "";
}
// 注解标注在接口类上
@TestOne
public interface Eat {
}
// 类实现接口
public class Parent implements Eat {
}
// 子类继承父类
public class Son extends Parent{
}
public class TestInHerited {
public static void main(String[] args) {
TestOne testOne = Eat.class.getAnnotation(TestOne.class);
TestOne parentTestOne = Parent.class.getAnnotation(TestOne.class);
TestOne sonTestOne = Son.class.getAnnotation(TestOne.class);
System.out.println(testOne);
System.out.println(parentTestOne);
System.out.println(sonTestOne);
}
}
结论:注解上没有标注@Inherited注解。注解标注在接口上。jdk原生api:接口实现类,接口实现类的子类获取不到注解。
注解标注@Inherited注解。定义接口上,通过jdk原生api获取。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface TestOne {
String oneName() default "";
String one() default "";
}
public @interface TestOne {
String oneName() default "";
String one() default "";
}
// 注解标注在接口类上
@TestOne
public interface Eat {
}
// 类实现接口
public class Parent implements Eat {
}
// 子类继承父类
public class Son extends Parent{
}
public class TestInHerited {
public static void main(String[] args) {
TestOne testOne = Eat.class.getAnnotation(TestOne.class);
TestOne parentTestOne = Parent.class.getAnnotation(TestOne.class);
TestOne sonTestOne = Son.class.getAnnotation(TestOne.class);
System.out.println(testOne);
System.out.println(parentTestOne);
System.out.println(sonTestOne);
}
}
结论:注解上增加注解@Inherited。结果一样。
注解不标注@Inherited注解。定义类上,通过jdk原生api获取。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestOne {
String oneName() default "";
String one() default "";
}
public interface Eat {
}
// 类实现接口。// 注解标注在类上
@TestOne
public class Parent implements Eat {
}
// 子类继承父类
public class Son extends Parent{
}
public class TestInHerited {
public static void main(String[] args) {
TestOne testOne = Eat.class.getAnnotation(TestOne.class);
TestOne parentTestOne = Parent.class.getAnnotation(TestOne.class);
TestOne sonTestOne = Son.class.getAnnotation(TestOne.class);
System.out.println(testOne);
System.out.println(parentTestOne);
System.out.println(sonTestOne);
}
}
结论:jdk原生API。只有注解标注的类可以获取到注解。
注解标注@Inherited注解。定义类上,通过jdk原生api获取。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface TestOne {
String oneName() default "123";
String one() default "456";
}
public interface Eat {
}
// 类实现接口。// 注解标注在类上
@TestOne
public class Parent implements Eat {
}
// 子类继承父类
public class Son extends Parent{
}
public class TestInHerited {
public static void main(String[] args) {
TestOne testOne = Eat.class.getAnnotation(TestOne.class);
TestOne parentTestOne = Parent.class.getAnnotation(TestOne.class);
TestOne sonTestOne = Son.class.getAnnotation(TestOne.class);
System.out.println(testOne);
System.out.println(parentTestOne);
System.out.println(sonTestOne);
}
}
结论:jdk原生api,@Inherited注解只有标注在类上。子类才会获取到注解
。
jdk原生api,@Inherited总结
@Inherited注解的含义是继承。在jdk原生api中。只有标注到类上,子类才能获取到注解。标注到接口上,实现类获取不到注解
。
spring框架提供的注解处理类
注解标注@Inherited注解。定义类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface TestOne {
String oneName() default "123";
String one() default "456";
}
public interface Eat {
}
// 类实现接口。// 注解标注在类上
@TestOne
public class Parent implements Eat {
}
// 子类继承父类
public class Son extends Parent{
}
public class TestInHerited {
public static void main(String[] args) {
/* TestOne testOne = Eat.class.getAnnotation(TestOne.class);
TestOne parentTestOne = Parent.class.getAnnotation(TestOne.class);
TestOne sonTestOne = Son.class.getAnnotation(TestOne.class);*/
TestOne testOne = AnnotationUtils.findAnnotation(Eat.class, TestOne.class);
TestOne parentTestOne = AnnotationUtils.findAnnotation(Parent.class, TestOne.class);
TestOne sonTestOne = AnnotationUtils.findAnnotation(Son.class, TestOne.class);
System.out.println(testOne);
System.out.println(parentTestOne);
System.out.println(sonTestOne);
}
}
结论:和jdk原生api一样。子类可以获取父类上的注解。
注解不标注@Inherited注解。定义类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestOne {
String oneName() default "123";
String one() default "456";
}
public interface Eat {
}
// 类实现接口。// 注解标注在类上
@TestOne
public class Parent implements Eat {
}
// 子类继承父类
public class Son extends Parent{
}
public class TestInHerited {
public static void main(String[] args) {
/* TestOne testOne = Eat.class.getAnnotation(TestOne.class);
TestOne parentTestOne = Parent.class.getAnnotation(TestOne.class);
TestOne sonTestOne = Son.class.getAnnotation(TestOne.class);*/
TestOne testOne = AnnotationUtils.findAnnotation(Eat.class, TestOne.class);
TestOne parentTestOne = AnnotationUtils.findAnnotation(Parent.class, TestOne.class);
TestOne sonTestOne = AnnotationUtils.findAnnotation(Son.class, TestOne.class);
System.out.println(testOne);
System.out.println(parentTestOne);
System.out.println(sonTestOne);
}
}
结论:即使不标注注解@Inherited,子类依然可以获取到父类上标注的注解。
注解标注@Inherited注解。定义接口上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface TestOne {
String oneName() default "123";
String one() default "456";
}
@TestOne
public interface Eat {
}
// 类实现接口。// 注解标注在类上
// @TestOne
public class Parent implements Eat {
}
// 子类继承父类
public class Son extends Parent{
}
public class TestInHerited {
public static void main(String[] args) {
/* TestOne testOne = Eat.class.getAnnotation(TestOne.class);
TestOne parentTestOne = Parent.class.getAnnotation(TestOne.class);
TestOne sonTestOne = Son.class.getAnnotation(TestOne.class);*/
TestOne testOne = AnnotationUtils.findAnnotation(Eat.class, TestOne.class);
TestOne parentTestOne = AnnotationUtils.findAnnotation(Parent.class, TestOne.class);
TestOne sonTestOne = AnnotationUtils.findAnnotation(Son.class, TestOne.class);
System.out.println(testOne);
System.out.println(parentTestOne);
System.out.println(sonTestOne);
}
}
结论:接口实现类,子类都可以获取到接口上标注的注解。
注解不标注@Inherited注解。定义接口上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestOne {
String oneName() default "123";
String one() default "456";
}
@TestOne
public interface Eat {
}
// 类实现接口。// 注解标注在类上
// @TestOne
public class Parent implements Eat {
}
// 子类继承父类
public class Son extends Parent{
}
public class TestInHerited {
public static void main(String[] args) {
/* TestOne testOne = Eat.class.getAnnotation(TestOne.class);
TestOne parentTestOne = Parent.class.getAnnotation(TestOne.class);
TestOne sonTestOne = Son.class.getAnnotation(TestOne.class);*/
TestOne testOne = AnnotationUtils.findAnnotation(Eat.class, TestOne.class);
TestOne parentTestOne = AnnotationUtils.findAnnotation(Parent.class, TestOne.class);
TestOne sonTestOne = AnnotationUtils.findAnnotation(Son.class, TestOne.class);
System.out.println(testOne);
System.out.println(parentTestOne);
System.out.println(sonTestOne);
}
}
结论:注解上不标注@Inherited。接口实现类,子类也可以获取到接口上标注的注解。
spring框架的api,@Inherited总结
spring框架提供的工具类,操作注解。属性@Inherited没有意义。接口实现类,子类都可以获取到注解信息。
补充。@AliasFor
同一个注解内,属性设置别名
public @interface AliasTest{
@AliasFor("locations")
String[] value() default {};
@AliasFor("value")
String[] locations() default {};
}
使用限制:
(1)别名都是成对出现,为value属性设置locations别名,需要含有locations属性并为其设置value别名;
(2)成对出现的属性返回值相同;
(3)必须为成对出现的属性必须设置默认值;
(4)成对出现的属性的默认值必须相同;
(5)AliasFor注解不能设置annotation属性
注解内,设置其他注解的属性别名
public @interface AliasTestOne{
String[] one() default {};
}
@AliasTestOne
public @interface AliasTestTwo{
@AliasFor(annotation = AliasTestOne.class, attribute = "one")
String[] value() default {};
String[] locations() default {};
}
使用限制:
(1)AliasFor中定义的annotation属性必须为该注解元注解;
(2)AliasFor中定义的attribute属性的值必须为annotation属性指定的元注解中的属性;
补充。AnnotationUtils 与 AnnotatedElementUtils的简单比较
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@TestTwo
public @interface TestOne {
@AliasFor(annotation = TestTwo.class,attribute = "twoName")
String oneName() default "123";
String one() default "456";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestTwo {
String twoName() default "abc";
String two() default "def";
}
@TestOne(oneName = "哈哈")
public interface Eat {
}
public class TestInHerited {
public static void main(String[] args) {
TestTwo annotation = AnnotationUtils.findAnnotation(Eat.class, TestTwo.class);
System.out.println(annotation);
TestTwo mergedAnnotation = AnnotatedElementUtils.findMergedAnnotation(Eat.class, TestTwo.class);
System.out.println(mergedAnnotation);
}
}
可以发现,通过注解@TestOne设置oneName属性,别名对应的@TestTwo的twoName属性。
AnnotationUtils获取注解@TestTwo的属性,还是@TestTwo自己默认的属性
。
AnnotatedElementUtils获取注解@TestTwo的属性,获取到的是@TestOne中别名oneName设置的属性
。
补充。MethodIntrospector方法内省的使用
// 定义注解TestMethodOne ,元注解上配置注解TestMethodTwo
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@TestMethodTwo
public @interface TestMethodOne {
String methodOne() default "";
}
// 定义注解TestMethodTwo
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestMethodTwo {
String methodTwo() default "";
}
// 定义方法测试类。testMethod方法标注注解TestMethodOne,testMehtodThree方法标注注解TestMethodTwo
public class MethodTest {
@TestMethodOne(methodOne = "哈哈哈哈")
public void testMethod() {
System.out.println("我是测试方法testMethod注解嘞");
}
public void testMethodTwo() {
System.out.println("我是测试方法testMethodTwo注解嘞");
}
@TestMethodTwo(methodTwo = "嘻嘻嘻嘻")
public void testMethodThree() {
System.out.println("testMethodThree");
}
}
// 验证。过滤标注注解TestMethodOne的方法。过滤标注注解TestMethodTwo的方法
public class TestMethod {
public static void main(String[] args) {
Map<Method, TestMethodTwo> methodTwo = MethodIntrospector.selectMethods(MethodTest.class, new MethodIntrospector.MetadataLookup<TestMethodTwo>() {
@Override
public TestMethodTwo inspect(Method method) {
return AnnotationUtils.findAnnotation(method, TestMethodTwo.class);
}
});
Map<Method, TestMethodOne> methodOne = MethodIntrospector.selectMethods(MethodTest.class, new MethodIntrospector.MetadataLookup<TestMethodOne>() {
@Override
public TestMethodOne inspect(Method method) {
return AnnotationUtils.findAnnotation(method, TestMethodOne.class);
}
});
System.out.println(methodTwo);
System.out.println(methodOne);
}
}
可以发现,通过MethodIntrospector的selectMethod方法可以按照注解过滤对应的方法。
过滤注解TestMethodTwo方法,返回的是两个方法。testMethodThree,testMethod。testMethod方法上标注的@TestMethodOne注解,元注解中有@TestMethodTwo注解
。但是@TestMethodTwo属性确实空的。可以通过上面介绍的AnnotatedElementUtils的方法获取属性。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@TestMethodTwo
public @interface TestMethodOne {
// 定义了别名
@AliasFor(annotation = TestMethodTwo.class,attribute = "methodTwo")
String methodOne() default "";
}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestMethodTwo {
String methodTwo() default "";
}
public class MethodTest {
@TestMethodOne(methodOne = "哈哈哈哈")
public void testMethod() {
System.out.println("我是测试方法testMethod注解嘞");
}
public void testMethodTwo() {
System.out.println("我是测试方法testMethodTwo注解嘞");
}
@TestMethodTwo(methodTwo = "嘻嘻嘻嘻")
public void testMethodThree() {
System.out.println("testMethodThree");
}
}
public class TestMethod {
public static void main(String[] args) {
/* Map<Method, TestMethodTwo> methodTwo = MethodIntrospector.selectMethods(MethodTest.class, new MethodIntrospector.MetadataLookup<TestMethodTwo>() {
@Override
public TestMethodTwo inspect(Method method) {
return AnnotationUtils.findAnnotation(method, TestMethodTwo.class);
}
});
Map<Method, TestMethodOne> methodOne = MethodIntrospector.selectMethods(MethodTest.class, new MethodIntrospector.MetadataLookup<TestMethodOne>() {
@Override
public TestMethodOne inspect(Method method) {
return AnnotationUtils.findAnnotation(method, TestMethodOne.class);
}
});*/
Map<Method, TestMethodTwo> methodTwo = MethodIntrospector.selectMethods(MethodTest.class, new MethodIntrospector.MetadataLookup<TestMethodTwo>() {
@Override
public TestMethodTwo inspect(Method method) {
// 通过AnnotatedElementUtils获取注解
return AnnotatedElementUtils.findMergedAnnotation(method, TestMethodTwo.class);
}
});
Map<Method, TestMethodOne> methodOne = MethodIntrospector.selectMethods(MethodTest.class, new MethodIntrospector.MetadataLookup<TestMethodOne>() {
@Override
public TestMethodOne inspect(Method method) {
// 通过AnnotatedElementUtils获取注解
return AnnotatedElementUtils.findMergedAnnotation(method, TestMethodOne.class);
}
});
System.out.println(methodTwo);
System.out.println(methodOne);
}
}
可以发现,过滤出来的方法,注解@TestMethodOne中的methodOne属性与@TestMethodTwo中的methodTwo一致。