前面的文章写了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); }