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