zoukankan      html  css  js  c++  java
  • @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 Type Examples Expression Description include-filter为例
    annotation org.example.SomeAnnotation 符合SomeAnnoation的target class

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

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

    assignable org.example.SomeClass 指定class或interface的全名

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

    指定扫描StuService类作为bean

    aspectj org.example..*Service+ AspectJ語法  
    regex org.example.Default.* Regelar Expression  
    custom org.example.MyTypeFilter Spring3新增自訂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>
  • 相关阅读:
    git提交本地代码到远程服务器
    报错 D:Program Files odejs ode_cache\_logs2019-05-07T07_07_30_992Z-debug.log
    vue项目中使用插件将字符串装化为格式化的json数据(可伸缩)
    odoo官方文档第二章 Data Files
    odoo官方文档第一章 ORM
    odoo模块的创建 openacademy学习笔记
    mysql存储过程的学习(二)
    mysql存储过程的学习(一)
    linux 进入mysql的常用命令(转)
    Dubbo入门学习(转)
  • 原文地址:https://www.cnblogs.com/jing99/p/11043221.html
Copyright © 2011-2022 走看看