您的位置:首页 > 编程语言 > Java开发

【Spring源码解析】—— 结合SpringMVC过程理解IOC容器初始化之注解部分探究

2019-05-08 14:14 2091 查看

前面的文章写了xml中直接配置bean进行IOC的过程解析,接下来会针对注解进行IOC容器初始化的过程解析

因为会与之前的内容存在部分重叠,因此会针对相同的部分简略带过,针对不同的部分做重点说明:

 

一、Xml的配置和代码中的注解配置:

applicationContext.xml配置添加:

<context:component-scan base-package="cn.lx.controller" />

 

代码中配置注解修改:

@Controller

public class TestController {

@RequestMapping("/test.form")

public void execute(){

return ;

}

}

二、详解:

入口部分:ContextLoaderListener类中的contextInitialized,进入到ContextLoader类的initWebApplicationContext

方法中,该方法中执行的关键方法是:configureAndRefreshWebApplicationContext()进入到ConfigurableWebApplicationContext

类的实例wac.refresh()调用中,至此进入到具体接下来的load阶段了

load过程:

进入到AbstractApplicationContext类的refresh()类中,之后进入到ObtainFreshBeanFactory()方法中,一路往下跟进到实现方法,之后进入到:AbstractRefreshableApplicationContext类中的refreshBeanFactory()方法,在此方法中,进行CreateFactory()会得到DefaultListableBeanFactory类的一个实例beanFactory,之后会作为方法参数传入到loadBeanDefinitions(beanFactory)中,这里其实就是能明显看到有load字眼啦,继续一步步往下跟进,进入到真正做事情的方法就是doLoadBeanDefinitions中,这里会生成一个BeanDefinitionDocumentReader类的实例,之后通过该实例调用方法registerBeanDefinitions,依然是要进入到真正做事的doRegisterBeanDefinitions方法中,至此就马上到了process的部分了,在这个部分会针对传入的元素进行解析前、中、后的处理,我们进入到解析中的方法:parseBeanDefinitions(root, this.delegate),在解析的方法中,会判断如果是bean相关namespace的,则会parseDefaultElement,因为这里是注解的形式,因此其nameSpace不为默认的bean相关的,而是Context的,因此进入到:delegate.parseCustomElement(ele)中,接下来就是具体基于注解进行解析的部分了,即process过程

具体可见下方代码:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//判断节点是否属于同一命名空间,是则执行后续的解析
if (delegate.isDefaultNamespace(root)) {
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 {
//注解定义的Context的nameSpace进入到这个分支中
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

process过程:

具体process过程做了哪些事情呢?可分为两个步骤来说明,首先根据ele的定义得到key,通过key返回对应的namespaceUri,之后根据namespaceUri的解析得到一个NameSpaceHandler的实例handler,之后由具体实现了NameSpaceHandler接口的类NameSpaceHandlerSupport类进行的方法调用,即parse方法的调用,即离具体的解析更进一步啦~

可见下方代码注释部分:

//常规解析方法
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
//获取namespaceUri,例如xml中配置的如果是<context:componet-scan base-packages:xxx>
//这里的ele会得到component-scan,并且值为null,namespaceUri为 http://www.springframework.org/schema/context
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
//基于namespaceUri得到handler,得到handler之后,不同的handler实现了parse方法,到具体的parse去进行调用和处理
//因为Handler为:org.springframework.context.config.ContextNamespaceHandler
//在resolve的init操作中直接将component-scan的key和对应的ComponentScanBeanDefinitionParser的实例放入到了parser的map中
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
//NameSpaceHandler是接口,具体这里调用的就是得到的handler的实际类型,通过它进行parse调用
//实现该Handler的类是:NamespaceHandlerSupport
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

下面继续说明一下通过namespaceUri的resolve具体是如何实现的的?接下来是对这里的详细说明:

首先this.readerContext.getNamespaceHandlerResolver()返回内容为:

public final NamespaceHandlerResolver getNamespaceHandlerResolver() {
return this.namespaceHandlerResolver;
}

注意这里定义的是final,final的方法不能被重写,因为返回的是NamespaceHandlerResolver,发现其是一个接口,因此直接找实现类,点击resolve找到对应的实现类DefaultNamespaceHandler(注意这里又体现了,最终真的去做事情的,很多都会被命名为Defaultxxx类,或者Simplexxx类)对该方法的实现,resolve中所做事项就是判定是否已有可用解析类,若无则进行初始化init操作,并且返回handler实例

public NamespaceHandler resolve(String namespaceUri) {
//先通过handlerMappings的配置进行get获取,针对传入的namespaceUri是否存在handler可供使用
Map<String, Object> handlerMappings = getHandlerMappings();
//注意:这里的从mapping中得到的key为Object的,因为这里可能为各种不同的具体Handeler,namespaceUri不同,则其value不同
//这里根据namespaceUri为context的值:org.springframework.context.config.ContextNamespaceHandler,发现不为null
Object handlerOrClassName = handlerMappings.get(namespaceUri);
//if判断不为空,跳过此逻辑
if (handlerOrClassName == null) {
return null;
}
//判断不为NamespaceHandler的实例
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
//直接将前面的“org.springframework.context.config.ContextNamespaceHandler”转成String类型的
String className = (String) handlerOrClassName;
try {
//生成类
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
//类的实例化,如果是前一个函数中的<context:Component-scan>这里得到的Handler为:
//org.springframework.context.config.ContextNamespaceHandler
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
//因为上面是ContextNamespaceHandler,将N种不同的key对应的具体的parser进行new之后作为key放到parsers的map中
//这里针对key为"component-scan",直接new的实例就是:ComponentScanBeanDefinitionParser
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
}
}
}

之后通过handler实例进行parse方法调用,实现类为:NamespaceHandlerSupport,其中parse方法如下,就是找到真正的parser,从之前的handler的init操作所put的map中将需要解析的key对应的value即解析类实例取出,然后进行真正的解析操作,在parse中会将bean定义注册代理给scanner类实例,之后通过scanner.doScan()方法调用真正完成bean的解析和注册到容器中

public BeanDefinition parse(Element element, ParserContext parserContext) {
//确定是什么parser,之前已经存储在Handler的parsers的map中
//find就是找到对应element对应的具体key的具体解析类
BeanDefinitionParser parser = findParserForElement(element, parserContext);
//根据具体解析类,直接进行对应的解析调用
return (parser != null ? parser.parse(element, parserContext) : null);
}

就进入到parser.parse()部分,因为对应key为Component-scan对应的解析类为ComponentScanBeanDefinitionParser类,进入到此类的parse方法中:

public BeanDefinition parse(Element element, ParserContext parserContext) {
//根据element为“base=package”得到基础包路径
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
//解析basePacakge的String值,将占位符前后缀都去掉
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
//这里其实就是对String的basePackage进行解析,最终得到:
//按照分隔符将多个路径转换成数组,并去掉空格以及换行符等
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

// Actually scan for bean definitions and register them.
//进行beandefinition的扫描并注册
//将bean定义注册代理给scanner类处理
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
//doScan的调用
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

return null;
}

进入到比较重要的部分:ClasspathBeanDefinitionScanner类的doScan()方法,该方法主要做的事情是:针对xml中所做的配置<context:component-scan base-package="cn.lx.controller" />,根据base-package的package路径下的class文件,进行遍历操作,根据其是否具备注解定义,得到beanDefinition的候选集合,针对候选集中的每一个beanDefinition,进行beanName的生成,并且针对是否属于AbstractBeanDefinition和AnnotatedBeanDefinition,进行相应的属性设置,之后会通过beanName获取是否已经容器中是否已经存在此beanName,若无则直接返回true,表示需要进行后续注册操作,即进入到了register的过程

其中doScan的主要方法及注释如下:

//针对xml中所做的配置<context:component-scan base-package="cn.lx.controller" />
//根据base-package的package路径下的class文件,进行遍历操作
//根据其是否具备注解定义,得到beanDefinition的候选集合
//针对候选集中的每一个beanDefinition,进行beanName的生成
//并生成BeanDefinitionHolder,进行scopeProxyMode的设置,之后进行register操作
//和非注解形式的合并到一路上了,后续的注册操作
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
//针对basePackages中的每一项basePackage做循环
for (String basePackage : basePackages) {
//得到候选BeanDefinition集合,注意这里如果没有@Controller等注解样式的是不会被加入到候选集中
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
//针对集合中的每一个候选项BeanDefinition进行详细的解析和处理
for (BeanDefinition candidate : candidates) {
//这里的生成内容会基于scope的字符进行解析,会得到:
//scopedName和scopedProxyMode,其中scopeName不单独说明的话,则默认为singleton,ProxyMode为No
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
//基于解析获得scope的性质给candidate设值
candidate.setScope(scopeMetadata.getScopeName());
//获取beanName,会走两步判断,有指定beanName的会直接赋值返回,否则会build默认的name,即shortName,例如:
//cn.lx.controller.LoginController,则会取LoginController,并且会将首字母小写,最终形式是:loginController
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
//判断beanDefinition的类型
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
//基于注解类型的BeanDefinition
//其实没有看AbstractBeanDefinition和AnnotatedBeanDefinition有何不同,写具体文章的时候要看
//AnnotatedBeanDefinition是接口
//其中在candidates的生成方法findCandidateComponents()进行candidates的候选集的生成中
//ScannedGenericBeanDefinition是实现了AnnotatedBeanDefinition接口的
//返回的beanDefinition本身就是就是实现了这个接口的,因此必然满足instanceof
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//对beanName和候选项的bean进行校验,以确定是否要进行注册,还是可能与已有bean存在冲突
if (checkCandidate(beanName, candidate)) {
//BeanDefinition的持有者
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
//设置ScopeProxyMode,若为设置,则默认为No,直接返回beanDefinition
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//真正标识着下一步就是register的操作了,只是要一步步走到DefaultListableBeanFactory类实例中的注册方法的调用
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}

register过程:

register过程无所谓是针对有无注解的情况,都是相同的逻辑,通过ClassPathBeanDefinitionScanner类的registerBeanDefition方法,其实调用的是BeanDefinitionReaderUtils类的静态方法registerBeanDefinition,之后再跟进到此静态方法中,就进入到了DefaultListableBeanFactory类的register操作,就会进行真正的map操作了

protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

 

三、过程时序图(梳理主要调用逻辑,涉及类和方法,可通过下载大图查看)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: