Spring(二)——IOC启动流程源码
iwehdio的博客园:https://www.cnblogs.com/iwehdio/
学习自:
1、对IOC的理解
-
IoC(Inversion of Control)控制反转,包含了两个方面:一、控制。二、反转
- 控制指的是:当前对象对内部成员的控制权。是 bean 的创建、管理的权利,控制 bean 的整个生命周期。
- 反转指的是:这种控制权不由当前对象管理了,由其他(类,第三方容器)来管理。由之前的自己主动创建对象,变成现在被动接收别人给我们的对象的过程。
-
使用IOC的好处:
- 不用自己组装,拿来就用。
- 享受单例的好处,效率高,不浪费空间。
- 便于单元测试,方便切换mock组件。
- 便于进行AOP操作,对于使用者是透明的。
- 统一配置,便于修改。
-
IOC的原理:
- 原理就是通过Java的反射技术来实现的!通过反射我们可以获取类的所有信息(成员变量、类名等等等)!
- 再通过配置文件(xml)或者注解来描述类与类之间的关系
- 我们就可以通过这些配置信息和反射技术来构建出对应的对象和依赖关系了

- 根据Bean配置信息在容器内部创建Bean定义注册表
- 根据注册表加载、实例化bean、建立Bean与Bean之间的依赖关系
- 将这些准备就绪的Bean放到Map缓存池中,等待应用程序调用
-
Spring容器(Bean工厂)可简单分成两种:
- BeanFactory:是最基础、面向Spring的,可以理解为 HashMap。
- ApplicationContext:是在BeanFactory基础之上,面向使用Spring框架的开发者。提供了一系列的功能!
-
Bean的初始化过程:

- BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个
<bean>解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中; - 容器扫描BeanDefinitionRegistry中的BeanDefinition;调用InstantiationStrategy进行Bean实例化的工作;使用BeanWrapper完成Bean属性的设置工作;
- 单例Bean缓存池:Spring 在DefaultSingletonBeanRegistry类中提供了一个用于缓存单实例 Bean 的缓存器,它是一个用HashMap实现的缓存器,单实例的Bean以beanName为键保存在这个HashMap中
- BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个
2、bean容器的启动流程


















3、bean容器的启动
体系结构
-
引入Spring依赖:
- spring-context 会自动将 spring-core、spring-beans、spring-aop、spring-expression 这几个基础 jar 包带进来。
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.11.RELEASE</version> </dependency> -
最基本的启动 Spring 容器的例子:
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml"); -
这一行,就是在 ClassPath 中寻找 xml 配置文件,根据 xml 文件内容来构建 ApplicationContext。当然,除了 ClassPathXmlApplicationContext 以外,也还有其他构建 ApplicationContext 的方案可供选择,先来看看大体的继承结构是怎么样的:

1、FileSystemXmlApplicationContext 的构造函数需要一个 xml 配置文件在系统中的路径,其他和 ClassPathXmlApplicationContext 基本上一样。
2、AnnotationConfigApplicationContext 是基于注解来使用的,它不需要配置文件,采用 java 配置类和各种注解来配置,是比较简单的方式,也是大势所趋吧。
-
BeanFactory:
- 从名字上也很好理解,生产 bean 的工厂,它负责生产和管理各个 bean 实例。

- ApplicationContext 继承了 ListableBeanFactory,这个 Listable 的意思就是,通过这个接口,我们可以获取多个 Bean,大家看源码会发现,最顶层 BeanFactory 接口的方法都是获取单个 Bean 的。
- ApplicationContext 继承了 HierarchicalBeanFactory,Hierarchical (层次结构)单词本身已经能说明问题了,也就是说我们可以在应用中起多个 BeanFactory,然后可以将各个 BeanFactory 设置为父子关系。
- AutowireCapableBeanFactory 这个名字中的 Autowire 大家都非常熟悉,它就是用来自动装配 Bean 用的,ApplicationContext 并没有继承它,不过不用担心,不使用继承,不代表不可以使用组合,如果你看到 ApplicationContext 接口定义中的最后一个方法 getAutowireCapableBeanFactory() 就知道了。
- ConfigurableListableBeanFactory 也是一个特殊的接口,特殊之处在于它继承了第二层所有的三个接口,而 ApplicationContext 没有。
准备工作
-
第一步:ClassPathXmlApplicationContext 的构造方法说起。
- 调用的是第82行的构造方法。
refresh()是核心方法。为什么是叫 refresh(),而不是 init() 这种名字的方法。因为 ApplicationContext 建立起来以后,其实我们是可以通过调用 refresh() 这个方法重建的,refresh() 会将原来的 ApplicationContext 销毁,然后再重新执行一次初始化操作。
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { private Resource[] configResources; // 如果已经有 ApplicationContext 并需要配置成父子关系,那么调用这个构造方法 public ClassPathXmlApplicationContext(ApplicationContext parent) { super(parent); } ... //传入配置文件的位置,第一次启动容器,调用这个方法,读取配置文件,创建并刷新上下文 public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); // 根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割) setConfigLocations(configLocations); if (refresh) { refresh(); // 核心方法 } } ... } -
AbstractApplicationContext中,
refresh()方法的大致流程:@Override public void refresh() throws BeansException, IllegalStateException { // 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛 synchronized (this.startupShutdownMonitor) { // 准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符 prepareRefresh(); // 这步比较关键,这步完成后,配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中, // 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了, // 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map) ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean // 这块待会会展开说 prepareBeanFactory(beanFactory); try { // 【这里需要知道 BeanFa ctoryPostProcessor 这个知识点,Bean 如果实现了此接口, // 那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。】 // 这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化 // 具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事 postProcessBeanFactory(beanFactory); // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法 invokeBeanFactoryPostProcessors(beanFactory); // 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别 // 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization // 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化 registerBeanPostProcessors(beanFactory); // 初始化当前 ApplicationContext 的 MessageSource,国际化这里就不展开说了,不然没完没了了 initMessageSource(); // 初始化当前 ApplicationContext 的事件广播器,这里也不展开了 initApplicationEventMulticaster(); // 从方法名就可以知道,典型的模板方法(钩子方法), // 具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前) onRefresh(); // 注册事件监听器,监听器需要实现 ApplicationListener 接口。这也不是我们的重点,过 registerListeners(); // 重点,重点,重点 // 初始化所有的 singleton beans //(lazy-init 的除外) finishBeanFactoryInitialization(beanFactory); // 最后,广播事件,ApplicationContext 初始化完成 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源 destroyBeans(); // 重置活动标志ex cancelRefresh(ex); // 把异常往外抛 throw ex; } finally { //重置Spring中的公共部分缓存,因为我们可能不再需要单例bean的单例数据了 resetCommonCaches(); } } } -
创建Bean容器前的准备工作:AbstractApplicationContext中的
prepareRefresh()。protected void prepareRefresh() { // 记录启动时间, // 将 active 属性设置为 true,closed 属性设置为 false,它们都是 AtomicBoolean 类型 this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // 初始化上下文环境中的占位符属性源 initPropertySources(); // 校验 xml 配置文件 getEnvironment().validateRequiredProperties(); this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>(); }
解析xml文件,把BeanDefinition注册到BeanFactory并返回
-
创建 Bean 容器,加载并注册 Bean:AbstractApplicationContext中的
obtainFreshBeanFactory()。- 这个方法是全文最重要的部分之一,这里将会初始化 BeanFactory、加载 Bean、注册 Bean 等等。
- 当然,这步结束后,Bean 并没有完成初始化。这里指的是 Bean 实例并未在这一步生成。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 关闭旧的 BeanFactory (如果有),创建新的 BeanFactory,加载 Bean 定义、注册 Bean 等等 refreshBeanFactory(); // 返回刚刚创建的 BeanFactory ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }-
refreshBeanFactory()方法在AbstractApplicationContext中是抽象的,其子类AbstractRefreshableApplicationContext对其进行了实现:@Override protected final void refreshBeanFactory() throws BeansException { // 如果 ApplicationContext 中已经加载过 BeanFactory 了,销毁所有 Bean,关闭 BeanFactory // 注意,应用中 BeanFactory 本来就是可以多个的,这里可不是说应用全局是否有 BeanFactory,而是当前 // ApplicationContext 是否有 BeanFactory if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { // 初始化一个 DefaultListableBeanFactory,为什么用这个,我们马上说。 DefaultListableBeanFactory beanFactory = createBeanFactory(); // 用于 BeanFactory 的序列化,我想大部分人应该都用不到 beanFactory.setSerializationId(getId()); // 下面这两个方法很重要,别跟丢了,具体细节之后说 // 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用 customizeBeanFactory(beanFactory); // 加载 Bean 到 BeanFactory 中 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } } -
ApplicationContext 继承自 BeanFactory,但是它不应该被理解为 BeanFactory 的实现类,而是说其内部持有一个实例化的 BeanFactory(DefaultListableBeanFactory)。以后所有的 BeanFactory 相关的操作其实是委托给这个实例来处理的。
-
为什么选择实例化 DefaultListableBeanFactory ?
- 因为其是继承体系中最底层的,功能最强大的。
- 继承自DefaultSingletonBeanRegistry单例池注册器,可以获取池中的单例bean。
- 实现了BeanFactory,可以对外提供getBean方法。
-
BeanDefinition:
- BeanFactory 是 Bean 容器,那么 Bean 又是什么呢?
- BeanDefinition 就是我们所说的 Spring 的 Bean,我们自己定义的各个 Bean 其实会转换成一个个 BeanDefinition 存在于 Spring 的 BeanFactory 中。
- BeanDefinition 中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。
- BeanDefinition 的接口定义:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { // 我们可以看到,默认只提供 sington 和 prototype 两种, // 很多读者可能知道还有 request, session, globalSession, application, websocket 这几种, // 不过,它们属于基于 web 的扩展。 String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; // 比较不重要,直接跳过吧 int ROLE_APPLICATION = 0; int ROLE_SUPPORT = 1; int ROLE_INFRASTRUCTURE = 2; // 设置父 Bean,这里涉及到 bean 继承,不是 java 继承。请参见附录的详细介绍 // 一句话就是:继承父 Bean 的配置信息而已 void setParentName(String parentName); // 获取父 Bean String getParentName(); // 设置 Bean 的类名称,将来是要通过反射来生成实例的 void setBeanClassName(String beanClassName); // 获取 Bean 的类名称 String getBeanClassName(); // 设置 bean 的 scope void setScope(String scope); String getScope(); // 设置是否懒加载 void setLazyInit(boolean lazyInit); boolean isLazyInit(); // 设置该 Bean 依赖的所有的 Bean,注意,这里的依赖不是指属性依赖(如 @Autowire 标记的), // 是 depends-on="" 属性设置的值。 void setDependsOn(String... dependsOn); // 返回该 Bean 的所有依赖 String[] getDependsOn(); // 设置该 Bean 是否可以注入到其他 Bean 中,只对根据类型注入有效, // 如果根据名称注入,即使这边设置了 false,也是可以的 void setAutowireCandidate(boolean autowireCandidate); // 该 Bean 是否可以注入到其他 Bean 中 boolean isAutowireCandidate(); // 主要的。同一接口的多个实现,如果不指定名字的话,Spring 会优先选择设置 primary 为 true 的 bean void setPrimary(boolean primary); // 是否是 primary 的 boolean isPrimary(); // 如果该 Bean 采用工厂方法生成,指定工厂名称。对工厂不熟悉的读者,请参加附录 // 一句话就是:有些实例不是用反射生成的,而是用工厂模式生成的 void setFactoryBeanName(String factoryBeanName); // 获取工厂名称 String getFactoryBeanName(); // 指定工厂类中的 工厂方法名称 void setFactoryMethodName(String factoryMethodName); // 获取工厂类中的 工厂方法名称 String getFactoryMethodName(); // 构造器参数 ConstructorArgumentValues getConstructorArgumentValues(); // Bean 中的属性值,后面给 bean 注入属性值的时候会说到 MutablePropertyValues getPropertyValues(); // 是否 singleton boolean isSingleton(); // 是否 prototype boolean isPrototype(); // 如果这个 Bean 是被设置为 abstract,那么不能实例化, // 常用于作为 父bean 用于继承,其实也很少用...... boolean isAbstract(); int getRole(); String getDescription(); String getResourceDescription(); BeanDefinition getOriginatingBeanDefinition(); } - BeanFactory 是 Bean 容器,那么 Bean 又是什么呢?
-
再往下看 refreshBeanFactory() 方法中的剩余部分:
customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); -
AbstractRefreshableApplicationContext中的
customizeBeanFactory(beanFactory):- 就是配置是否允许 BeanDefinition 覆盖、是否允许循环引用。
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { if (this.allowBeanDefinitionOverriding != null) { // 是否允许 Bean 定义覆盖 beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.allowCircularReferences != null) { // 是否允许 Bean 间的循环依赖 beanFactory.setAllowCircularReferences(this.allowCircularReferences); } }- BeanDefinition 的覆盖问题:就是在配置文件中定义 bean 时使用了相同的 id 或 name,默认情况下,allowBeanDefinitionOverriding 属性为 null,如果在同一配置文件中重复了,会抛错,但是如果不是同一配置文件中,会发生覆盖。
- 循环引用就是:A 依赖 B,而 B 依赖 A。或 A 依赖 B,B 依赖 C,而 C 依赖 A。默认情况下,Spring 允许循环依赖,当然如果你在 A 的构造方法中依赖 B,在 B 的构造方法中依赖 A 是不行的。
-
loadBeanDefinitions(beanFactory)在AbstractRefreshableApplicationContext是抽象的,具体的实现在 AbstractXmlApplicationContext中:- 根据配置,加载各个 Bean,然后放到 BeanFactory 中。
- 读取配置的操作在 XmlBeanDefinitionReader 中,其负责加载配置、解析。
/** 我们可以看到,此方法将通过一个 XmlBeanDefinitionReader 实例来加载各个 Bean。*/ @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 给这个 BeanFactory 实例化一个 XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 在这个上下文的源加载环境中配置 bean definition reader beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 初始化 BeanDefinitionReader,其实这个是提供给子类覆写的, // 我看了一下,没有类覆写这个方法,我们姑且当做不重要吧 initBeanDefinitionReader(beanDefinitionReader); // 重点来了,继续往下 loadBeanDefinitions(beanDefinitionReader); } -
AbstractXmlApplicationContext中的
loadBeanDefinitions(beanDefinitionReader):- 作用:用刚刚初始化的 Reader 开始来加载 xml 配置,将一个配置文件转换为一颗 DOM 树。
- 在转换完毕后,调用
doRegisterBeanDefinitions(root)从 xml 根节点开始解析文件。 - 调用DefaultBeanDefinitionDocumentReader中的
parseBeanDefinitions(root, this.delegate)方法解析xml文件中的标签。
-
parseBeanDefinitions(root, this.delegate):- 核心解析方法。
// default namespace 涉及到的就四个标签 <import />、<alias />、<bean /> 和 <beans />, // 其他的属于 custom 的 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)) { // 解析 default namespace 下面的几个元素 parseDefaultElement(ele, delegate); } else { // 解析其他 namespace 的元素 delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }-
对于每个配置来说,分别进入到 parseDefaultElement(ele, delegate); 和 delegate.parseCustomElement(ele); 这两个分支了。
-
parseDefaultElement(ele, delegate) 代表解析的节点是
<import />、<alias />、<bean />、<beans />这几个。 -
这里的四个标签之所以是 default 的,是因为它们是处于这个 namespace 下定义的:
http://www.springframework.org/schema/beans -
而对于其他的标签,将进入到 delegate.parseCustomElement(element) 这个分支。如我们经常会使用到的
<mvc />、<task />、<context />、<aop />等。这些属于扩展,如果需要使用上面这些 ”非 default“ 标签,那么上面的 xml 头部的地方也要引入相应的 namespace 和 .xsd 文件的路径。同时代码中需要提供相应的 parser 来解析,如 MvcNamespaceHandler、TaskNamespaceHandler、ContextNamespaceHandler、AopNamespaceHandler 等。
-
DefaultBeanDefinitionDocumentReader中的
parseDefaultElement(ele, delegate)方法:- 处理default标签。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // 处理 <import /> 标签 importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // 处理 <alias /> 标签定义 // <alias name="fromName" alias="toName"/> processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // 处理 <bean /> 标签定义,这也算是我们的重点吧 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // 如果碰到的是嵌套的 <beans /> 标签,需要递归 doRegisterBeanDefinitions(ele); } } -
DefaultBeanDefinitionDocumentReader中的
processBeanDefinition(ele, delegate):- 以解析
<bean />标签为例。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 将 <bean /> 节点中的信息提取出来,然后封装到一个 BeanDefinitionHolder 中,细节往下看 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); // 下面的几行先不要看,跳过先,跳过先,跳过先,后面会继续说的 if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 注册bean BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // 发送注册事件给监听器 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } } - 以解析
-
BeanDefinitionParserDelegate中的
parseBeanDefinitionElement(Element ele)解析<bean>标签:- 解析出
<bean>标签中的各个属性,如类名,id、作用域、注入方式等。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); } public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<String>(); // 将 name 属性的定义按照 “逗号、分号、空格” 切分,形成一个 别名列表数组, // 当然,如果你不定义 name 属性的话,就是空的了 if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; // 如果没有指定id, 那么用别名列表的第一个名字作为beanName if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isDebugEnabled()) { logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } // 根据 <bean ...>...</bean> 中的配置创建 BeanDefinition,然后把配置中的信息都设置到实例中, // 细节后面细说,先知道下面这行结束后,一个 BeanDefinition 实例就出来了。 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); // 到这里,整个 <bean /> 标签就算解析结束了,一个 BeanDefinition 就形成了。 if (beanDefinition != null) { // 如果都没有设置 id 和 name,那么此时的 beanName 就会为 null,进入下面这块代码产生 // 如果读者不感兴趣的话,我觉得不需要关心这块代码,对本文源码分析来说,这些东西不重要 if (!StringUtils.hasText(beanName)) { try { - 解析出
