1.@ComponentScan 是什么

主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中。注解定义如下

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    /**
     * 对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组
     * @return
     */
    @AliasFor("basePackages")
    String[] value() default {};
    /**
     * 和value一样是对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组
     * @return
     */
    @AliasFor("value")
    String[] basePackages() default {};
    /**
     * 指定具体的扫描的类
     * @return
     */
    Class<?>[] basePackageClasses() default {};
    /**
     * 对应的bean名称的生成器 默认的是BeanNameGenerator
     * @return
     */
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    /**
     * 处理检测到的bean的scope范围
     */
    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
    /**
     * 是否为检测到的组件生成代理
     * Indicates whether proxies should be generated for detected components, which may be
     * necessary when using scopes in a proxy-style fashion.
     * <p>The default is defer to the default behavior of the component scanner used to
     * execute the actual scan.
     * <p>Note that setting this attribute overrides any value set for {@link #scopeResolver}.
     * @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode)
     */
    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
    /**
     * 控制符合组件检测条件的类文件   默认是包扫描下的  **/*.class
     * @return
     */
    String resourcePattern() default "**/*.class";
    /**
     * 是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的
     * @return
     */
    boolean useDefaultFilters() default true;
    /**
     * 指定某些定义Filter满足条件的组件 FilterType有5种类型如:
     *                                  ANNOTATION, 注解类型 默认
                                        ASSIGNABLE_TYPE,指定固定类
                                        ASPECTJ, ASPECTJ类型
                                        REGEX,正则表达式
                                        CUSTOM,自定义类型
     * @return
     */
    ComponentScan.Filter[] includeFilters() default {};
    /**
     * 排除某些过滤器扫描到的类
     * @return
     */
    ComponentScan.Filter[] excludeFilters() default {};
    /**
     * 扫描到的类是都开启懒加载 ,默认是不开启的
     * @return
     */
    boolean lazyInit() default false;

    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;

        @AliasFor("classes")
        Class<?>[] value() default {};

        @AliasFor("value")
        Class<?>[] classes() default {};

        String[] pattern() default {};
    }
}

basePackages与value: 用于指定包的路径,进行扫描

basePackageClasses: 用于指定某个类的包的路径进行扫描

nameGenerator: bean的名称的生成器

useDefaultFilters: 是否开启对@Component,@Repository,@Service,@Controller的类进行检测

includeFilters: 包含的过滤条件

FilterType.ANNOTATION:按照注解过滤

        FilterType.ASSIGNABLE_TYPE:按照给定的类型

        FilterType.ASPECTJ:使用ASPECTJ表达式

        FilterType.REGEX:正则

        FilterType.CUSTOM:自定义规则

excludeFilters: 排除的过滤条件,用法和includeFilters一样,使用excludeFilters时,useDefaultFilters 要为true。

2.@ComponentScan注解的详细使用

做过web开发的同学一定都有用过@Controller,@Service,@Repository注解,查看其源码你会发现,他们中有一个共同的注解@Component,没错@ComponentScan注解默认就会装配标识了@Controller,@Service,@Repository,@Component注解的类到spring容器中,好下面咱们就先来简单演示一下这个例子

分别创建带注解的service,dao,controller类,目录如下

Controller带@Controller

Service带@Service

Dao带@Repository

java redisTemplate扫描前缀匹配key_spring boot


再建一个配置类MainConfig

//配置类 == 配置文件xml
@Configuration
@ComponentScan(value = "com.enjoy.cap2")
public class MainConfig {

    //给aop容器注册一个bean,类型为返回值类型,默认bean id 是方法名,可以自定义  
    @Bean
    public Person person(){
        return new Person("JJJ",12);
    }
}

测试类

测试一:默认拦截规则

@Test
    public void test01(){

        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] names = applicationContext.getBeanDefinitionNames();
        //System.out.println(names);
        for (String name : names) {
            System.out.println(name);
        }
    }`

测试结果:

java redisTemplate扫描前缀匹配key_spring_02


把所有带注解的bean都注册进去了

测试二:includeFilters 修改Mainconfig如下,使用注解拦截器。过滤类型是ANNOTATION(注解),只过滤controller,关闭默认的过滤器

//配置类 == 配置文件xml
@Configuration
@ComponentScan(value = "com.enjoy.cap2",includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
},useDefaultFilters = false)
public class MainConfig {

    //给aop容器注册一个bean,类型为返回值类型,默认bean id 是方法名,可以自定义  
    @Bean
    public Person person(){
        return new Person("JJJ",12);
    }
}

运行测试类,测试结果:

java redisTemplate扫描前缀匹配key_bean_03


除了自己本身和自定义的bean之外,系统只注入了controller

测试三:excludeFilters 修改Mainconfig如下,使用注解拦截器。过滤类型是ANNOTATION(注解),排除controller,开启默认的过滤器

//配置类 == 配置文件xml
@Configuration
@ComponentScan(value = "com.enjoy.cap2",excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
},useDefaultFilters = true)
public class MainConfig {

    //给aop容器注册一个bean,类型为返回值类型,默认bean id 是方法名,可以自定义  
    @Bean
    public Person person(){
        return new Person("JJJ",12);
    }
}

测试结果:没有注册controller类

java redisTemplate扫描前缀匹配key_spring boot_04


测试四:过滤方式为 ASSIGNABLE_TYPE,指定过滤类型,配置类如下

//配置类 == 配置文件xml
@Configuration
@ComponentScan(value = "com.enjoy.cap2",includeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {OrderController.class})
},useDefaultFilters = false)
public class MainConfig {

    //给aop容器注册一个bean,类型为返回值类型,默认bean id 是方法名,可以自定义  
    @Bean
    public Person person(){
        return new Person("JJJ",12);
    }
}

测试结果:只有指定的orderController类被注册进容器

java redisTemplate扫描前缀匹配key_java_05


测试五:自定义过滤方式

1.新建一个CustomTypeFilter类实现TypeFilter接口,并实现其 match 方法。

public class CustomTypeFilter implements TypeFilter {
    /**
     *
     * @param metadataReader:读取到当前正在扫描类的信息
     * @param metadataReaderFactory:可以获取到其任何类信息
     * @return
     * @throws IOException
     */
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        //获取当前扫描到的类的注解元数据(信息)
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
       //获取当前扫描到的类的元数据(信息)
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前扫描到的类的资源信息(路径)
        Resource resource = metadataReader.getResource();

        String className = classMetadata.getClassName();
        System.out.println("========>"+className);

        if(className.contains("con")){ //当类包含con字符串,匹配成功,返回true
            return true;
        }

        return false;
    }

这里简单对扫描到的类名进行判断,如果类名包含”con“的就符合条件,也就会注入到容器中。
2.修改配置类:type = FilterType.CUSTOM, classes = {CustomTypeFilter.class}

//配置类 == 配置文件xml
@Configuration
@ComponentScan(value = "com.enjoy.cap2",includeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {CustomTypeFilter.class})
},useDefaultFilters = false)
public class MainConfig {

    //给aop容器注册一个bean,类型为返回值类型,默认bean id 是方法名,可以自定义  
    @Bean
    public Person person(){
        return new Person("JJJ",12);
    }
}

测试结果:

java redisTemplate扫描前缀匹配key_自定义_06