Spring IOC容器启动流程与Bean生命周期源码分析
文章目录1、IOC容器启动与Bean生命周期源码分析1、new ClassPathXmlApplicationContext初始化过程1、Super(parent)2、setConfigLocations(configLocations)3、refresh()1、prepareRefresh2、obtainFreshBeanFactory1、加载bean声明的一系列过程2、注册bean声明3、pr
文章目录
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的生命周期。
-
Bean实例的创建
-
为Bean实例设置属性
-
调用Bean的初始化方法
-
通过IOC容器使用Bean
-
当容器关闭时,调用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三步:
- 自定义PropertyEditor
- 自定义PropertyEditor注册器
- 将自定义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、父子容器
父子容器特点:
- 父容器和子容器是相互隔离的,他们内部可以存在名称相同的bean;
- 子容器可以访问父容器中的bean,而父容器不能访问子容器中的bean;
- 调用子容器的getBean方法获取bean的时候,会沿着当前容器开始向上面的容器进行查找,直到找到对应的bean为止;
- 子容器中可以通过任何注入方式注入父容器中的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比较
功能 | BeanFactory | ApplicationContext |
---|---|---|
Bean实例化 | 支持 | 支持 |
集成生命周期管理 | 不支持 | 支持 |
自动BeanPostProcessor注册 | 编程方式手动注册 | 支持 |
自动BeanFactoryPostProcessor注册 | 编程方式手动注册 | 支持 |
方便的MessageSource访问(用于内部化) | 不支持 | 支持 |
内置的ApplicationEvent发布机制 | 不支持 |
ApplicationContext较之BeanFactory特有的一些特性:
- 统一资源加载策略
- 国际化(I18n)信息支持
bean,导致整个依赖层次比较混乱; - 父容器和子容器的需求也不一样,比如父容器中需要有事务的支持,会注入一些支持事务的扩展组件,而子容器中controller完全用不到这些,对这些并不关心;
- 利用父子容器特性可以将一些不关心的东西隔开,可以有效的避免一些不必要的错误,而父子容器加载的速度也会快一些;
4、BeanFactory与ApplicationContext比较
功能 | BeanFactory | ApplicationContext |
---|---|---|
Bean实例化 | 支持 | 支持 |
集成生命周期管理 | 不支持 | 支持 |
自动BeanPostProcessor注册 | 编程方式手动注册 | 支持 |
自动BeanFactoryPostProcessor注册 | 编程方式手动注册 | 支持 |
方便的MessageSource访问(用于内部化) | 不支持 | 支持 |
内置的ApplicationEvent发布机制 | 不支持 |
ApplicationContext较之BeanFactory特有的一些特性:
- 统一资源加载策略
- 国际化(I18n)信息支持
- 容器内事件发布
更多推荐
所有评论(0)