Spring学习过程中学到的知识点总结

下面就这几个方面记录下,为以后复习做个笔记。

一、@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();
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值