Spring-ClassPathBeanDefinitionScanner自动扫描

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

      

12161652_3NHO.jpg

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));
    }
    ...
}

     

 

 

 

 

转载于:https://my.oschina.net/ysma1987/blog/710415

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值