基于@ComponentScan注解及其XML配置方式

 更新时间:2023年09月22日 15:07:24   作者:kosamino  
这篇文章主要介绍了基于@ComponentScan注解及其XML配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

@ComponentScan注解及其XML配置

开发中会经常使用包扫描,只要标注了@Controller、@Service、@Repository,@Component 注解的类会自动加入到容器中,ComponentScan有注解和xml配置两种方式。

注解

@ComponentScan 包含过滤和排除过滤

  • ComponentScan.Filter[] includeFilters() default {}; 按照某些规则排除组件
  • ComponentScan.Filter[] excludeFilters() default {}; 指定扫描的时候只需要包含哪些组件
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    @AliasFor("basePackages")
    String[] value() default {};
    @AliasFor("value")
    String[] basePackages() default {};
    Class<?>[] basePackageClasses() default {};
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
    String resourcePattern() default "**/*.class";
    boolean useDefaultFilters() default true;
    ComponentScan.Filter[] includeFilters() default {};
    ComponentScan.Filter[] excludeFilters() default {};
    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 {};
    }
}

FilterType 指定不同的包含/排除规则:

package org.springframework.context.annotation;
public enum FilterType {
    ANNOTATION,
    ASSIGNABLE_TYPE,
    ASPECTJ,
    REGEX,
    CUSTOM;
    private FilterType() {
    }
}

1、Spring使用注解 包扫描 @ComponentScan

@ComponentScan("com.spring.annotation")
@Configuration
public class MainConfig {
}

注意:mainConfig 配置类也是一个组件 因为@Configuration 注解中标有@Component。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}

2、按照注解类型排除

例如排除以下类型:Controller.class, Service.class, Repository.class:

@ComponentScan(value = "com.spring.annotation", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class, Repository.class})
})
@Configuration
public class MainConfig {
}

3、包含过滤includeFilters

如果想要只包含 Controller 注解的bean,如下配置:

@ComponentScan(value = "com.spring.annotation", includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,
                classes = {Controller.class})},useDefaultFilters = false)
@Configuration
public class MainConfig {
}

注意:需要添加 useDefaultFilters = false。

4、使用@ComponentScans 来指定扫描策略

ComponentScans 注解结构如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ComponentScans {
    ComponentScan[] value();
}

可以看到其内部是一个ComponentScan[] 数组,所以我们可以在其中指定多个ComponentScan。

例如:

指定不同的类型,包含Controller 注解的bean 和 BookService类型的bean:

@ComponentScans(value = {
        @ComponentScan(value = "com.spring.annotation", includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
        },useDefaultFilters = false)
})
@Configuration
public class MainConfig {
}

5、FilterType.CUSTOM:使用自定义规则

1>. 编写MyTypeFilter 并实现 TypeFilter 接口;

2>. match方法中 实现自定义规则。

/**
 * 自定义过滤规则
 */
public class MyTypeFilter implements TypeFilter {
    /**
     *
     * @param metadataReader
     * @param metadataReaderFactory
     * @return
     * @throws IOException
     * metadataReader:读取到的当前正在扫描的类的信息
     * metadataReaderFactory:可以获取到其他任何类信息的
     *
     *
     */
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        // TODO Auto-generated method stub
        //获取当前类注解的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前类资源(类的路径)
        Resource resource = metadataReader.getResource();
        String className = classMetadata.getClassName();
        System.out.println("--->"+className);
        if(className.contains("er")){
            return true;
        }
        return false;
    }
}

3>. 使用实例(当前扫描到的类,类名中包含er,就会注入到容器中):

@ComponentScans(value = {
        @ComponentScan(value = "com.spring.annotation", includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class}),
                @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
        },useDefaultFilters = false)
})
@Configuration
public class MainConfig {
}

注解小结:

  • @ComponentScan value:指定要扫描的包
  • excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
  • includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
  • FilterType.ANNOTATION:按照注解
  • FilterType.ASSIGNABLE_TYPE:按照给定的类型;
  • FilterType.ASPECTJ:使用ASPECTJ表达式
  • FilterType.REGEX:使用正则指定
  • FilterType.CUSTOM:使用自定义规则

XML配置

我们使用component-scan来进行bean的加载,例如,我们通常会使用如下的配置:

application.xml:

<context:component-scan base-package="com.cn.kvn.service,com.cn.kvn.config,com.baidu">
        <context:include-filter type="annotation" expression="com.alibaba.dubbo.config.annotation.Service" />
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

spring-servlet.xml:

<context:component-scan base-package="com.cn.kvn.controller" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

原理: componentScan解析bean的入口为:ComponentScanBeanDefinitionParser#parse(Element element, ParserContext parserContext)

@Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
        basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
        String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        // Actually scan for bean definitions and register them.
        ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
        Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
        registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
        return null;
    }

base-package属性告诉spring要扫描的包,use-default-filters="false"表示不要使用默认的过滤器。

configureScanner会去配置scan时的使用的filter,其中use-default-filters属性是来控制是否要使用默认的过滤器的(默认的过滤器会去解析base-package下的含有@Component注解的类作为bean,其中@Repository, @Service, and @Controller都是@Component的子注解,故,默认的过滤器会将它们全部解析成bean)。

原码中的英文注释:

ClassPathScanningCandidateComponentProvider#ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment)

useDefaultFilters whether to register the default filters for the @Component, @Repository, @Service, and @Controller stereotype annotations

用户自定义的include-filter和exclude-filter会在以下方法中被解析加载。

ComponentScanBeanDefinitionParser#parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext)

在执行Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);的时候,使用过滤器的顺序是,exclude-filter优于include-filter

也就是说,如果同时定义了exclude-filter排除了某类(某个)bean,但是include-filter又将其包含了,则该bean不会被加载到spring容器。

ClassPathScanningCandidateComponentProvider.java
/**
     * Determine whether the given class does not match any exclude filter
     * and does match at least one include filter.
     * @param metadataReader the ASM ClassReader for the class
     * @return whether the class qualifies as a candidate component
     */
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        for (TypeFilter tf : this.excludeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return false;
            }
        }
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return isConditionMatch(metadataReader);
            }
        }
        return false;
    }

附:过滤规则设置

filter标签的type和表达式说明如下:

Filter TypeExamples ExpressionDescriptioninclude-filter为例
annotationorg.example.SomeAnnotation符合SomeAnnoation的target class

<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>

表示扫描base-package下的类上加了Aspect注解的类,并注册到spring的bean容器

assignableorg.example.SomeClass指定class或interface的全名

<context:include-filter type="assignable" expression="com.test.scan.StuService"/>

指定扫描StuService类作为bean

aspectjorg.example..*Service+AspectJ語法
regexorg\.example\.Default.*Regelar Expression
customorg.example.MyTypeFilterSpring3新增自訂Type,實作org.springframework.core.type.TypeFilter

注意:如果通过regex将filter的type设置成了正则表达式,注意在正则里面.表示所有字符,而\.才表示真正的.字符。例如我们的正则表示以Dao或者Service结束的类。

我们也可以使用annotaion来限定,如下:

<context:component-scan base-package="cn.outofmemory.spring" use-default-filters="false">
  <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/> 
  <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> 
</context:component-scan>

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • JavaSwing后台播放音乐mp3

    JavaSwing后台播放音乐mp3

    这篇文章主要为大家详细介绍了JavaSwing后台播放音乐mp3,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • Java8中Optional的一些常见错误用法总结

    Java8中Optional的一些常见错误用法总结

    我们知道 Java 8 增加了一些很有用的 API, 其中一个就是 Optional,下面这篇文章主要给大家介绍了关于Java8中Optional的一些常见错误用法的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2018-07-07
  • 详解java动态代理模式

    详解java动态代理模式

    这篇文章主要为大家详细介绍了java动态代理模式,总结一下代理模式,以及jdk,cglib代理模式用法,来理解代理模式,感兴趣的小伙伴们可以参考一下
    2016-02-02
  • 企业级Kubernetes管理平台Wayne功能特性介绍

    企业级Kubernetes管理平台Wayne功能特性介绍

    这篇文章主要为大家介绍了企业级Kubernetes管理平台Wayne的功能特性及架构设计,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-02-02
  • 使用@RequestBody配合@Valid校验入参参数

    使用@RequestBody配合@Valid校验入参参数

    这篇文章主要介绍了使用@RequestBody配合@Valid校验入参参数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • J2SE与c#的几点比较

    J2SE与c#的几点比较

    这篇文章主要介绍了J2SE与c#的几点比较,是看完马士兵老师的J2SE视频教程有感而写,需要的朋友可以参考下
    2014-08-08
  • Java中使用BeanMap将对象转为Map详解

    Java中使用BeanMap将对象转为Map详解

    这篇文章主要介绍了Java中使用BeanMap将对象转为Map详解,BeanMap 是 Apache Commons BeanUtils 库中的一个类,BeanMap 可以将 Java 对象的属性作为键,属性值作为对应的值,存储在一个 Map 中,它提供了一种将 Java 对象转换为 Map 的方式,需要的朋友可以参考下
    2024-01-01
  • SpringBoot部署到Linux读取resources下的文件及遇到的坑

    SpringBoot部署到Linux读取resources下的文件及遇到的坑

    本文主要给大家介绍SpringBoot部署到Linux读取resources下的文件,在平时业务开发过程中,很多朋友在获取到文件内容乱码或者文件读取不到的问题,今天给大家分享小编遇到的坑及处理方案,感兴趣的朋友跟随小编一起看看吧
    2021-06-06
  • Java实现简易画图板

    Java实现简易画图板

    这篇文章主要为大家详细介绍了Java实现简易画图板,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • 关于Java内存访问重排序的研究

    关于Java内存访问重排序的研究

    文章主要介绍了重排序现象及其在多线程编程中的影响,包括内存可见性问题和Java内存模型中对重排序的规则
    2025-01-01

最新评论