https://segmentfault.com/a/1190000020802458

深入理解 spring 注解之@ComponentScan 注解liu20111590 的专栏-CSDN 博客@componentscan 注解

1 @ComponentScan 注解是什么

其实很简单,@ComponentScan 主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到 spring 的 bean 容器中

2 @ComponentScan 注解的详细使用

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

2.1 准备数据

在包 com. Zhang. Controller 下新建一个 UserController 带@Controller 注解如下:

package com.zhang.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
}

在包 com. Zhang. Service 下新建一个 UserService 带@Service 注解如下:

package com.zhang.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
}

在包 com. Zhang. Dao 下新建一个 UserDao 带@Repository 注解如下:

package com.zhang.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
}

新建一个配置类如下:

@ComponentScan(value="com.zhang")
@Configuration
public class MainScanConfig {
}

新建测试方法如下:

AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainScanConfig.class);
        String[] definitionNames = applicationContext2.getBeanDefinitionNames();
        for (String name : definitionNames) {
            System.out.println(name);
}

运行结果如下:

mainScanConfig
userController
userDao
userService

怎么样,包扫描的方式比以前介绍的通过@Bean 注解的方式是不是方便很多,这也就是为什么 web 开发的同学经常使用此方式的原因了

2.2 对@ComponentScan 的详细解释

上面只是简单的介绍了@ComponentScan 注解检测包含指定注解的自动装配,接下来让我们来看看@ComponentScan 注解的更加详细的配置,在演示详细的配置之前,让我们先看看@ComponentScan 的源代码如下:

@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 ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
    /**
     * 是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的
     * @return
     */
    boolean useDefaultFilters() default true;
    /**
     * 指定某些定义Filter满足条件的组件 FilterType有5种类型如:
     *                                  ANNOTATION, 注解类型 默认
                                        ASSIGNABLE_TYPE,指定固定类
                                        ASPECTJ, ASPECTJ类型
                                        REGEX,正则表达式
                                        CUSTOM,自定义类型
     * @return
     */
    Filter[] includeFilters() default {};
    /**
     * 排除某些过来器扫描到的类
     * @return
     */
    Filter[] excludeFilters() default {};
    /**
     * 扫描到的类是都开启懒加载 ,默认是不开启的
     * @return
     */
    boolean lazyInit() default false;
}

2.3 案例

2.3.1 basePackageClasses

演示 basePackageClasses 参数,如我们把配置文件改成如下

@ComponentScan(value="com.zhang.dao",useDefaultFilters=true,basePackageClasses=UserService.class)
@Configuration
public class MainScanConfig {
}

测试结果如下:

mainScanConfig
userDao
userService

只有 userDao 外加basePackageClasses指定的 userService 加入到了 spring 容器中

2.3.2 includeFilters

在 com. Zhang. Service 包下新建一个 UserService 2 类如下:注意没有带@Service 注解

package com.zhang.service;
public class UserService2 {
}

配置类改成:

@ComponentScan(value="com.zhang",useDefaultFilters=true,
    includeFilters={
        @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
        @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={UserService2.class})
    })
@Configuration
public class MainScanConfig {
}

运行结果如下:

mainScanConfig
userController
userDao
userService
userService2

UserService 2 同样被加入到了 spring 容器
新增一个自定义的实现了 TypeFilter 的 MyTypeFilter 类如下:

public class MyTypeFilter implements TypeFilter {
    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);
        // 检测名字包含Service的bean
        if(className.contains("Service")){
            return true;
        }
        return false;
    }
}

修改主配置如下:

@ComponentScan(value="com.zhang",useDefaultFilters=true,
    includeFilters={
        @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
        @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
    })
@Configuration
public class MainScanConfig {
}

运行结果如下:

mainScanConfig
userController
userDao
userService
userService2

可以发现同样 userService 2 被加入到了 spring 容器中

3 XML 中配置 component-scan

情况一:最基本的扫描方式

 <context:component-scan base-package="com.atguigu.spring6">
 </context:component-scan>

情况二:指定要排除的组件

 <context:component-scan base-package="com.atguigu.spring6">
     <!-- context:exclude-filter标签:指定排除规则 -->
     <!--
         type:设置排除或包含的依据
         type="annotation",根据注解排除,expression中设置要排除的注解的全类名
         type="assignable",根据类型排除,expression中设置要排除的类型的全类名
     -->
     <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
         <!--<context:exclude-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
 </context:component-scan>

情况三:仅扫描指定组件

 <context:component-scan base-package="com.atguigu" use-default-filters="false">
     <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
     <!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
     <!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
     <!--
         type:设置排除或包含的依据
         type="annotation",根据注解排除,expression中设置要排除的注解的全类名
         type="assignable",根据类型排除,expression中设置要排除的类型的全类名
     -->
     <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
     <!--<context:include-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
 </context:component-scan>

4 总结

总结一下@ComponentScan 的常用方式如下

  • 自定扫描路径下边带有@Controller,@Service,@Repository,@Component 注解加入 spring 容器
  • 通过 includeFilters 加入扫描路径下没有以上注解的类加入 spring 容器
  • 通过 excludeFilters 过滤出不用加入 spring 容器的类
  • 自定义增加了@Component 注解的注解方式