开发中会经常使用包扫描,只要标注了@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>

@ComponentScan注解及其XML配置的更多相关文章

  1. [spring]Bean注入——使用注解代替xml配置

    使用注解编程,主要是为了替代xml文件,使开发更加快速. 一.使用注解前提: <?xml version="1.0" encoding="UTF-8"?& ...

  2. Spring使用AspectJ注解和XML配置实现AOP

    本文演示的是Spring中使用AspectJ注解和XML配置两种方式实现AOP 下面是使用AspectJ注解实现AOP的Java Project首先是位于classpath下的applicationC ...

  3. mybatis使用注解替代xml配置,动态生成Sql

    mybatis使用注解替代xml配置时,遇到判断条件是否为null或者为空时,@Select很难搞定,不知道怎么办? mybatis3中增加了使用注解来配置Mapper的新特性,使用 SelectPr ...

  4. Spring基础篇——通过Java注解和XML配置装配bean

    自动化装配的确有很大的便利性,但是却并不能适用在所有的应用场景,比如需要装配的组件类不是由自己的应用程序维护,而是引用了第三方的类库,这个时候自动装配便无法实现,Spring对此也提供了相应的解决方案 ...

  5. AspectJ用注解替换xml配置

    AspectJ基于注解的使用 AspectJ简介 AspectJ是一个基于Java语言的AOP框架,一般 其主要用途:自定义开发 一般情况下spring自动生成代理,要配置aop, 首先确定目标类,a ...

  6. Spring_Task初探(注解,XML配置)

    这几天想写一个动态添加任务项目找了找Spring下的自带定时功能发现还真有,然后网上找了找资料写了个demo 写了两种方式来执行定时的任务(XML配置和注解) 先建两个普通的任务类(XML配置调用的任 ...

  7. Spring基础篇——通过Java注解和XML配置装配bean(转载)

      作者:陈本布衣 出处:http://www.cnblogs.com/chenbenbuyi 本文版权归作者和博客园共有,欢迎转载分享,但必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留 ...

  8. 注解 和 xml 配置的优缺点【转】

    java annotation(注解) 的优点缺点 Annotation和xml各自作为配置项的优点与缺点. Annotation 一.Annotation 的优点 1.保存在 class 文件中,降 ...

  9. spring框架学习(二)使用注解代替xml配置

    注解 1.使用注解配置spring 1)开启使用注解代理配置文件 <?xml version="1.0" encoding="UTF-8"?> &l ...

随机推荐

  1. Windows核心编程随笔

    最近在学习Windows底层原理,准备写个系列文章分享给大家,Michael Li(微软实习期间的Mentor,为人超好)在知乎回答过一些关于学习Windows原理的书籍推荐,大家可以拜读其中一本来入 ...

  2. 第一个go程序

    进入到工作空间中(我的是$HOME/go, 所以使用cd $HOME/go命令直接进入) 然后创建一个目录 src/hello( mkdir src/hello), 然后进入到该目录中(cd src/ ...

  3. segi日期计算笔记

    计算月末最后一天 /* * 输入年月格式YM(如:201911),输入该月最后一天 */ int monthEndDay(int YM) { int endDay; ; ; == M) { == Y ...

  4. Java基础篇---多线程

    内容导航: 1.多线程的实现方式 2.线程安全问题 3.线程间通信 4.生产者消费者模式 第一部分多线程的实现方式 在java中多线程实现方式有2种 一.自定义一个类A,继承Thread类 publi ...

  5. [Python3 填坑] 004 关于八进制

    目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 问题的由来 2.2 问题的解决 2.2.1 先说结论 2.2.2 八进制的用途 2.2.3 少废话,上例子 1. print( 坑的信息 ...

  6. MVCC原理 4步 什么是MVCC、事务ACID、事物隔离级别、Innodb存储引擎是如何实现MVCC的

    MVCC是来处理并发的问题,提高并发的访问效率,读不阻塞写.事物A 原子性C 一致性I 隔离性D 持久性高并发的场景下的问题脏读不可重复读幻读事物隔离级别RU读未提交 脏读/不可重复读/幻读 .不适用 ...

  7. Kubernetes---Service(SVC)服务

    ⒈介绍 kubernetes 通过标签选择的方式来匹配一组pod,然后提供对外访问的一种机制 一组pod可以对应到多个svc的 每一个service(svc)都可以理解为一个微服务 Service有且 ...

  8. python 之 Urllib库的基本使用

    目录 python 之 Urllib库的基本使用 官方文档 什么是Urllib urlopen url参数的使用 data参数的使用 timeout参数的使用 响应 响应类型.状态码.响应头 requ ...

  9. spring-cloud 学习三 服务提供者

    基于spring-boot创建一个module提供服务 使用mysql数据库,dao使用mybatis,数据库连接池使用阿里的druid 添加maven依赖 <parent> <gr ...

  10. java7:核心技术与最佳实践读书笔记——类加载

    流程:class -> 加载 ->  jvm虚拟机 -> 链接 . 一.类加载器概述 1.引出      类加载器也是一个java类,java.lang.ClassLoader类是所 ...