1、IOC容器启动与Bean生命周期源码分析

1、new ClassPathXmlApplicationContext初始化过程

ClassPathXmlApplicationContext类有多个重载的构造器,但最终都是调用同一个构造器实现的,如下图所示。

在这里插入图片描述

该构造器有三个参数,分别是:

  • 配置文件路径数组

  • 是否刷新上下文

  • 父容器

该构造器相当于做了三件事,分别是:

  • Super(parent)

  • setConfigLocations(configLocations);

  • refresh()

下面分三个部分来详细介绍。

1、Super(parent)

既然看到了super关键字,那就不妨看看ClassPathXmlApplicationContext与ApplicationContext之间的关系吧,如下图所示。

在这里插入图片描述

单独把ClassPathXmlApplicationContext和ApplicationContext拎出来的话,就是下面这个样子。如下图所示。

在这里插入图片描述

而super(parent)这句代码,一直往上层super,直到AbstractApplicationContext类就不super了,如下所示。

在这里插入图片描述

this()是调用该类的无参构造器,该构造器做了如下的事:

this.resourcePatternResolver = getResourcePatternResolver();

其实就是设置一个资源解析器。

而setParent(parent)是设置父容器,因为我们没有设置父容器,所以这里的parent是null。

super(parent)到这里就介绍完毕。

2、setConfigLocations(configLocations)

该方法的代码如下图所示。

在这里插入图片描述

点进resolvePath方法里面,如下图所示。

在这里插入图片描述

该方法的作用是解析给定的路径,必要时用相应的环境属性值替换占位符。

getEnvironment方法创建了一个StandardEnvironment对象。

而resolveRequiredPlaceholders最终是到了这里,如下图所示。

在这里插入图片描述

由此可以知道,该方法的作用就是解析配置文件路径中的${}变量。如果我们的配置文件路径没有变量,则这个方法就直接返回原值。

setConfigLocations(configLocations)到这里就介绍完毕。

3、refresh()

整个refresh方法如下所示。

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// 上下文刷新前做一些准备工作
		prepareRefresh();
		// 告诉子类刷新内部bean工厂
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		// 准备在此上下文中使用的bean工厂				 
		prepareBeanFactory(beanFactory);

		try {
			// 设置beanFactory的后置处理
			postProcessBeanFactory(beanFactory);
			// 调用BeanFactory的后处理器,这些后处理器是在bean定义中向容器注册的
			invokeBeanFactoryPostProcessors(beanFactory);
			// 注册拦截bean创建的bean后处理器
			registerBeanPostProcessors(beanFactory);
			// 初始化此上下文的消息源
			initMessageSource();
			//初始化广播事件
			initApplicationEventMulticaster();
			//初始化其他特殊bean
			onRefresh();
			// 注册监听器
			registerListeners();
			// 实例化所有的(非lazy-init)单例
			finishBeanFactoryInitialization(beanFactory);
			//最后一步:发布相应的事件,结束refresh过程
			finishRefresh();
		}catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}
			destroyBeans();
			cancelRefresh(ex);
			throw ex;
		}finally {
			resetCommonCaches();
		}
	}
}
1、prepareRefresh

上下文刷新前做一些准备工作。具体做什么准备工作呢?

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());
		}
	}

	// 在上下文环境中初始化所有的占位符属性资源,实际上该方法一行代码都没有
	initPropertySources();

	// ConfigurablePropertyResolver#setRequiredProperties方法可将一些属性标记为required,validateRequiredProperties就是来验证这些required的属性是否为null
	getEnvironment().validateRequiredProperties();

	// 下面几行代码是关于事件和监听器的
	if (this.earlyApplicationListeners == null) {
		this.earlyApplicationListeners=new LinkedHashSet<>(this.applicationListeners);
	}else {
		this.applicationListeners.clear();
		this.applicationListeners.addAll(this.earlyApplicationListeners);
	}
	this.earlyApplicationEvents = new LinkedHashSet<>();
}

到这里prepareRefresh就讲完了。

2、obtainFreshBeanFactory

获取新鲜的bean工厂。
告诉子类刷新内部bean工厂。
obtainFreshBeanFactory方法内部调用了refreshBeanFactory()方法来生成bean工厂,然后加载bean声明注册到这个bean工厂里(bean工厂内部维护了一个concurrentMap), 最后返回这个bean工厂。
AbstractRefreshableApplicationContext.refreshBeanFactory方法如下。

@Override
protected final void refreshBeanFactory() throws BeansException {
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
		// new一个DefaultListableBeanFactory对象,如果有父工厂会将父工厂放置进去		
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());

		//个性化bean工程主要是调用了两个set方法
		// beanFactory.setAllowBeanDefinitionOverriding(
		// this.allowBeanDefinitionOverriding);
		// beanFactory.setAllowCircularReferences(this.allowCircularReferences);
		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);
	}
}

1、加载bean声明的一系列过程

注意:加载bean声明的详细过程可以不用看,不影响spring源码运行流程的分析。
BeanDefinition接口的子类如下图所示,下文中创建的BeanDefinition实际上用的就是GenericBeanDefinition。

在这里插入图片描述

AbstractXmlApplicationContext .loadBeanDefinitions方法如下。

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// 为给定的BeanFactory创建一个新的XmlBeanDefinitionReader
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

	// 使用此上下文的资源加载环境配置bean定义阅读器
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
	// 阅读器初始化,主要是reader.setValidating(true),设置验证
	initBeanDefinitionReader(beanDefinitionReader);
	// 加载bean声明
	loadBeanDefinitions(beanDefinitionReader);
}

loadBeanDefinitions方法最终是走到了AbstractBeanDefinitionReader.loadBeanDefinitions,该方法的形参为

String location, @Nullable Set<Resource> actualResources

实参分别是1.1.2节中的configLocations(configLocations是个数组,遍历即可)和null。

AbstractBeanDefinitionReader.loadBeanDefinitions方法的关键代码如下。

// 将string类型的配置文件路径,转化为Resource对象
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);

又经过层层调用,最终到了XmlBeanDefinitionReader. loadBeanDefinitions(EncodedResource encodedResource)方法。
这里的EncodedResource是对Resource包装了一层,实际上并没有进行编码。

该方法有如下关键代码:

InputStream inputStream = encodedResource.getResource().getInputStream());
InputSource inputSource = new org.xml.sax.InputSource(inputStream);
return doLoadBeanDefinitions(InputSource, encodedResource.getResource());

doLoadBeanDefinitions方法关键代码如下:

// 将resource转为xml Document对象
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource); 

registerBeanDefinitions方法经常层层调用,最终到了DefaultBeanDefinitionDocumentReader.parseDefaultElement方法。
可以看到配置文件的转化过程:string configLocation->Resource-> EncodedResource->InputSteam-> InputSource->Document->Element

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	// 处理Import元素
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		importBeanDefinitionResource(ele);
	}
	// 处理alias元素
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		processAliasRegistration(ele);
	}
	// 处理bean元素
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
	// 处理beans元素,递归调用
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// recurse
		doRegisterBeanDefinitions(ele);
	}
}

processBeanDefinition就是真正创建一个BeanDefinition,如何将xml元素解析成BeanDefinition呢?其实是通过BeanDefinitionParseDelegate类的parseBeanDefinitionElement方法完成的,这个类中包含了对各种bean定义规则的处理,比如bean标签如何处理,id,name,alias等属性如何处理。

AbstractBeanDefinition类中定义了一些变量用于存放解析完成的bean声明信息,比如:

private ConstructorArgumentValues constructorArgumentValues;
private MutablePropertyValues propertyValues;

分别存放解析后的构造器信息和属性信息。
最终将解析完成的BeanDefinition对象放到BeanDefinitionHolder对象中,BeanDefinitionHolder对象还包含了bean的名称和别名。如何解析的具体实现细节就不深究了。

2、注册bean声明

创建BeanDefinitionden之后,就要调用DefaultListableBeanFactory.registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法注册bean声明,然后再注册别名。
关键代码如下,registry就是DefaultListableBeanFactory对象:

registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
registry.registerAlias(beanName, alias);

registry.registerBeanDefinition方法内部的核心代码如下:

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);// 存储所有bean的名称
this.beanDefinitionMap.put(beanName, beanDefinition);

而DefaultListableBeanFactory的父类SimpleAliasRegistry维护了一个aliasMap变量存储别名:

private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);

需要注意的是,源码中对于BeanName的处理,
Xml中bean标签的id属性beanName,name属性(可逗号、分号分隔)为bean的别名。
如果id为空,则取第一个别名为beanName。
如果name也为空,则spring自动创建一个beanName。自动生成beanName的代码是BeanDefinitionReaderUtils.generateBeanName方法,代码如下所示。
最后,再将Bean的className(带包名)作为bean的别名。

public static String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) throws BeanDefinitionStoreException {
	String generatedBeanName = definition.getBeanClassName();
	if (generatedBeanName == null) {
		if (definition.getParentName() != null) {
			generatedBeanName = definition.getParentName() + "$child";
		}
		else if (definition.getFactoryBeanName() != null) {
			generatedBeanName = definition.getFactoryBeanName() + "$created";
		}
	}
	if (!StringUtils.hasText(generatedBeanName)) {
		throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
				"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
	}
	if (isInnerBean) {
		// Inner bean: generate identity hashcode suffix.
		return generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
	}
	// Top-level bean: use plain class name with unique suffix if necessary.
	return uniqueBeanName(generatedBeanName, registry);
}

到这里,加载bean声明就介绍完毕了。

3、prepareBeanFactory

准备在此上下文中使用的bean工厂。
BeanFactory是一个接口,而该接口的子类如下,其中DefaultListableBeanFactory就是前面创建的Bean工厂。

在这里插入图片描述

PrepareBeanFactory为容器配置classLoader、propertyEditor、BeanExpressionResolver和BeanPostProcessor等,为容器的启动做好准备工作。
注意:要将BeanPostProcessor和BeanFactoryProcessor区分开。
而容器关闭时,也要做一系列工作,AbstractApplicationContext的doClose方法先将Bean逐个关闭,再关闭容器自身。

简单介绍一下Bean的生命周期。

  1. Bean实例的创建

  2. 为Bean实例设置属性

  3. 调用Bean的初始化方法

  4. 通过IOC容器使用Bean

  5. 当容器关闭时,调用Bean的销毁方法

4、postProcessBeanFactory

设置bean工厂的后置处理器。其实该方法一行代码也没有,啥也没设置。
但是,我下面要说的是BeanFactoryPostProcessor。

BeanFactoryPostProcessor是Spring提供的一种容器扩展机制,允许新增或修改已经注册到容器的BeanDefinition所保存的信息(即允许修改Bean的配置元数据)。
实现BeanFactoryPostProcessor接口可以自定义BeanFactoryPostProcessor,如果有多个,可以实现Ordered接口进行排序。

当然,spring提供了很多BeanFactoryPostProcessor,下面来讲几个重要的:

  • PropertyPlaceholderConfigurer(5.2版本后用PropertySourcesPlaceholderConfigurer,更灵活):用于替换占位符,比如jdbc配置文件

  • PropertyOverrideConfigurer:直接替换值,比如将xml文件中加密字符串替换为解密后的值

  • CustomEditorConfigurer:辅助性地将后期要用到的信息注册到容器,对BeanDefinition没有做任何变动。

PropertySourcesPlaceholderConfigurer的使用如下所示。

<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
	<property name="locations">
		<list>
			<value>jdbc.properties</value>
			<value>mail.properties</value>
		</list>
	</property>
</bean>

<bean id="dataSourceMailDelegate" class="com.bobo.group.lifecycle.DataSourceMailDelegate">
	<property name="dbUsername" value="${db.username}" />
	<property name="dbPassword" value="${db.password}" />
	<property name="dbType" value="${db.type}" />
	<property name="dbUrl" value="${db.url}" />
	<property name="mailSender" value="${mail.sender}" />
	<property name="mailPassword" value="${mail.password}" />
	<property name="mailProtocol" value="${mail.protocol}" />
</bean>

占位符除了可以使用自定义配置文件里的变量,还可以使用系统属性。
;另外,还可以通过

<context:property-placeholder location="jdbc.properties,mail.properties"></context:property-placeholder>

来定义用于占位符替换的文件位置。

PropertyOverrideConfigurer的使用如下所示。

可以用

<context:property-override location="override.properties"></context:property-override>

来定义用于属性覆盖文件位置。
该文件的语法如下:
BeanName.property=value(支持复合属性)
比如:dataSourceMailDelegate.dbUsername=bobo
这样就可以将beanName为dataSourceMailDelegate的bean的dbUsername属性值替换为bobo了。

CustomEditorConfigurer的使用如下所示。
不管是xml配置,还是其它形式,都是通过字符串形式配置的,但程序中都是各种类型的对象,那么就需要将字符串转化为具体的对象。
Spring内部通过javaBean的PropertyEditor来完成string类型到其它类型的转换。
PropertyEditor子类如下图所示。

在这里插入图片描述

自定义PropertyEditor三步:

  1. 自定义PropertyEditor
  2. 自定义PropertyEditor注册器
  3. 将自定义PropertyEditor注册器配置到CustomEditorConfigurer中(也可以忽略第2步,使用CustomEditorConfigurer的customEditors属性)

整个代码如下所示。

import java.beans.PropertyEditorSupport;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class DatePropertyEditor extends PropertyEditorSupport {
    private String pattern;
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        try {
            setValue(format.parse(text));
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
    // 省略getter/setter
}

import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import java.beans.PropertyEditor;
public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar {
    private PropertyEditor propertyEditor;
    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(java.util.Date.class,propertyEditor);
}
// 省略getter/setter
}

<!--将datePropertyEditor的注册器,配置到CustomEditorConfigurer中去-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	<property name="propertyEditorRegistrars">
		<list>
			<ref bean="datePropertyEditorRegistrar" />
		</list>
	</property>
</bean>
<!--配置datePropertyEditor的注册器-->
<bean id="datePropertyEditorRegistrar" class="com.bobo.group.lifecycle.propertyeditor.DatePropertyEditorRegistrar">
	<property name="propertyEditor" ref="datePropertyEditor" />
</bean>
<!--配置datePropertyEditor-->
<bean id="datePropertyEditor" class="com.bobo.group.lifecycle.propertyeditor.DatePropertyEditor">
	<property name="pattern" value="yyyy/MM/dd HH/mm/ss" />
</bean>

2、 ApplicationContext.getBean实例化过程

1、Bean生命周期图

Bean生命周期如下图所示。

在这里插入图片描述

2、以getBean方法为入口一路跟踪

在这里插入图片描述

Application::getBean方法有多个重载,不同的getBean方法其内部逻辑是略有差异的,这里我以getBean(Class requiredType)方法为入口,对bean实例化过程进行分析。

首先,从getBean方法入手。首先一路跟踪到DefaultListableBeanFactory的resolveBean方法,如下图。

在这里插入图片描述

NamedBeanHolder包含了两个属性:beanName和bean真正的实例。

进入resolveNamedBean方法,如下图所示。

在这里插入图片描述

因为这里是按类型获取bean,而一个类型可能对应有多个beanName,上图就对不同情况分别做了处理,我们主要看只有一个beanName时的情况。

继续跟踪到AbstractBeanFactory::doGetBean方法,该方法返回bean真正的实例,如下图所示。

在这里插入图片描述

doGetBean方法的流程与核心代码如下所示。

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
	final String beanName = transformedBeanName(name);
	Object bean;
	// 从三级缓存中获取bean
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}
	else {
		// 如果当前bean属于正在创建的原型Bean,则抛出异常
		if (isPrototypeCurrentlyInCreation(beanName)) {
		}
		BeanFactory parentBeanFactory = getParentBeanFactory();
		if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
		}
		if (!typeCheckOnly) {
		// typeCheckOnly为false。将bean标记为已创建,并且将BeanDefinition标记为需要合并
			markBeanAsCreated(beanName);
		}
		// 如果RootBeanDefinition已存在且不需要合并,则直接返回。否则,强行合并
		// 因为spring有各种BeanDefinition,为了能统一处理,所以就统一合并为RootBeanDefinition
		final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
		// 检查RootBeanDefinition是否为抽象的,如果是则抛出异常
		checkMergedBeanDefinition(mbd, beanName, args);
		// 初始化depends on bean
		String[] dependsOn = mbd.getDependsOn();
		if (dependsOn != null) {
		}
		if (mbd.isSingleton()) {
			// 单例作用域
			sharedInstance = getSingleton(beanName, () -> {
				return createBean(beanName, mbd, args);
			});
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
		}
		else if (mbd.isPrototype()) { // 原型作用域 	}
		else { // 其它作用域 	}
	}
	// 检查requiredType与生成的bean类型是否匹配
	if (requiredType != null && !requiredType.isInstance(bean)) { }
	return (T) bean;
}

可以看到,这里根据bean的不同作用域而创建Bean的逻辑。这里重点看单例Bean的创建,并且createBean方法是重点,但我们先看getSingleton方法。

getSingleton方法的核心代码如下。

// 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	synchronized (this.singletonObjects) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null) {
	// 调用getObject方法创建, getObject方法的实现其实就是调用了createBean方法
			singletonObject = singletonFactory.getObject();
			newSingleton = true;
			
			if (newSingleton) {
				// 添加到一级缓存的同时,移除二级、三级缓存
				addSingleton(beanName, singletonObject);
			}
		}
		// 一级缓存中有则直接返回
		return singletonObject;
	}
}

createBean方法如下。

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){

	RootBeanDefinition mbdToUse = mbd;
	Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
	if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
		mbdToUse = new RootBeanDefinition(mbd);
		mbdToUse.setBeanClass(resolvedClass);
	}

	mbdToUse.prepareMethodOverrides();

	Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
	if (bean != null) {
		return bean;
	}

	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
	return beanInstance;
}

下面主要看看倒数第二行代码中的doCreateBean方法,这个是Bean实例化的核心方法,其它代码暂时先不看。

3、Bean实例化、填充、初始化总览

先来看一下Bean实例化过程中的日志打印。

在这里插入图片描述

AbstractAutowireCapableBeanFactory. doCreateBean方法如下。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) {
	// 实例化bean
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	final Object bean = instanceWrapper.getWrappedInstance();

	// 执行bean声明的合并:主要执行的是MergedBeanDefinitionPostProcessor,它是BeanPostProcessor的子接口
	applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
// 是单例 && 允许循环引用 && 当前单例正在创建
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
	isSingletonCurrentlyInCreation(beanName));
	// 这句代码是解决循环引用的核心。作用是:添加到三级缓存,addSingletonFactory方法的第二个参数是一个ObjectFactory,该ObjectFactory的产出正是前面刚刚实例化好的bean
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	
	Object exposedObject = bean;
	//填充bean,组装普通属性和依赖的bean
	populateBean(beanName, mbd, instanceWrapper);
	//初始化bean,组装Aware、BeanPostProcessor前置处理、初始化方法、BeanPostProcessor后置处理
	exposedObject = initializeBean(beanName, exposedObject, mbd);

	// 注册bean的销毁方法
	registerDisposableBeanIfNecessary(beanName, bean, mbd);

	return exposedObject;
}

下面来看一下MergedBeanDefinitionPostProcessor的子类。

在这里插入图片描述

AutowiredAnnotationBeanPostProcessor这个子类对@Autowired和@Value的支持起到了至关重要的作用,它会扫描@Autowired注解标记的属性,然后将它合并到RootBeanDefinition中去。
而RequiredAnnotationBeanPostProcessor主要支持的是@Required注解,但@Required注解从spring5.1版本后就过期了,建议使用带必选参数的构造方法来替代。

1、Bean实例化方法instantiateBean

下面从createBeanInstance方法出发来看bean实例化过程。

在这里插入图片描述

createBeanInstance最后一行代码调用的是instantiateBean方法,下面看看instantiateBean方法。

在这里插入图片描述

getInstantiationStrategy()用于获取初始化策略。

在这里插入图片描述

如果是普通的bean,则用的是SimpleInstantiationStrategy,如果是有方法注入的bean,则用CglibSubclassingInstantiationStrategy。

下面再看看SimpleInstantiationStrategy类的instantiate方法。

在这里插入图片描述

最后来看一下BeanUtils是怎么实例化Bean的。

在这里插入图片描述

2、Bean填充方法populateBean

填充Bean的属性。具体细节后续更新。

3、Bean初始化方法initializeBean

initializeBean方法代码如下。

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
	// 执行Aware接口的方法:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
	invokeAwareMethods(beanName, bean);

	Object wrappedBean = bean;
	// 执行前置BeanPostProcessor
	wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	
	// 执行初始化方法
	invokeInitMethods(beanName, wrappedBean, mbd);
	
	//执行后置BeanPostProcessor
	wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

	return wrappedBean;
}
4、BeanPostProcessor的作用

BeanPostProcessor是Bean实例化阶段强有力的扩展点。
BeanPostProcessor的概念容易与BeanFactoryPostProcessor的概念混淆。但只要记住
BeanPostProcessor是存在于对象实例化阶段,而BeanFactoryPostProcessor则是存在于容器启动阶段。
与BeanFactoryPostProcessor通常会处理容器内所有符合条件的BeanDefinition类似,Bean-
PostProcessor会处理容器内所有符合条件的实例化后的对象实例。该接口声明了两个方法,分别在两个不同的时机执行,如下所示。

public interface BeanPostProcessor{
	Object postProcessBeforeInitialization(Object bean, String beanName) ;
	Object postProcessAfterInitialization(Object bean, String beanName);
}

BeanPostProcessor的作用:

  • Spring Aop来为对象生成相应的代理对象

  • 可以用来解密某些字段

自定义BeanPostProcessor如下。

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof LifecycleCallbacksBean){
            System.out.println("-- MyBeanPostProcessor BeforeInitialization --");
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof LifecycleCallbacksBean){
            System.out.println("-- MyBeanPostProcessor AfterInitialization --");
        }
        return bean;
    }
}

<bean class="com.bobo.group.lifecycle.MyBeanPostProcessor"></bean>
5、Bean实例化过程中如何解决循环依赖?

在这里插入图片描述

如上图,解决循环依赖的关键就是Spring的第三级缓存+Java反射。
当采用构造器注入时,Spring无法解决基于构造器的循环依赖,这是因为new对象的时候就被堵住了,典型的先有鸡还是先有蛋问题,Spring会直接抛出异常。
当采用基于setter方法且是原型bean时,Spring也无法解决循环依赖,这样会导致一直new对象直到OOM为止,因此Spring直接抛出异常。
当单例Bean达到可用状态时(即实例化、填充、初始化都完成了),会将该单例Bean添加到一级缓存中去(同时移除二、三级缓存),下次再获取该单例Bean时,就会从一级缓存中获取,因此单例Bean始终是同一个对象。

Spring三级缓存:
三级缓存是互斥的,同一时间段bean实例只能存在于其中一个缓存中。
一级缓存是暴露给用户的,二级、三级缓存是给spring内部使用的。
一级缓存存放的是可以使用的bean实例,而二、三级缓存存放的是早期的bean实例(半成品,只new出了对象,但还未给属性赋值)
二级缓存有什么用,可以去掉吗?
三级缓存存放的是单例工厂,通过单例工厂的getObject方法可以产出bean实例。只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题,因为如果没有AOP代理的话,那么getObject方法每次返回的都是同一个对象,而有AOP代理时,getObject方法每次返回的都是不同的代理对象,因此第一次调用getObject方法后,需要将此代理对象放入二级缓存中,如下图所示。

在这里插入图片描述

2、基于注解的容器配置

@Required 不建议使用,可以用带参构造方法代替
@Autowired
@Primary
@Qualifier
@Resource
@Value

在这里插入图片描述

如上所示,DefaultListableBeanFactory类有一个属性allowBeanDefinitionOverriding默认为true,表示当出现beanName相同的两个bean声明时,后者可以覆盖前者。
如果是基于xml的配置,则要求beanName相同的两个bean在不同的xml中。

如果有多个beanName不同,但类型相同的bean时,如果按类型自动装配会出现问题,这时有以下几种解决方案:

  • 使用@Autowired时,由于@Autowired是先按类型装配,再按名称装配,如果按类型找到多个,但是按名称只找到一个的话,是没有问题的;

  • 使用集合,比如List去接收多个Bean,List annotationBean;

  • 设置bean标签的primary属性为true(或者使用@Primary注解),primary意思为主要的,在类型相同的多个bean中选一个主要的

  • @Autowired和@Qualifier搭配使用,Qualifier是合格的意思,在类型相同的多个bean中选一个合格者,@Qualifier(“com.bobo.group.annotatiton.AnnotationBean#1”), com.bobo.group.annotatiton.AnnotationBean#1是spring为bean生成的beanName,标识符从0开始

3、父子容器

父子容器特点:

  1. 父容器和子容器是相互隔离的,他们内部可以存在名称相同的bean;
  2. 子容器可以访问父容器中的bean,而父容器不能访问子容器中的bean;
  3. 调用子容器的getBean方法获取bean的时候,会沿着当前容器开始向上面的容器进行查找,直到找到对应的bean为止;
  4. 子容器中可以通过任何注入方式注入父容器中的bean,而父容器中是无法注入子容器中的bean,原因是第2点;

注意事项:

  • BeanFactory接口,是spring容器的顶层接口,这个接口中的方法是支持容器嵌套结构查找的,比如我们常用的getBean方法,就是这个接口中定义的,调用getBean方法的时候,会沿着当前容器向上查找,直到找到满足条件的bean为止;
  • 而ListableBeanFactory这个接口中的方法是不支持容器嵌套结构查找的;
  • BeanFactoryUtils这个类中提供了很多静态方法,有很多支持向上查找的方法,名称中包含有Ancestors的都是支持向上查找的;

springmvc中为什么需要用到父子容器?

  • 通常我们使用springmvc的时候,采用3层结构,controller层,service层,dao层;父容器中会包含dao层和service层,而子容器中包含的只有controller层;这2个容器组成了父子容器的关系,controller层通常会注入service层的bean;

  • 采用父子容器可以避免service层注入controller层的bean,导致整个依赖层次比较混乱;

  • 父容器和子容器的需求也不一样,比如父容器中需要有事务的支持,会注入一些支持事务的扩展组件,而子容器中controller完全用不到这些,对这些并不关心;

  • 利用父子容器特性可以将一些不关心的东西隔开,可以有效的避免一些不必要的错误,而父子容器加载的速度也会快一些;

4、BeanFactory与ApplicationContext比较

功能BeanFactoryApplicationContext
Bean实例化支持支持
集成生命周期管理不支持支持
自动BeanPostProcessor注册编程方式手动注册支持
自动BeanFactoryPostProcessor注册编程方式手动注册支持
方便的MessageSource访问(用于内部化)不支持支持
内置的ApplicationEvent发布机制不支持

ApplicationContext较之BeanFactory特有的一些特性:

  • 统一资源加载策略
  • 国际化(I18n)信息支持
    bean,导致整个依赖层次比较混乱;
  • 父容器和子容器的需求也不一样,比如父容器中需要有事务的支持,会注入一些支持事务的扩展组件,而子容器中controller完全用不到这些,对这些并不关心;
  • 利用父子容器特性可以将一些不关心的东西隔开,可以有效的避免一些不必要的错误,而父子容器加载的速度也会快一些;

4、BeanFactory与ApplicationContext比较

功能BeanFactoryApplicationContext
Bean实例化支持支持
集成生命周期管理不支持支持
自动BeanPostProcessor注册编程方式手动注册支持
自动BeanFactoryPostProcessor注册编程方式手动注册支持
方便的MessageSource访问(用于内部化)不支持支持
内置的ApplicationEvent发布机制不支持

ApplicationContext较之BeanFactory特有的一些特性:

  • 统一资源加载策略
  • 国际化(I18n)信息支持
  • 容器内事件发布
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐