springboot 注解中@AliaFor的原理:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface AliasFor {//作用就是给AliasFor注解的value属性起了一个别名叫做attribute,那么调用的时候调用 @AliasFor(attribute='123')那么value属性的值就是123@AliasFor("attribute")String value() default "";//作用就是给AliasFor注解的attribute属性起了一个别名叫做value,那么调用的时候调用 @AliasFor(value='123')那么attribute属性的值就是123@AliasFor("value")String attribute() default "";//元注解:默认是Annotation,作用是标明别名指向的是那个注解,在使用的时候如果不指定annotation的类型的话,默认使用元注解的类型就是AnnotationClass<? extends Annotation> annotation() default Annotation.class;}
1.在同一个注解中为同一个功能定义两个名称不一样的属性,那么这两个属性彼此互为别名
比如:@RequestMapping注解中的value和path它们两互为别名。
@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Mappingpublic @interface RequestMapping {String name() default "";@AliasFor("path")String[] value() default {};@AliasFor("value")String[] path() default {};RequestMethod[] method() default {};String[] params() default {};String[] headers() default {};String[] consumes() default {};String[] produces() default {};
验证代码:
//定义注解@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface MyAliasAnnotation {@AliasFor(value = "location")String value() default "";@AliasFor(value = "value")String location() default "";}//编写测试类public class MyClass {@MyAliasAnnotation(location = "这是值")public static void one(){}@MyAliasAnnotation(value = "这是值")public static void one2(){}}//编写测试代码public class MyClassTest {@Testpublic void test1() throws NoSuchMethodException {Consumer<MyAliasAnnotation> logic = a -> {Assert.assertTrue("", "这是值".equals(a.value()));Assert.assertTrue("", a.value().equals(a.location()));};MyAliasAnnotation aliasAnnotation = AnnotationUtils.findAnnotation(MyClass.class.getMethod("one"), MyAliasAnnotation.class);logic.accept(aliasAnnotation);MyAliasAnnotation aliasAnnotation2 = AnnotationUtils.findAnnotation(MyClass.class.getMethod("one2"), MyAliasAnnotation.class);logic.accept(aliasAnnotation2);}}
因为两个属性互为别名,所以在value没有赋值的情况下,通过AnnotationUtils仍然可以获取到值,而通过java原生的方式则无法获取。
Spring其实是自己实现了jdk动态的拦截器来实现别名功能.
1.把多个元注解的属性组合在一起形成新的注解
比如:在springboot启动类注解中,他就继承了EnableAutoConfiguration注解的exclude、excludeName属性,和ComponentScan的scanBasePackages、basePackageClasses属性
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {/*** Exclude specific auto-configuration classes such that they will never be applied.* @return the classes to exclude*/@AliasFor(annotation = EnableAutoConfiguration.class)Class<?>[] exclude() default {};@AliasFor(annotation = EnableAutoConfiguration.class)String[] excludeName() default {};@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")String[] scanBasePackages() default {};@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")Class<?>[] scanBasePackageClasses() default {};@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;}
继承注解的功能
- 比如:如@Controller,@Service,@Repository都继承了@Component的功能,他们的基本作用和@Component完全一样都是标明某个类是Spring的Bean,需要Spring容器进行管理。
不同之处在于对Spring bean进行了归类,从而能对不同类型的Bean进行不同的处理。 ```java @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service {
/**
- The value may indicate a suggestion for a logical component name,
- to be turned into a Spring bean in case of an autodetected component.
- @return the suggested component name, if any (or empty String otherwise) */ @AliasFor(annotation = Component.class) String value() default “”;
}
根据@AliasFor的使用形式我们可以将它分为三类```java// 1.注解内部的显性别名public @interface ContextConfiguration {@AliasFor("locations")String[] value() default {};@AliasFor("value")String[] locations() default {};}//2.用于元注解属性的显性别名@ContextConfigurationpublic @interface MyTestConfig {//如案例所示,xmlFiles()指向元注解ContextConfiguration的locations属性,相当于给元注解的locations属性在当前注解中做了一个映射,//使用@MyTestConfig(xmlFiles='123')和使用@ContextConfiguration(locations='123')效果一样@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")String[] xmlFiles();}// 3.注解中的隐性别名(包括注解中的可传递隐性别名)@ContextConfigurationpublic @interface MyTestConfig {//如案例所示,value()、groovyScripts()、xmlFiles()都是指向元注解ContextConfiguration的locations属性,// 所以他们的作用一样互为隐形别名@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")String[] value() default {};@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")String[] groovyScripts() default {};@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")String[] xmlFiles() default {};}public @interface GroovyOrXmlTestConfig {//如案例所示,groovy()、xml()分别指向元注解MyTestConfig的groovyScripts属性和元注解ContextConfiguration的locations属性//因为元注解MyTestConfig的groovyScripts属性指向元注解ContextConfiguration的locations属性,所以groovy和xml互为可传递隐性别名@AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts")String[] groovy() default {};@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")String[] xml() default {};}
实现原理代码:
