spring5.exercise.demo17.spring.xml
描述
上述spring.xml配置中,配置了一个component-scan标签,这个标签的作用是扫描项目代码中com.jd.nlp.dev.muzi.spring5.exercise.demo01下的class类,如果有@Service @Controller @Component等注解的类,将这些类解析成一个个的BeanDefinition,容器初始化时会针对这些类去创建实例对象。
com.jd.nlp.dev.muzi.spring5.exercise.demo17.ProductService
@Service("productService")
public class ProductService {
private String name = "Muzi";
private String well = "开始Spring5的炫酷之旅,(Muzi)书生不读四书五经!";
public void show() {
System.out.println(name + "\n" + well);
}
public ProductService() {
this.name = "无参数构造函数";
}
}
测试容器启动代码
@Test
public void run01(){
// 基于加载XML配置文件的方式,启动spring容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:spring5.exercise.demo17.spring.xml");
ProductService productService = (ProductService) context.getBean("productService");
productService.show();
}
描述
上述测试代码中,创建了基于解析XML的容器,解析spring5.exercise.demo17.spring.xml配置文件,在配置文件中配置了一个component-scan标签去扫描“com.jd.nlp.dev.muzi.spring5.exercise.demo17”包下面的含有Spring @Component下的类ProductService。
我们测试代码中通过容器获取ProductService的实例,调用show方法。
测试结果
09:54:56.037 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'productService'
无参数构造函数
开始Spring5的炫酷之旅,(Muzi)书生不读四书五经!
那么具体component-scan标签是如何解析的呢?和我跟着源码一点一点阅读吧。
component-scan是一个自定义标签
namespace uri是http://www.springframework.org/schema/context
xmlns:context="http://www.springframework.org/schema/context"
所以我们需要去context的jar包去找 spring.handlers文件
spring-context的spring.handlers文件
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
基于上述文件可知,http://www.springframework.org/schema/context这个namespace uri对应的解析类是“org.springframework.context.config.ContextNamespaceHandler”
在ContextNamespaceHandler的init方法中,component-scan这个自定义标签是ComponentScanBeanDefinitionParser这个解析类来进行解析的。
@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());
}
registerBeanDefinitionParser
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
最后实际上registerBeanDefinitionParser是把对应标签的解析类注册到parsers这个Map中了。
DefaultBeanDefinitionDocumentReader -> parseBeanDefinitions
/**
* 获取根节点中所有的子节点
*/
NodeList nl = root.getChildNodes();
/**
* 遍历
*/
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
/**
* 默认标签解析
*/
parseDefaultElement(ele, delegate);
}else {
/**
* 自定义标签解析,委托给delegate解析
*/
delegate.parseCustomElement(ele);
}
}
}
}
代码描述
在这个方法中,遍历所有标签元素,如果是默认的namespace就走默认标签解析,如否则走自定义标签解析。
BeanDefinitionParserDelegate -> parseCustomElement
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
/**
* 1.获取标签元素的NamespaceURI
*/
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
/**
* 2.通过URI来获得对应的NamespaceHandler
*/
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
/**
* 3.使用handler来解析该标签,带入 readerContext 进去。
*/
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
代码描述
在这个方法中,首先还是先获取标签的namespace uri,然后通过namespace uri来寻找对应的namespace handler,我们待解析的component-scan标签对应的handler就是之前找到的ContextNamespaceHandler。
得到ContextNamespaceHandler后就会调用他的parse方法进行解析。
上面执行resolve获取NamespaceHandler的方法,最终调用以下方法
public NamespaceHandler resolve(String namespaceUri) {
//加载"META-INF/spring.handlers"文件,建立URI和处理类的映射关系
Map handlerMappings = getHandlerMappings();
// ... ... 省略
// 处理类(字符串)反射
String className = (String) handlerOrClassName;
/**
* 反射这个类
*/
Class> handlerClass = ClassUtils.forName(className, this.classLoader);
// ... ... 省略
// 反射创建handler
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
// 调用处理类初始化方法
namespaceHandler.init();
// 替换映射关系key对应的值
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
// ... ... 省略
代码描述
NamespaceHandlerSuppoort -> parse
public BeanDefinition parse(Element element, ParserContext parserContext) {
/**
* 获取这个自定义标签元素的解析器
*/
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
NamespaceHandlerSuppoort描述
自定义的NamespaceHandelr都会继承官方提供的默认的NamespaceHandlerSuppoort,内部实现好了很多东西,不需要开发者去二次实现。开发者只需要专注于对应标签解析类的开发。
这个方法首先会获取当前待解析标签对应的解析器对象,然后调用对应解析器的解析方法进行解析。
NamespaceHandlerSuppoort -> findParserForElement获取解析器
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
/**
* 获取解析的parser,parsers这个map是在获取Handler时调用init的时候把parser注册进Map的。
*/
BeanDefinitionParser parser = this.parsers.get(localName);
// ... ...
return parser;
}
上述回顾SPI最后提到过,所有的解析器在init的时候会注册到parsers这个Map中,所以当前拿到的是 component-scan 标签对应的 ComponentScanBeanDefinitionParser 类的实例。
ComponentScanBeanDefinitionParser -> parse
public BeanDefinition parse(Element element, ParserContext parserContext) {
/**
* 扫描注解的解析方法
*
* base-package 包名路径获取
*/
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
// base-package 可以用,分隔,多个包。
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
/**
* 拿到注解扫描器
*/
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
/**
* 扫描包下的类
* 并把这些类的信息封装成BeanDefinitionHolder(BeanDefinition)对象集合
* 重要程度:* * * * *
*/
Set beanDefinitions = scanner.doScan(basePackages);
/**
* 注册 @Resource @Autowire @Value 这些注解组件的功能。
*
* 重要程度:* * * * *
*
* @Resource @Autowire @Value
* 是由N个BeanPostProcessor接口的实现类来实现的
* BeanPostProcessor接口是我们用到的最重要的接口
* 类接口实现类是通过注册组件注册进去的
*
* BeanPostProcessor 是Spring中至关重要的接口,大部分功能都是依赖他完成的。
*/
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
代码描述
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
// 使用默认过滤器
boolean useDefaultFilters = true;
/**
* @Service @Component @Controller 默认注解
*/
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
}
/**
* 创建注解扫描器
*/
ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
// ... ... 省略
/*
* 扫描 哪些注解
* 不扫描 哪些注解
*/
parseTypeFilters(element, scanner, parserContext);
return scanner;
}
代码描述
注解扫描器一般使用的是默认注解扫描器
createScanner中会调用到registerDefaultFilters
/**
* 过滤器中添加需要扫描的注解类型
* 为什么只丢Component这个注解 , @Service 和 @Controller都是Component这个注解的类型
*/
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
默认注解扫描器会扫描@Componenet注解的类,而@Service 和 @Controller都继承@Componenet注解所以会一并去进行扫描,注解会加入到includeFilters这个集合中。
ClassPathBeanDefinitionScanner -> doScan
protected Set doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
/**
* 扫描到有注解的类并封装成BeanDefinition
*
* 递归方式找被注解的类,封装成BeanDefinition
*
* 重要程度:* * * * *
*/
Set candidates = findCandidateComponents(basePackage);
/**
* 扫描有注解的BeanDefinition对象,并设置BeanDefinition的基础属性值。
*/
for (BeanDefinition candidate : candidates) {
/**
* 单例还是原型 属性设置
*/
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
/**
* autowireCandidate 是否可以被自动注入属性设置
*/
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
/**
* 类上面有哪些注解set哪些值
* Role
* Lazy
* Primary
* DependsOn
* Description
*/
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
/**
* beanDefinition beanName 的映射
*/
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
/**
* 注册BeanDefinition
*/
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
代码描述
逻辑都还比较简单,至此其实自定义扫描一个包下的类就完事了。
递归找文件
扫描class文件并给出初步的BeanDefinition内容的代码。
ClassPathScanningCandidateComponentProvider -> scanCandidateComponents
private Set scanCandidateComponents(String basePackage) {
Set candidates = new LinkedHashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
/**
* 递归找类文件
* PathMatchingResourcePatternResolver
* 重要程度:* * * *
*/
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 {
/**
* 包装了类的基本信息的对象(和类相关)
* 如果类上面有 includeFilters 集合装载的注解
*/
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
/**
* 如果类元数据信息中有includeFilter集合中的注解,就去实例化
*/
if (isCandidateComponent(metadataReader)) {
/**
* 把这个类包装成一个GenericBeanDefinition对象
*
* ScannedGenericBeanDefinition 继承了 GenericBeanDefinition
*
* 把 metadataReader 元数据信息集合丢给了 ScannedGenericBeanDefinition 对象
*/
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
// ... ... 省略
}
/**
* 返回 beanDefinition 的集合
*/
return candidates;
}
BeanPostProcessor 是Spring中至关重要的接口,bean的实例化大部分功能都是依赖他完成的。
ComponentScanBeanDefinitionParser -> registerComponents
protected void registerComponents(
XmlReaderContext readerContext, Set beanDefinitions, Element element) {
// ... ... 省略
if (annotationConfig) {
/**
* 生成组件BeanDefinition并注册
* AutowireAnnotationBeanPostProcessor,ConfigurationClassPostProcessor,CommonAnnotationBeanPostProcessor
* AutowireAnnotationBeanPostProcessor:是Autowire的支撑,是DI的核心处理。
*
* 组件注册
* registerAnnotationConfigProcessors
*/
Set processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
}
}
readerContext.fireComponentRegistered(compositeDef);
}
实际的组册组件的代码。
AnnotationConfigUtils -> registerAnnotationConfigProcessors
public static Set registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
// ... ... 省略
Set beanDefs = new LinkedHashSet<>(8);
/**
* 解析 @ComponentScan @Import @Bean @ImportSource
*/
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
/**
* ConfigurationClassPostProcessor 至关重要,解析@ComponentScan @Import @Bean @ImportSource注解的能力。
* @Configuration 注解的组件功能注册
*/
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
/**
* @Autowire 注解的组件功能注册
*/
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
/**
* @PostConstruct @Resource 等注解的组件功能注册
* 包装成BeanDefinition对象,赋值,注册。
*/
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// ... ... 省略
return beanDefs;
}
这里会注册一些我们实例化过程中,或者是扫描BeanDefinition过程中可能会用到的一些组件。
扫描包下的类,有很多种方式,如 @ComponentScan(“xxx.xx.x”) 也是基于刚刚注册的组件去解析的,一样也是获取 Scanner 去 doScan,万变不离其宗,我们理解了 component-scan 标签的解析,再去看@ComponentScan(“xxx.xx.x”)注解的解析内容也会变得很简单。