https://www.jianshu.com/p/5489e25744a9
https://www.cnblogs.com/warehouse/p/9385110.html
概述
使用 Spring 时,XML 和注解是使用得最多的两种配置方式,虽然是两种完全不同的配置方式,但对于 IOC 容器来说,两种方式的不同主要是在 BeanDefinitionReader 读取不同的配置,解析成 BeanDefinition 对象。而对于核心的容器启动流程,仍然是一致的。
主程序入口
XmlUserTest 测试类
public class XmlUserTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
User user = ac.getBean("user", User.class);
System.out.println(user.toString());
}
}
spring-config.xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.zlp.spring.init.entity.User">
<property name="age" value="10"/>
<property name="userName" value="smile"/>
</bean>
</beans>
User 实体类
/**
* User实体类
* @date: 2021/2/24 17:47
*/
@Lazy(value = false)
public class User {
private String userName;
private Integer age;
public User(){
System.out.println("空参构造方法");
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
源码调试详解
前期准备环境工作做好,下面进入正题,开始 debug 调试Spring Ioc 启动流程分析。
ClassPathXmlApplicationContext
/**
* 创建一个新的 ClassPathXmlApplicationContext,从给定的XML文件加载定义并自动刷新上下文
* @param configLocation resource location
* @throws BeansException if context creation failed
*/
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
// 本类构造函数
this(new String[] {configLocation}, true, null);
}
/**
* 使用给定的父级创建新的ClassPathXmlApplicationContext,从给定的XML文件加载定义。
* @param 资源位置数组
* @param 是否自动刷新上下文,加载所有bean定义并创建所有单例。或者,在进一步配置上下文后手动调用refresh。
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public ClassPathXmlApplicationContext(String[] configLocations,
boolean refresh,
@Nullable ApplicationContext parent) throws BeansException {
/**
* 1.调用父类构造函数完成一些资源的初始化:
* 1.1 初始化默认的资源文件解析器,默认为:PathMatchingResourcePatternResolver.
* 1.2 初始化默认的类加载器.
*/
super(parent);
// 2.解析配置文件的路径,例如:applicationContext-${user.name}.xml这种配置文件名称,并且保存到configLocations中,为下一步的解析bean定义做准备
setConfigLocations(configLocations);
// 3.如果需要刷新容器
if (refresh) {
// 执行刷新容器的操作
refresh();
}
}
细节部分:
- String [] configLocations :可以是一个数组,可以加载多个配置文件
- ApplicationContext parent :默认是空(null)
- super(parent) : 当前类继承了 ClassPathXmlApplicationContext 父类时,可以自动义资源初始化配置操作
refresh()
总共有 13 个方法
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
/**
* 1.准备上下文的刷新工作,记录bean容器的启动时间,容器活跃状态
* 验证系统中一些属性和属性值的设置等.
* 使用LinkedHashSet初始化earlyApplicationListeners和earlyApplicationEvents
*/
prepareRefresh();
/**
* 2.获取Bean工厂,期间会做解析和加载bean定义的一些列工作.生成BeanDefinition对象.
* 此处返回的beanFactory的真实类型为:DefaultListableBeanFactory
*
*
* 自定义的xsd约束文件也会在该步骤进行解析,通过实现BeanDefinitionParser接口,并实现parse方法
* 解析自定义标签时通过实现NamespaceHandlerSupport接口,并实现init方法进行实现
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
* 3.bean工厂的初始化准备工作,设置bean工厂的一些属性
* 比如:创建bean工厂时,需要忽略哪些接口,需要注册哪些bean,需要设置哪些Bean的后置处理器等.
*
* 例如常用的:ApplicationContextAwareBeanPostProcessor, ApplicationListenerDetector
*
* 此外,注册一些和环境相关的bean单实例bean.
*/
prepareBeanFactory(beanFactory);
try {
/**
* 4.Bean定义加载完毕之后实现,目前方法为空实现,留给开发人员进行自定义扩展。
* 和BeanFactoryPostProcessor中的方法postProcessBeanFactory相同
*
* 该方法在Bean定义加载完毕之后,Bean实例化之前会执行
* 比如在BeanFactory加载完所有的Bean定义之后,想要修改某个bean的定义信息,可以通过重写这个方法实现.
* 比如:在xml中配置了<bean id="user"><property name="name" value="wb"></property></bean>
* 如果想在不修改配置文件的情况下修改name的值,可以使用如下的方法:
* class MyApplicationContext extends ClassPathXmlApplicationContext{
public MyApplicationContext(String s){
super(s);
}
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("user");
PropertyValue propertyValue=new PropertyValue("name", "www.so.com");
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
*/
postProcessBeanFactory(beanFactory);
/**
* 5.执行beanFactory的后置处理器
*
* 先执行BeanDefinitionRegistryPostProcessor接口的实现类的postProcessBeanDefinitionRegistry方法,
* 执行过程中,也是先执行实现了优先级接口PriorityOrdered的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
* 然后执行实现了Ordered接口的...
* 最后执行未实现PriorityOrdered接口和Ordered接口的...
*
* 然后执行BeanFactoryPostProcessor接口的实现类的postProcessBeanFactory方法
* 执行过程中,也是先执行实现了优先级接口PriorityOrdered的BeanFactoryPostProcessor的postProcessBeanFactory方法
* 然后执行实现了Ordered接口的...
* 最后执行未实现PriorityOrdered接口和Ordered接口的...
*
* 其中也涉及到了排序过程
*
*
* 配置类中的Selector类型的组件和@Component,@ComponentScan中的元数据信息也会在该步骤中进行解析
* 还包括执行条件注解@Condition的回调逻辑
*
*
* ImportBeanDefinitionRegistrar对应的registerBeanDefinitions方法也会在该步骤中调用,给容器中注册自定义的组件.
*/
invokeBeanFactoryPostProcessors(beanFactory);
/**
* 6.注册所有bean的后置处理器.用来拦截Bean的创建
*
* 注册所有实现了BeanPostProcessor接口的后置处理器
* 执行过程中,也是先执行实现了优先级接口PriorityOrdered接口的BeanPostProcessor的addBeanPostProcessor方法
* 然后执行实现了Ordered接口的...
* 最后执行未实现PriorityOrdered接口和Ordered接口的...
*
* 其中也涉及到了排序过程
*/
registerBeanPostProcessors(beanFactory);
/**
* 7.初始化消息源
* 用来做国际化,消息绑定,消息解析等功能
* 一般在SpringMVC中会使用到.
*/
initMessageSource();
/**
* 8.初始化事件派发器,用来发布事件
* 如果容器中有类型为ApplicationEventMulticaster的派发器组件,则直接获取使用
* 如果容器中没有,则默认创建一个类型为SimpleApplicationEventMulticaster的派发器,供容器派发事件使用
*/
initApplicationEventMulticaster();
/**
* 9.用来初始化一些特殊的Bean,目前默认是空方法,未实现,可以通过继承AbstractApplicationContext类,
* 然后覆写该方法进行自定义特殊bean的初始化.
*
* 比如:AbstractRefreshableWebApplicationContext中onRefresh方法用来初始化主题能力.
*
* SpringBoot也是在改步骤中启动内嵌Tomcat容器的
*/
onRefresh();
/**
* 10.注册监听器
* 将监听器绑定到广播器上,将监听器对应的beanName绑定到到第8步初始化的事件派发器中,
* 如果之前有发布的事件,则直接通过事件派发器将事件派发出去.
*/
registerListeners();
/**
* 11.初始化所有剩余的单实例Bean(没有使用懒加载的Bean).整个Spring IOC的核心.
*
* 包括执行@PostConstruct标注的方法.
*
* 注意:SpringMVC的父子容器创建Bean的过程:
* SpringMVC中,存在着父容器和子容器。当父容器启动之后,会通过该方法将所有的Dao和Service对应的Bean创建出来,保存到beanFactory的单例缓存容器中
* 当子容器启动之后,也会通过该方法将所有的Controller,viewResolver,HandlerMapping对应的Bean创建出来,然后放入到beanFactory的单例缓存容器中.
*/
finishBeanFactoryInitialization(beanFactory);
/** 12.发布事件。例如容器中的刷新事件:ContextRefreshedEvent就是在这一步中发布. SpringCloud在该步骤中会启动web服务 */
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
// 清空单实例bean对应的map及缓存
destroyBeans();
// 设置容器的活跃状态为false
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
prepareRefresh()
该方法很简单,主要做前期准备工作
- 记录容器的启动时间
- 设置容器的关闭状态closed(false)和激活状态active(true)
- initPropertySources,初始化容器中的一些校验规则,比如字段的校验;使用getEnvironment().setRequiredProperties()进行设置
- 校验容器中的一些必要属性,如果第(3)步中设置了容器中的必要属性,该步骤会对第三步设置的值进行校验
声明容器中早期的应用监听器以及容器中早期的应用事件
/**
* 准备此上下文以进行刷新,设置其启动日期和活动标志,以及执行属性源的任何初始化。
*/
protected void prepareRefresh() {
// 记录容器的启动时间.
this.startupDate = System.currentTimeMillis();
// 记录容器未关闭
this.closed.set(false);
// 记录容器状态为激活状态
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// Initialize any placeholder property sources in the context environment.
/**
* 如果需要在验证系统属性之前,给系统中设置一些默认值。可以通过继承AbstractApplicationContext类,并重写该方法实现。
* Spring留给开发人员的一个扩展点.
*
* 例如子类在方法中设置环境属性中必须需要的变量:getEnvironment().setRequiredProperties("myProp");
*/
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
/**
* 作用:校验设置的必须属性是否能够在系统环境中找到对应的值
*
* 如果在initPropertySources方法中使用getEnvironment().setRequiredProperties(String... keys)设置了必须的属性,而通过this.getProperty(key)
* 没有从系统环境中获取到属性的值,则会抛出MissingRequiredPropertiesException异常
*/
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
if (this.earlyApplicationListeners == null) {
/** 在SpringBoot中会有大量的初始化监听器,用于初始化使用 */
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
/** 定义早期的应用事件 */
this.earlyApplicationEvents = new LinkedHashSet<>();
}
网上有人说其实这个函数没什么用,因为最后两句代码才是最为关键的,但是却没有什么逻辑处理,initPropertySources 是空的,没有任何逻辑,而 getEnvironment().validateRequiredProperties 也因为没有需要验证的属性而没有做任何处理。其实这都是因为没有彻底理解才会这么说,这个函数如果用好了作用还是挺大的。那么,该怎么用呢?我们先探索下各个函数的作用。
(1)initPropertySources 正符合 Spring 的开放式结构设计,给用户最大扩展 Spring 的能力。用户可以根据自身的需要重写 initPropertySources 方法,并在方法中进行个性化的属性处理及设置。
(2)validateRequiredProperties 则是对属性进行验证,那么如何验证呢?我们举个融合两句代码的小例子来帮助大家理解。
假如现在有这样一个需求,工程在运行过程中用到的某个设置(例如 VAR )是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,那么工程可能不会工作。这一要求可能会有各种各样的解决办法,当然,在 Spring 中可以这样做,你可以直接修改 Spring 的源码,例如修改 ClassPathXmlApplicationContext 。当然,最好的办法还是对源码进行扩展,我们可以自定义
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
public MyClassPathXmlApplicationContext(String... configLocations) throws BeansException {
super(configLocations);
}
@Override
protected void initPropertySources() {
// 添加验证要求
getEnvironment().setRequiredProperties("VAR");
}
}
我们自定一义了继承自 ClassPathXmlApplicationContext 的 MyClassPathXmlApplicationContext , 并重写了 initPropertySources 方法,在方法中添加了我们的个性化需求,那么在验证的时候也就是程序走到 getEnvironment().validateRequiredProperties() 代码的时候,如果系统并没有检测到对应 VAR 的环境变量,那么将抛出异常。当然我们还需要在使用的时候替换掉原有的 ClassPathXmlApplicationContext:
public static void main(String[] args) {
ApplicationContext ac = new MyClassPathXmlApplicationContext("spring-config.xml");
User user = (User) ac.getBean("user");
System.out.println(user.toString());
}
obtainFreshBeanFactory()
obtainFreshBeanFactory 方法从字面上理解是获取 BeanFactory。之前有说过,ApplicationContext 是对BeanFactory 的功能上的扩展,不但包含了BeanFactory的全部功能更在其基础上添加了大量的扩展功能,那么obtainFreshBeanFactory 正是实现BeanFactory的地方,也就是经过了这个函数后 ApplicationContext 就已经拥有了 BeanFactory 的全部功能。
/**
* 告诉子类刷新内部bean工厂
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
/**
* 刷新bean工厂,判断bean工厂是否已经存在,如果存在需要进行销毁和关闭
* 默认调用的是AbstractRefreshableApplicationContext的refreshBeanFactory方法.
* 刷新Bean工厂时会进行bean定义的加载操作。
*/
refreshBeanFactory();
return getBeanFactory();
}
方法中将核心实现委托给了 refreshBeanFactory
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
// 判断bean工厂是否存在,如果存在需要先销毁和关闭。否则会出现问题
if (hasBeanFactory()) {
// 销毁bean,根据bean的名称将bean工厂中的所有bean都从map中移除
destroyBeans();
// 关闭bean工厂,设置Bean工厂的序列化id为null,并将beanFactory的值赋值为null
closeBeanFactory();
}
try {
// 重新创建bean工厂,默认返回的是Bean工厂类型是:DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 设置序列化ID,ID: class名称 + "@" + 对象的十六进制值
beanFactory.setSerializationId(getId());
/** 自定义Bean工厂
* 设置两个属性:
* 1. 是否允许覆盖同名称的不同定义的对象
* 2. 是否允许bean之间存在循环依赖
*/
customizeBeanFactory(beanFactory);
// 解析并加载bean的定义,默认是通过AbstractXmlApplicationContext类中的loadBeanDefinitions实现
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
我们详细分析上面的每个步骤。
- 创建 DefaultListableBeanFactory。
- 在介绍BeanFactory 的时候,不知道读者是否还有印象,声明方式为:ApplicationContext ac = new ClassPathXmlApplicationContext(“spring-config.xml”); 并 XmlBeanDefinitionReader 提供了读取类型的 reader 属性也就是说 DefaultListableBeanFactory 是容器的基础。必须首先实例化,那么在这里就是实例化DefaultListableBeanFactory 的步骤。
- 指定序列化ID。
- 定制BeanFactory。
- 加载BeanDefinition。
- 使用全局变量记录BeanFactory类实例。
因为DefaultListableBeanFactory类型的变量beanFactory是函数内的局部变量,所以要使用全局变量记录解析结果。
定制化 BeanFactory
/**
* Customize the internal bean factory used by this context.
* Called for each {@link #refresh()} attempt.
* <p>The default implementation applies this context's
* {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
* and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
* if specified. Can be overridden in subclasses to customize any of
* {@link DefaultListableBeanFactory}'s settings.
* @param beanFactory the newly created bean factory for this context
* @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
* @see DefaultListableBeanFactory#setAllowCircularReferences
* @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
* @see DefaultListableBeanFactory#setAllowEagerClassLoading
*/
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
// 如果属性allowBeanDefinitionOverriding不为空,设置给beanFactory对象相应属性
// 此属性的含义:是否允许覆盖同名称的不同定义的对象
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 如果属性allowCircularReferences不为空,设置给beanFactory对象相应属性
// 此属性的含义:是否允许bean之间存在循环依赖
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
是否允许覆盖和允许依赖的设置这里这是判断了是否为空,如果不为空则进行设置,但是并没有看到在哪里进行设置,究竟这个设置是在哪里进行设置的呢?还是那句话,使用子类覆盖方法,例如:
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
......
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
super.setAllowBeanDefinitionOverriding(false);
super.setAllowCircularReferences(false);
super.customizeBeanFactory(beanFactory);
}
}
加载 BeanDefinition
在第一步中提到了 ClassPathXmlApplicationContext 与 XmlBeanFactory 创建的对比,在实现配置文件的加载功能中除了我们在第一步中已经初始化的 DefaultListableBeanFactory外,还需要 XmlBeanDefinitionReader 来读取XML,那么在这个步骤中首先要做的就是初始化 XmlBeanDefinitionReader。
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 为指定 beanFactory 创建 XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
// 对beanDefinitionReader进行环境变量的设置
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
// 对beanDefinitionReader进行设置,可以覆盖
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
在初始化了 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 后就可以进行配置文件的读取了。
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
* method; hence this method is just supposed to load and/or register bean definitions.
* @param reader the XmlBeanDefinitionReader to use
* @throws BeansException in case of bean registration errors
* @throws IOException if the required XML document isn't found
* @see #refreshBeanFactory
* @see #getConfigLocations
* @see #getResources
* @see #getResourcePatternResolver
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
使用 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法进行配置文件的加载注册相信大家已经不陌生,这完全就是开始 BeanFactory 的套路。因为在XmlBeanDefinitionReader 中已经将之前初始化的DefaultListableBeanFactory 注册进去了,所有 XmlBeanDefinitionReader 所读取的 BeanDefinitionHolder 都会注册到 DefaultListableBeanFactory 中,也就是经过此步骤,类型 DefaultListableBeanFactory 的变量beanFactory 已经包含了所有解析好的配置。
prepareBeanFactory
在进入 prepareBeanFactory 前,Spring 已经完成了对配置的解析,而ApplicationContext在功能上的扩展也由此展开。
/**
* 配置工厂的标准上下文特征,例如上下文的类加载器和后处理器
* @param beanFactory the BeanFactory to configure
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
// 设置SPEL表达式解析器,用来支持Spring的SPEL表达式
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 添加属性编辑注册器。例如一个字符串类型的地址需要转换为一个Address对象,可以使用该功能.
// 可参考示例:spring-source-study模块下的com.wb.spring.propertyeditor包下的示例程序
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 添加bean的后置处理器。此处添加的是Spring自己的后置处理器,用来回调bean所实现的aware接口中的方法.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 下面的ignoreDependencyInterface是用来设置bean工厂中需要忽略的接口
// 可以通过实现EnvironmentAware接口来获取到当前的环境信息Environment。
/**
* 如果将EnvironmentAware接口添加到ignoreDependencyInterface中,则在使用的地方通过@Autowired将会无法正常注入
* 而是需要通过setEnvironment方法进行注入,下面的其他接口都类似.
*/
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
// 可以通过实现EmbeddedValueResolverAware接口来获取String类型值的解析器
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
// 资源加载器,例如使用:@Autowired ResourceLoaderAware aware; 将不会被注入
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
// 事件发布器
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
// 消息资源
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
// 应用的上下文信息
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// 注册一些可以自动装配的接口。 当类型为dependencyType时, 注入autowiredValue。为了解决一个类型有多个子类实现时,优先注入那个子类实现的问题。
// 例如下面第一个,当注入类型为BeanFactory时,注入的值为beanFactory,默认为DefaultListableBeanFactory
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
// 当注入类型为ResourceLoader时,注入的值为ApplicationContext
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
// 当注入类型为ApplicationEventPublisher时,注入的值为ApplicationContext
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
// 当注入的类型为ApplicationContext时,注入的值为ApplicationContext.
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// 增加一个bean的后置处理器,ApplicationListenerDetector
// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// 如果bean工厂中存在着名称为loadTimeWeaver的bean定义,则给bean工厂中加入LoaderTimeWeaverAwareProcessor后置处理器
// 补充:1、增加对AspectJ的支持,在Java中织入分为3种:(1) 编译期织入; (2) 类加载期织入; (3) 运行期织入。编译期织入指的是在java编译期,
// 采用特殊的编译器,将切面织入到java类中;类加载期织入则指的是通过特殊的类加载器,在字节码加载到JVM时,织入切面;运行期织入则是
// 通过采用cglib或者jdk进行切面的织入。
// 2、aspectJ中提供了两种方式:
// (1) 通过特殊编译器,在编译期,将aspectJ语言编写的切面类织入到java类中;
// (2) 类加载期织入,就是通过下面的LoadTimeWeaving
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// 给容器中注册一些与运行环境相关的单实例Bean
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
// beanName: environment,直接将new出来的Spring内部对象放入到Spring的单实例缓存池中.
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
// beanName: systemProperties 方法:System.getProperties();
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
// beanName: systemEnvironment, 方法:System.getEnv();
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
上面函数中主要进行了几个方面的扩展。
- 增加对 SPEL 语言的支持。
- 增加对属性编辑器的支持。
- 增加对一些内置类,比如 EnvironmentAvare、MessageSourceAware 的信息注入。
- 设置了依赖功能可忽略的接口。
- 注册一些固定依赖的属性。
- 增加对AspectJ的支持。
将相关环境变量及属性注册以单例模式注册。
可能读者不是很理解每个步骤的具体含义,接下来我们会对各个步骤进行详细地分析。
增加SPEL语言的支持
Spring 表达式语言全称为 “Spring Expression Language”,缩写为 “SpEL”,类似于Struts 2x 中使用的OGNL表达式语言,能在运行时构建复杂表达式、存取对象图属性、对象方法调用等, 并且能与Spring功能完美整合,比如能用 来配置bean定义。SpEL是单独模块,只依赖于core 模块,不依赖于其他模块,可以单独使用。
SpEL使用#{…}为定界符,所有在花括号中的字符都将被认为是SpEL,使用格式如下:
<bean id = "saxophone" value = "com.xxx.xxx.Xxx"/>
<bean >
<property name="instrument" value="#{saxophone}"/>
<bean/>
相当于:
<bean id = "saxophone" value = "com.xxx.xxx.Xxx"/>
<bean >
<property name="instrument" ref="saxophone"/>
<bean/>
当然,上面只是列举了其中最简单的使用方式,SPEL功能非常强大,使用好可以大大提高开发效率,这里只为唤起读者的记忆来帮助我们理解源码,有兴趣的读者可以进一步深入研究。
在源码中通过代码 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver()) 注册语言解析器,就可以对SPEL进行解析了,那么在注册解析器后 Spring 又是在什么时候调用这个解析器进行解析呢?
之前我们讲解过 Spring 在 bean 进行初始化的时候会有属性填充的一步,而在这一步中 Spring 会调用AbstractAutowireCapableBeanFactory 类的 applyPropertyValues 函数来完成功能。就在这个函数中,会通过构造 BeanDefinitionValueResolver 类型实例 valueResolver 来进行属性值的解析。同时,也是在这个步骤中一般通过 AbstractBeanFactory中的 evaluateBeanDefinitionString 方法去完成 SPEL 的解析。
/**
* Evaluate the given String as contained in a bean definition,
* potentially resolving it as an expression.
* @param value the value to check
* @param beanDefinition the bean definition that the value comes from
* @return the resolved value
* @see #setBeanExpressionResolver
*/
@Nullable
protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
if (this.beanExpressionResolver == null) {
return value;
}
Scope scope = null;
if (beanDefinition != null) {
String scopeName = beanDefinition.getScope();
if (scopeName != null) {
scope = getRegisteredScope(scopeName);
}
}
return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
}
当调用这个方法时会判断是否存在语言解析器,如果存在则调用语言解析器的方法进行解析,解析的过程是在Spring的 expression 的包内,这里不做过多解释。我们通过查看对evaluateBeanDefinitionString方法的调用层次可以看出,应用语言解析器的调用主要是在解析依赖注入bean的时候,以及在完成bean的初始化和属性获取后进行属性填充的时候。
增加属性注册编辑器
在Spring DI注入的时候可以把普通属性注入进来,但是像Date类型就无法被识别。例如:
public class UserManager {
private Date dateValue;
public Date getDateValue() {
return dateValue;
}
public void setDateValue(Date dateValue) {
this.dateValue = dateValue;
}
@Override
public String toString() {
return "dataValue: " + dateValue;
}
}
上面代码中,需要对日期型属性进行注入:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userManager" class="org.cellphone.uc.UserManager">
<property name="dateValue">
<value>2018-07-29</value>
</property>
</bean>
</beans>
测试代码:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring/beans-test.xml");
UserManager manager = (UserManager) context.getBean("userManager");
System.out.println(manager);
}
如果直接这样使用,程序则会报异常,类型转换不成功。因为在UserManager中的dataValue属性是Date类型的,而在XML中配置的确实String类型的,所以当然会报异常。
Spring针对此问题提供了两种解决方法。
使用自定义属性编辑器
使用自定义属性编辑器,通过继承PropertyEditorSupport,重写setAsText方法,具体步骤如下。
(1)编写自定义的属性编辑器。
public class DatePropertyEditor extends PropertyEditorSupport {
private String format = "yyyy-MM-dd";
public void setFormat(String format) {
this.format = format;
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
System.out.println("text: " + text);
SimpleDateFormat sdf = new SimpleDateFormat(format);
try {
Date d = sdf.parse(text);
this.setValue(d);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
(2)将自定义属性编辑器注册到Spring中。
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date" value="org.cellphone.uc.DatePropertyEditor"/>
</map>
</property>
</bean>
在配置文件中引入类型为org.springframework.beans.factory.config.CustomEditorConfigurer的bean,并在属性customEditors中加入自定义的属性编辑器,其中key为属性编辑器所对应的类型。通过这样的配置,当Spring在注入bean的属性时一旦遇到了java.util.Date类型的属性会自动调用自定义的DatePropertyEditor解析器进行解析,并用解析结果代替配置属性进行注入。
注册Spring自带的属性编辑器 CustomDateEditor
通过注册Spring自带的属性编辑器CustomDateEditor,具体步骤如下:
(1)定义属性编辑器。
public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}
}
(2)注册到Spring中。
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="org.cellphone.uc.DatePropertyEditorRegistrar"></bean>
</list>
</property>
</bean>
通过在配置文件中将自定义的DatePropertyEditorRegistrar注册进入org.springframework.beans.factory.config.CustomEditorConfigurer 的 propertyEditorRegistrars 属性中,可以具有与方法 1 同样的效果。
我们了解了自定义属性编辑器的使用,但是,似乎这与本节中围绕的核心代码beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()))并无联系,因为在注册自定义属性编辑器的时候使用的是 ResourceEditorRegistrar 的 registerCustomEditors 方法,而这里使用的是ConfigurableListableBeanFactory的addPropertyEditorRegistrar方法。
我们不妨深入探索一下 ResourceEditorRegistrar 的内部实现,在 ResourceEditorRegistrar 中,我们最关心的方法是registerCustomEditors 。
/**
* Populate the given {@code registry} with the following resource editors:
* ResourceEditor, InputStreamEditor, InputSourceEditor, FileEditor, URLEditor,
* URIEditor, ClassEditor, ClassArrayEditor.
* <p>If this registrar has been configured with a {@link ResourcePatternResolver},
* a ResourceArrayPropertyEditor will be registered as well.
* @see org.springframework.core.io.ResourceEditor
* @see org.springframework.beans.propertyeditors.InputStreamEditor
* @see org.springframework.beans.propertyeditors.InputSourceEditor
* @see org.springframework.beans.propertyeditors.FileEditor
* @see org.springframework.beans.propertyeditors.URLEditor
* @see org.springframework.beans.propertyeditors.URIEditor
* @see org.springframework.beans.propertyeditors.ClassEditor
* @see org.springframework.beans.propertyeditors.ClassArrayEditor
* @see org.springframework.core.io.support.ResourceArrayPropertyEditor
*/
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
doRegisterEditor(registry, Resource.class, baseEditor);
doRegisterEditor(registry, ContextResource.class, baseEditor);
doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));
ClassLoader classLoader = this.resourceLoader.getClassLoader();
doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));
if (this.resourceLoader instanceof ResourcePatternResolver) {
doRegisterEditor(registry, Resource[].class,
new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
}
}
/**
* Override default editor, if possible (since that's what we really mean to do here);
* otherwise register as a custom editor.
*/
private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
if (registry instanceof PropertyEditorRegistrySupport) {
((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
}
else {
registry.registerCustomEditor(requiredType, editor);
}
}
在 doRegisterEditor 函数中,可以看到在之前提到的自定义属性中使用的关键代码: registry.registerCustomEditor(requiredType, editor),回过头来看 ResourceEditorRegistrar 类的 registerCustomEditors方法的核心功能,其实无非是注册了一系列的常用类型的属性编辑器,例 如,代码 doRegisterEditor(registry,Class.class, new ClassEditor(classLoader))实现的功能就是注册 Class类对应的属件编辑器。那么,注册后,一旦某个实体bean中存在一些Class类型的属性, 那么Spring会调用ClassEditor将配置中定义的String类型转换为Class类型并进行陚值。
分析到这里,我们不禁有个疑问,虽说ResourceEditorRegistrar类的registerCustomEditors方法实现了批量注册的功能,但是beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())仅仅是注册了 ResourceEditorRegistrar 实例,却并没有调用ResourceEditorRegistrar 的registerCustomEditors方法进行注册,那么到底是在什么时候进行注册的呢?进一步查看 ResourceEditorRegistrar 的 registerCustomEditors 方法的调用层次结构,如下图所示。
发现在AbstractBeanFactory中的registerCustomEditors方法中被调用过,继续查看AbstractBeanFactory中的registerCustomEditors方法的层次结构,如下图所示。
其中我们看到一个方法是我们熟悉的,就是AbstractBeanFactory类中的initBeanWrapper 方法,这是在bean初始化时使用的一个方法,之前巳经使用过大量的篇幅进行讲解,主要是在将BeanDefinition转换为BeanWrapper后用于对属件的填充。到此,逻辑已经明了,在bean的初始化后会调用ResourceEditorRegistrar的registerCustomEditors方法进行批量的通用属性编辑器注册。注册后,在属性填充的环节便可以直接让Spring使用这些编辑器进行属性的解析了。
既然提到了 BeanWrapper,这里也有必要强调下,Spring中用于封装bean的是BeanWrapper 类型,而它又间接继承了 PropertyEditorRegistry类型,也就是我们之前反复看到的方法参数 PropertyEditorRegistry registry,其实大部分情况下都是 BeanWrapper,对于 BeanWrapper 在 Spring 中的默认实现是 BeanWrapperlmpl,而 BeanWrapperlmpl 除了实现 BeanWrapper 接门外还继承了 PropertyEdhorRegistrySupport,在 PropertyEditorRegistrySupport 中有这样一个方法:
/**
* Actually register the default editors for this registry instance.
*/
private void createDefaultEditors() {
this.defaultEditors = new HashMap<>(64);
// Simple editors, without parameterization capabilities.
// The JDK does not contain a default editor for any of these target types.
this.defaultEditors.put(Charset.class, new CharsetEditor());
this.defaultEditors.put(Class.class, new ClassEditor());
this.defaultEditors.put(Class[].class, new ClassArrayEditor());
this.defaultEditors.put(Currency.class, new CurrencyEditor());
this.defaultEditors.put(File.class, new FileEditor());
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
this.defaultEditors.put(InputSource.class, new InputSourceEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
this.defaultEditors.put(Path.class, new PathEditor());
this.defaultEditors.put(Pattern.class, new PatternEditor());
this.defaultEditors.put(Properties.class, new PropertiesEditor());
this.defaultEditors.put(Reader.class, new ReaderEditor());
this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
this.defaultEditors.put(URI.class, new URIEditor());
this.defaultEditors.put(URL.class, new URLEditor());
this.defaultEditors.put(UUID.class, new UUIDEditor());
this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());
// Default instances of collection editors.
// Can be overridden by registering custom instances of those as custom editors.
this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
// Default editors for primitive arrays.
this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
// The JDK does not contain a default editor for char!
this.defaultEditors.put(char.class, new CharacterEditor(false));
this.defaultEditors.put(Character.class, new CharacterEditor(true));
// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
// The JDK does not contain default editors for number wrapper types!
// Override JDK primitive number editors with our own CustomNumberEditor.
this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));
// Only register config value editors if explicitly requested.
if (this.configValueEditorsActive) {
StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
this.defaultEditors.put(String[].class, sae);
this.defaultEditors.put(short[].class, sae);
this.defaultEditors.put(int[].class, sae);
this.defaultEditors.put(long[].class, sae);
}
}
具体的调用方法我们就不去深究了,但是至少通过这个方法我们已经知道了在Spring中定义了上面一系列常用的属性编辑器使我们可以方便地进行配置。如果我们定义的bean中的某个属性的类型不在上面的常用配置中的话,才需要我们进行个性化属性编辑器的注册。
添加 ApplicationContextAwareProcessor 处理器
了解了属性编辑器的使用后,接下来我们继续通过 AbstractApplicationContext 的 prepareBeanFactory 方法的主线来进行函数跟踪。对于beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this))其实主要目的就是注册个 BneaPostProcessor,而真正的逻辑还是在 ApplicationContextAwareProcessor 中。
ApplicationContextAwareProcessor 实现 BeanPostProcessor 接口,我们回顾下之前讲过的内容,在bean实例化的时候,也就是Spring激活bean的init-method的前后,会调用BeanPostProcessor 的 postProcessBeforelnitialization 方法和 postProcessAfterlnitialization 方法。
问样,对于ApplicationContextAwareProcessor我们也关心这两个方法。
对于postProcessAfterlnitialization 方法,在ApplicationContextAwareProcessor 中并没有做过多逻辑处理。
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
那么,我们重点看一下postProcessBeforelnitialization 方法。
@Override
@Nullable
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
postProcessBeforelnitialization 方法中调用了invokeAwareInterfaces。从invokeAwareInterfaces方法中,我们或许已经或多或少了解了Spring的用意,实现这些Aware接口的bean在被初始化之后,可以取得一些对应的资源。
设置忽略依赖
当 Spring 将 ApplicationContextAwareProcessor 注册后,那么在 invokeAwarelnterfaces 方法中间接调用的 Aware 类已经不是普通的 bean 了,如 ResourceLoaderAware、ApplicationEventPublisher Aware 等,那么当然需要在Spring做bean的依赖注入的时候忽略它们。而ignoreDependencylnterfece的作用正是在此。
// 设置了几个忽略自动装配的接口
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
注册依赖
Spring中有了忽略依赖的功能,当然也必不可少地会有注册依赖的功能。
// 设置了几个自动装配的特殊规则
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
当注册了依赖解析后,例如当注册了对BeanFactory.class的解析依赖后,当bean的属性注入的时候,一旦检测到属性为BeanFactory类型便会将beanFactory的实例注人进去。