下面就这几个方面记录下,为以后复习做个笔记。
一、@AliasFor注解: 这个注解有两个用途
- 别名。它可以注解到自定义注解的两个属性上,表示这两个互为别名,也就是说这两个属性其实同一个含义。但是需要注意互为别名的属性必须有默认值,并且默认值相等。如果使用该注解时,必须保证互为别名的属性相等。否则会报错。我们可以显式或者隐式设置别名,并且别名具有传递性。如:
@ContextConfiguration public @interface MyTestConfig { @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 {}; }
可以看到,在MyTestConfig注解中,为value,groovyScripts,xmlFiles都定义了别名@AliasFor(annotation = ContextConfiguration.class, attribute = "locations"),所以,其实在这个注解中,value、groovyScripts和xmlFiles也互为别名,这个就是所谓的在统一注解中的隐式别名方式;
@MyTestConfig public @interface GroovyOrXmlTestConfig { @AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts") String[] groovy() default {}; @AliasFor(annotation = ContextConfiguration.class, attribute = "locations") String[] xml() default {}; }
1,GroovyOrXmlTestConfig把 @MyTestConfig(参考上一个案例)作为元注解;
2,定义了groovy属性,并作为MyTestConfig中的groovyScripts属性的别名;
3,定义了xml属性,并作为ContextConfiguration中的locations属性的别名;
4,因为MyTestConfig中的groovyScripts属性本身就是ContextConfiguration中的locations属性的别名;所以xml属性和groovy属性也互为别名;
这个就是别名的传递性; - 可以继承其他注解。上面的@AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts") 就是继承注解。
二、@Repeatable注解
作用:注解是用于声明其它类型注解的元注解,来表示这个声明的注解是可重复的。@Repeatable的值是另一个注解,其可以通过这个另一个注解的值来包含这个可重复的注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Animals {
Animal[] value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Animals.class)
public @interface Animal {
String name() default "Tom";
String subject() default "Cat";
}
其中@Animal元注解@Repeatable
中的值,使用了@Animals注解。而@Animals中包含了@Animal 数组。这就是官方定义的用法。
在没有这个注解之前,我们是如何实现的呢?
@ComponentScans(value = {
@ComponentScan(value = "com.test"),
@ComponentScan(value = "com.demo")
})
class TestDemo{
private String name = "Tom";
@Bean
public User getUser(){
return new User();
}
@Override
public String toString() {
return this.name + "sss";
}
}
其实@Repeatable注解也是这样实现的。只是语法糖而已。
三、TypeFilter自定义过滤器
这里我们看下SpringBoot启动类@SpringBootApplication注解
@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 {
public enum FilterType {
/**
* 注解类型
*/
ANNOTATION,
/**
* ANNOTATION:指定的类型
*/
ASSIGNABLE_TYPE,
/**
* Aspectj类型
*/
ASPECTJ,
/**
* 按照正则表达式
*/
REGEX,
/**
*自定义
*/
CUSTOM
}
我们看下AutoConfigurationExcludeFilter类:
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
private ClassLoader beanClassLoader;
private volatile List<String> autoConfigurations;
@Override
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
}
private boolean isConfiguration(MetadataReader metadataReader) {
return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
}
private boolean isAutoConfiguration(MetadataReader metadataReader) {
return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
}
protected List<String> getAutoConfigurations() {
if (this.autoConfigurations == null) {
this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
this.beanClassLoader);
}
return this.autoConfigurations;
}
}
可以看到主要匹配EnableAutoConfiguration.class 和@Configuration注解的类。这里用到了自定义TypeFilter,通过自定义过滤规则。实现@ComponentScan扫描规则。那么我们是不是也可以自定义TypeFilter过滤规则呢?这是肯定的。
定义一个注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ComponentScan(value = "com.example.demo",
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type= FilterType.CUSTOM,classes = MyFilter.class)})
public @interface MyTypeFilter {
String value() default "";
}
定义一个过滤规则
public class MyFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前类注解信息(当前类指componentScan指定扫描的类,)
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前类的资源(类的路径)
Resource resource = metadataReader.getResource();
//获取正在扫描的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
if(className.contains("all")){
return true;
}
return false;
}
}
测试类
public class MyFilterTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestDemo.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String str : beanDefinitionNames){
System.out.println(str);
}
}
}
@Configuration
@MyTypeFilter
class TestDemo{
private String name = "Tom";
@Bean
public User getUser(){
return new User();
}
@Override
public String toString() {
return this.name + "sss";
}
}
打印结果:可以看到除了testDemo 、getUser这两个Bean以外。在com.example.demo包下有一个 myCallAble 符合条件。也被扫描到。注入IOC容器中。
09:30:12.916 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
09:30:12.927 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testDemo'
09:30:12.935 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myCallAble'
09:30:12.938 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getUser'
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
testDemo
myCallAble
getUser
四、断言、Java8 函数式编程的使用
public class MyAnnotationTest {
public static void main(String[] args) throws NoSuchMethodException {
Consumer<MyAnnotation> consumer = myAnnotation -> {
Assert.isTrue("Tom".equalsIgnoreCase(myAnnotation.value()),"");
Assert.isTrue(myAnnotation.location().equalsIgnoreCase(myAnnotation.value()),"");
};
MyAnnotation myAnnotation = AnnotationUtils.findAnnotation(MyAnnotationDemo.class.getMethod("doWork"), MyAnnotation.class);
MyAnnotation myAnnotation1 = AnnotationUtils.findAnnotation(MyAnnotationDemo.class.getMethod("doWorking"), MyAnnotation.class);
consumer.accept(myAnnotation);
consumer.accept(myAnnotation1);
Consumer<AnnotationSub> consumer1 = a -> {
Assert.isTrue(a.location().equalsIgnoreCase(a.subValue()),"相等");
System.out.println(a.location() + " --- " + a.subValue());
};
AnnotationSub sub = AnnotatedElementUtils.findMergedAnnotation(AnnotationDemo.class.getMethod("test"),AnnotationSub.class);
AnnotationSub sub1 = AnnotatedElementUtils.findMergedAnnotation(AnnotationDemo.class.getMethod("test1"),AnnotationSub.class);
consumer1.accept(sub);
consumer1.accept(sub1);
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigTest.class);
MyConfigTest myConfig = (MyConfigTest)applicationContext.getBean("myConfigTest");
System.out.println(myConfig.toString());
}
}
class MyAnnotationDemo{
@MyAnnotation(location = "Tom",value = "Tom")
public void doWork(){}
@MyAnnotation(value = "Tom")
public void doWorking(){}
}
class AnnotationDemo{
@AnnotationSub(subValue = "Tom")
public void test(){}
@AnnotationSub(location = "Rock")
public void test1(){}
}
@MyConfig(name = "myConfigTest")
class MyConfigTest{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return super.toString();
}
}