文档章节

Spring-ClassPathBeanDefinitionScanner自动扫描

重城重楼
 重城重楼
发布于 2016/07/12 15:59
字数 956
阅读 539
收藏 0

1、ClassPathBeanDefinitionScanner会扫描base-package下的所有spring定义的注解标识类,也可以对扫描的机制进行配置,设置一些Filter,只有满足Filter的类才能被注册为Bean:

有两种方式使用Classpath扫描机制:
第一种是直接使用AnnotationConfigApplicationContext,该类有一个 scan(String basePackages)方法,可以对指定的包进行扫描;
第二种是在Bean配置文件中使用如下配置开启classpath扫描功能:<context:component-scan base-package="com.ronry.springtest.simple" />
(注意需要配置context命名空间)。开启扫描功能之后即可按普通的方式从配置文件创建一个ApplicationContext。
在ApplicationContext的loadBeanDefinition时遇到component-scan元素时会以ComponentScanBeanDefinitionParser进行解析

不过 AnnotationConfigApplicationContext和ComponentScanBeanDefinitionParser,
内部依赖的都是ClassPathBeanDefinitionScanner

      

ResourcePatternResolver用来从classpath中加载Resource;
MetadataReaderFactory用来根据Resource生成MetadataReader;
BeanNameGenerator用来生成扫描到的Bean在容器中的名字;
ScopeMetadataResolver则用来处理扫描到的Bean的Scope。
其中需要特别注意的是BeanNameGenerator和ScopeMetadataResolver是可配置的,
在ComponentScanBeanDefinitionParser中有两个方法:parseBeanNameGenerator(element, scanner)和parseScope(element, scanner)就是用来处理这两个组件的配置的。
如果没有配置,则BeanNameGenerator默认是用AnnotationBeanNameGenerator而ScopeMetadataResolver用的是AnnotationScopeMetadataResolver。

说了这么多
doScan方法的扩展(重写)才是能达到我们自定义扫描的目的

 

2、扩展doScan方法

      首先看现有方法流程,理解自动扫描的原理

1、入口,【发现registry的必要性】
/**
 * Perform a scan within the specified base packages.
 * @param basePackages the packages to check for annotated classes
 * @return number of beans registered
 */
public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    doScan(basePackages);
    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
	AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }
    return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}
2、doScan的深入挖掘 【获取到Set<BeanDefinitionHolder>】
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
    for (String basePackage : basePackages) {
	Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            ...
            if (checkCandidate(beanName, candidate)) {
                ...
                beanDefinitions.add(definitionHolder);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
}
3、findCandidateComponents的深入挖掘
/**
 * Scan the class path for candidate components.
 * @param basePackage the package to check for annotated classes
 * @return a corresponding Set of autodetected bean definitions
 */
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    ...
    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
	if (isCandidateComponent(metadataReader)) {
        ...
    }
}
4、isCandidateComponent(...)资格校验
ClassPathScanningCandidateComponentProvider.isCandidateComponent
/**
 * 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)) {
	    AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
	    if (!metadata.isAnnotated(Profile.class.getName())) {
		return true;
	    }
	    AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
	    return this.environment.acceptsProfiles(profile.getStringArray("value"));
        }
    }
    return false;
}

3、扩展的准备

由2可见
要扩展ClassPathBeanDefinitionScanner的doScan方法 或者说要继承ClassPathBeanDefinitionScanner
首先需要注册一个registry那么如何获得一个registry呢?
其次需要实现两个filter,通过isCandidateComponent方法可以发现没有filter的过滤也完成不了scan
那么如何配置provider的filter呢?
1、获取registry很简单,从classpathBeanDefinition可以发现registry注入的BeanDefinitionRegistry
   那么可以采取实现的方式:
   class myRegistry implements BeanDefinitionRegistry {
       @Override
       public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
           //myClassPathInterfaceScanner extends ClassPathBeanDefinitionScanner
       }
   }

2、因为ClassPathBeanDefinitionScanner继承了ClassPathScanningCandidateComponentProvider
   所以可以直接复写addIncludeFilter(TypeFilter includeFilter)和
                addExcludeFilter(TypeFilter excludeFilter)方法
  
   具体实现那种TypeFilter可依据自己的实际需要变通处理
   //AssignableTypeFilter和AnnotationTypeFilter即可

    

4、如何结合FactoryBean

由2可以看到 doScan中代理类candidate均为BeanDefinition类型
问题是还返回了Set<BeanDefinitionHolder>
那么我们就可以统一为candidate配置代理类了:definition.setBeanClass(MyFactoryBean.class);

最后别忘记了设置propertyValues 【(如果需要的话) 我想工厂生成对象的时候应该需要一些枚举值】
比如:
public class MyFactoryBean<T> implements FactoryBean<T> {
    private Class<T> myInterface;

    @Override
    public T getObject() throws Exception {
        return (T) Proxy.newProxyInstance(MyFactoryBean.class.getClassLoader(), 
               new Class[] { this.myInterface},
               new MyInvocationHandler(myInterface));
    }
    ...
}

     

 

 

 

 

© 著作权归作者所有

共有 人打赏支持
上一篇: junit压测
重城重楼
粉丝 3
博文 48
码字总数 15108
作品 0
南京
程序员
私信 提问
Spring对注解(Annotation)处理源码分析1——扫描和读取Bean定义

1.从Spring2.0以后的版本中,Spring也引入了基于注解(Annotation)方式的配置,注解(Annotation)是JDK1.5中引入的一个新特性,用于简化Bean的配置,某些场合可以取代XML配置文件。开发人员对注...

李长春
2011/10/08
0
0
Spring MVC 解读——

Spring MVC 解读---context:component-scan/ 注解是骑士魂牵梦绕的美丽公主,也是骑士的无法摆脱的噩梦... 一、context:component-scan/ 想必@Component,@Repository,@Service,@Controll...

麦壳原野
2014/02/26
0
6
Spring3 整合MyBatis3 配置多数据源 动态选择SqlSessionFactory

一、摘要 上两篇文章分别介绍了Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法 和 Spring3 整合Hibernate3.5 动态切换SessionFactory (切换数据库方言),这篇文...

如月王子
2015/03/25
0
0
Arthas实践--获取到Spring Context,然后为所欲为

背景 Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。 https://github.com/alibaba/arthas Arthas提供了非常丰富的关于调用拦截的命令,比如 trace/watch/monitor/tt 。但是很多时候我...

阿里云云栖社区
01/31
0
0
Spring_总结_03_装配Bean之自动装配

一、前言 本文承接上一节:Spring总结02_依赖注入 在上一节我们了解到依赖注入的实质就是装配。 这一节我们来学习下装配Bean的相关知识。 二、Bean的装配机制 1.三种装配机制 Spring提供了三...

shirayner
2018/07/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

nacos之springboot

本地操作系统:ubuntu18,我使用docker的方式启动nacos服务 docker image方式启动nacos 在docker hub上可以搜到nacos-server List-1.1 拉去最新的版本 mjduan@mjduan-ubuntu:/opt/software$ ...

克虏伯
30分钟前
2
0
指针数组和数组指针的区别

这两个名字不同当然所代表的意思也就不同。我刚开始看到这就吓到了,主要是中文太博大精深了,整这样的简称太专业了,把人都绕晕了。从英文解释或中文全称看就比较容易理解。 指针数组:arr...

天王盖地虎626
今天
3
0
Qt那些事0.0.18

今天要记一下Qt中的Resource。自我感觉理解的不错,但是还会难免有谬误,在日后有可能会更新,也有可能不会。 小声的念叨一句,女人心,海底针。 今天就直接跳过了关于QML在qrc文件中的介绍,...

Ev4n
今天
2
0
深入解析js的作用域、预解析机制

虽然,ES6在我们工作中应用得越来越广泛,但是还是很多项目保留着ES5的写法,所以,今天,带着大家重新巩固下ES5下的作用域及预解析机制。 概念: 作用域:域,指的是一个空间、范围、区域,...

前端攻城老湿
今天
2
0
Spring Cloud Feign - 声明式 REST Client

1、Feign是什么 声明式REST client,来自NetFlix。 允许你编写无实现代码调用REST services 替换RestTemplate(甚至更简单) Spring Cloud 为使用Feign提供了包装器 2、怎样使用Feign 对比:...

Benz001
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部