序
上篇博客中,介绍了对于自定义标签的解析,以及自己实现了一个标签供spring扫描,此篇博客会主要分析下component-scan 标签的解析过程中做了哪些事情.
解析前的准备
首先在xml文件的beans标签中引入context标签
<context:component-scan base-package="com"></context:component-scan>
在xml文件中加入上面的代码 base-package指的是扫描的包名
当spring启动后,解析到该段时,是会进入到自定义标签的解析的.
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
}
context所对应的handler会注册上述的八个标签,其中当使用的是component-scan时,ComponentScanBeanDefinitionParser会负责去解析该标签
ComponentScanBeanDefinitionParser的 parse(Element element, ParserContext parserContext)方法
public BeanDefinition parse(Element element, ParserContext parserContext) {
//BASE_PACKAGE_ATTRIBUTE就是base-package,获取base-package的值
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
//这里貌似是对路径进行解析,可以支持"${}"这种写法,从Enviroment中获取resources注入进去
// <context:component-scan base-package="${LOCALAPPDATA}"></context:component-scan>这种配置下
//${LOCALAPPDATA}被正常解析了,主要还是要Enviroment中得propertySources拥有,应该都可以在此注入
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
//如果是多个包名,会在此被分析出来
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// 主角在此,获取一个 配置好的ClassPathBeanDefinitionScanner 对象
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
//用ClassPathBeanDefinitionScanner对象去执行扫描(核心方法)
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
//这里面将比较常用的内置的后置处理器注册到了BeanDenifitions里面
//ConfigurationClassPostProcessor.class
//AutowiredAnnotationBeanPostProcessor.class
//CommonAnnotationBeanPostProcessor.class等.这些后置出理解中ConfigurationClassPostProcessor.class
//非常重要,后续会详细分析
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
扫描器的创建
ComponentScanBeanDefinitionParser的configureScanner(parserContext, element)方法
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
//使用默认的过滤注解
boolean useDefaultFilters = true;
//在这里解析use-default-filters标签,如果xml中设置了,则会按照设置的值去设置useDefaultFilters
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
useDefaultFilters = Boolean.parseBoolean(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
}
//在此创建一个ClassPathBeanDefinitionScanner,创建的过程后续会解析
ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
//后面是对scanner的进行一些配置,主要是根据xml文件的配置去配置的
......
return scanner;
}
ComponentScanBeanDefinitionParserconfigure的 createScanner(XmlReaderContext readerContext, boolean useDefaultFilters)方法
protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
//本质也只是new一个对象而已
return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,
readerContext.getEnvironment(), readerContext.getResourceLoader());
}
ClassPathBeanDefinitionScanner的构造方法
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
//是否使用默认的过滤注解
if (useDefaultFilters) {
//注册默认的注解
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
ClassPathBeanDefinitionScanner的registerDefaultFilters()方法
protected void registerDefaultFilters() {
//在此处添加Component.class注解为会被扫描到的注解,@Configuration,@Service等注解都是该注解的子类也都会
//被扫描出来,this.includeFilters在ClassPathScanningCandidateComponentProvider的
//isCandidateComponent(MetadataReader metadataReader)方法中会被使用到
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
....
}
扫描器的扫描
ClassPathScanningCandidateComponentProvider类是ClassPathBeanDefinitionScanner的父类
ClassPathBeanDefinitionScanner的doScan(String… basePackages)
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
//对包进行遍历
for (String basePackage : basePackages) {
//递归扫描包得出候选的BeanDefinition(带有Component.class注解或者它的子注解都会被扫描到)
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
//对被扫描出来的BeanDenifiton进行信息的补全
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//将符合条件的BeanDefinition注册到IOC容器中(此时还只是BeanDenifiton)
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
ClassPathScanningCandidateComponentProvider的scanCandidateComponents(String basePackage)方法
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//补全扫描规则前面加classpath*:,将"."替换为"/"后面加/**/*.class,
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//递归操作解析路径名下所有文件并转化为Resource
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
//如果可读
if (resource.isReadable()) {
try {
//将Resource对象转化为metadataReader对象,便于使用
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//此方法判断是否有@component注解或其字注解
if (isCandidateComponent(metadataReader)) {
//自己new一个被扫描的BeanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
//加入候选的BeanDefinition中
candidates.add(sbd);
}
......
return candidates;
}
ClassPathScanningCandidateComponentProvider的isCandidateComponent(MetadataReader metadataReader)方法
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
//如果定义了排斥的注解,则返回false,即不加入候选的BeanDenitions
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
//this.includeFilters就是初始化时注册了@Component注解的类,包含了该注解会返回true,即会被扫描到
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
至此,component-scan标签的解析如此便完成了
扫描器的运用
稍稍改造一下上一篇博客的ZkParser类
public class ZkParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
//自己实例化一个扫描器,不使用默认的检索注解
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(
parserContext.getRegistry(),false
);
//将自己定义的注解添加进去
scanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class));
scanner.scan("com.zk.fiveDay");
System.out.println("zk标签被解析了");
return null;
}
}
实验对象代码
@MyAnnotation
public class Phone {
private String color;
}
启动类
public void a() throws InterruptedException {
new AnnotationConfigApplicationContext();
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("spring.xml");
System.out.println(applicationContext.getBean(Phone.class));}
最后的结果是
成功!!!
结
其实,component-scan标签的解析,就是实例化了一个ClassPathBeanDefinitionScanner的扫描器去做扫描.然后将扫描出来的class文件进行筛选,筛选方法是判断class文件中是否拥有@Component或其子类注解,而这个筛选条件是在实例化的时候注册进去的.