springboot 注解中@AliaFor的原理:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @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的类型的话,默认使用元注解的类型就是Annotation
Class<? extends Annotation> annotation() default Annotation.class;
}
1.在同一个注解中为同一个功能定义两个名称不一样的属性,那么这两个属性彼此互为别名
比如:@RequestMapping注解中的value和path它们两互为别名。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @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 {
@Test
public 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.用于元注解属性的显性别名
@ContextConfiguration
public @interface MyTestConfig {
//如案例所示,xmlFiles()指向元注解ContextConfiguration的locations属性,相当于给元注解的locations属性在当前注解中做了一个映射,
//使用@MyTestConfig(xmlFiles='123')和使用@ContextConfiguration(locations='123')效果一样
@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
String[] xmlFiles();
}
// 3.注解中的隐性别名(包括注解中的可传递隐性别名)
@ContextConfiguration
public @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 {};
}
实现原理代码: