前面的文章写了 xml 中直接配置 bean 进行 IoC 的过程解析, 接下来会针对注解进行 IoC 容器初始化的过程解析
因为会与之前的内容存在部分重叠, 因此会针对相同的部分简略带过, 针对不同的部分做重点说明:
一, xml 的配置和代码中的注解配置:
applicationContext.xml 配置添加:
<context:component-scanbase-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);
- }
三, 过程时序图 (梳理主要调用逻辑, 涉及类和方法, 可通过下载大图查看)
来源: https://www.cnblogs.com/keke-xiaoxiami/p/10773017.html