Spring源码学习笔记(4)——注解扫描

时间:2022-07-24
本文章向大家介绍Spring源码学习笔记(4)——注解扫描,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Spring源码学习笔记(4)——注解扫描

一. @ComponentScan注解基本使用

  1. @ComponentScan注解是Spring十分重要的一个注解,它代替了之前基于配置文件的模式下的标签:
<context:component-scan>
  1. 实现的功能完全一样,即扫描指定包下的组件,默认扫描标记了@Component、@Service、@Controller和@Repository的类。此外,还可以指定过滤规则和自定义过滤模式。
  2. 示例程序 主配置:
package william.annotation;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

/**
 * @Auther: ZhangShenao
 * @Date: 2018/9/21 10:15
 * @Description:配置类
 */
@Configuration
//使用@ComponentScan,默认扫描MainConfig配置类所在包及其子包下的@Component、@Service、@Controller和@Repository组件
@ComponentScan	
public class MainConfig {

}
  1. 在MainConfig的子包下,分别定义了UserController、UserService和UserDao测试类,并分别标记了@Controller、@Service和@Repository。 下面编写启动类,打印容器中所有注册的Bean的name
public class AnnotationMain {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
     //打印容器中所有定义的BeanName
     Arrays.stream(applicationContext.getBeanDefinitionNames())
         .forEach(System.err::println);
    }
}
  1. 控制台输出如下:
mainConfig
userDao
userController
userService
  1. 可以看到,默认情况下,@ComponentScan注解将所有组件都扫描并注入到IoC容器中。

二. 配置过滤规则

  1. @Filter注解 @ComponentScan注解支持使用Spring提供的过滤规则,也可以定制自己的组件过滤规则。 @ComponentScan注解有两个重要的属性:includeFilters和excludeFilters,分别对应包含规则和排除规则,具体见源码:
	//指定只扫描哪些组件
	Filter[] includeFilters() default {};

	//指定不扫描哪些组件
	Filter[] excludeFilters() default {};
  1. 其中FilterType是一个枚举,定义了过滤的类型,有以下几种:
    1. ANNOTATION:基于注解过滤
    2. ASSIGNABLE_TYPE:基于指定类型过滤
    3. ASPECTJ:基于AspectJ表达式过滤
    4. REGEX:基于正则表达式过滤
    5. CUSTOM:自定义过滤

    有了@Filter注解和FilterType过滤类型,我们就可以指定自己的注解扫描过滤规则。

  2. 包含规则 使用@ComponentScan的includeFilters属性,可以指定只扫描哪些组件,如下:
@Configuration
@ComponentScan(includeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {UserService.class})
        },useDefaultFilters = false)
public class MainConfig {

}
  1. 上面代码使用了FilterType.ASSIGNABLE_TYPE过滤类型,结合includeFilters属性,表示只扫描类型为UserService的组件。 注:使用includeFilters属性时,要记得将useDefaultFilters置为false,否则Spring仍然会使用默认的@Filter扫描@Component、@Service、@Controller和@Repository的类
  2. 排除规则 类似includeFilters,使用excludeFilters属性可以排除指定的组件,示例如下:
@Configuration
@ComponentScan(excludeFilters = {
    @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class MainConfig {

}
  1. 这段代码使用了ANNOTATION过滤方式,意义为不扫描标记了@Controller注解的类,控制台打印:
mainConfig
userDao
userService
  1. 可以看到UserController并没有被扫描进来。

三. 自定义过滤规则

  1. TypeFilter接口 FilterType还提供了CUSTOM自定义类型,允许我们实现定制化的组件扫描规则。使用方式十分简单,只需要实现TypeFilter接口。 TypeFilter定义如下:
public interface TypeFilter {

    /**
	 * 指定类是否匹配组件扫描的过滤规则
	 * @param metadataReader 目标类的元数据reader
	 * @param metadataReaderFactory 可以获取其他类元数据reader的工厂
	 * @return 是否匹配过滤规则
	 * @throws IOException 
	 */
    boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
        throws IOException;

}
  1. 该接口只定义了一个match方法,表示是否匹配扫描规则。如果返回true,则该类会被@ComponentScan扫描注入到IoC容器中。 该方法有两个参数:
    1. metadataReader:当前扫描到的目标类的元数据读取器,可以通过该对象获取目标类的信息;
    2. metadataReaderFactory:其他类的元数据工厂了,可以获取其他类的信息
  2. 代码示例 下面简单演示自定义过滤规则。 首先编写自定义Filter,实现TypeFilter接口
package william.annotation.filter;

import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;

/**
 * @Auther: ZhangShenao
 * @Date: 2018/9/21 11:14
 * @Description:自定义组件扫描过滤器
 */
public class UserDaoTypeFilter implements TypeFilter{

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory){
        //获取目标类元信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        String className = classMetadata.getClassName();

        //只扫描UserDao类
        return "william.annotation.dao.UserDao".equalsIgnoreCase(className);
    }
}
  1. 该实现简单过滤了UserDao类。 配置自定义过滤规则
@Configuration
@ComponentScan(includeFilters = {
    @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {UserDaoTypeFilter.class})
},useDefaultFilters = false)
public class MainConfig {

}
  1. 控制台打印:
mainConfig
userDao
  1. 可以看到,引入自定义过滤规则后,只有UserDao类被注入到了IoC容器中。