上一節(jié)分析了Spring實例化單例bean的準備工作姓赤,而且已經(jīng)接觸到了真正創(chuàng)建bean的方法doCreateBean庵寞,本小節(jié)分析Spring是如何實例化bean的枉证。
引言以躯,doCreateBean方法簡析
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
// Instantiate the bean.
// ① 實例化bean
BeanWrapper instanceWrapper = null;
// 注意factoryBeanInstanceCache是ConcurrentMap,remove方法會返回刪除的鍵值(如果不存在返回null)
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 如果factoryBeanInstanceCache沒有緩存對應(yīng)的BeanWrapper,則重新創(chuàng)建bean實例
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
// ② 允許MergedBeanDefinitionPostProcessor后處理器修改已合并的bean定義。
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// ③ 提前緩存ObjectFactory以解決bean之間的循環(huán)依賴
// mbd.isSingleton()->是否單例
// allowCircularReferences->是否允許循環(huán)依賴
// isSingletonCurrentlyInCreation->該bean是否創(chuàng)建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
// ④ 初始化bean實例 這里大家要與第①步區(qū)分開,到這里bean已經(jīng)完成了實例化,但是還沒有完成初始化的操作,例如bean的屬性填充
Object exposedObject = bean;
try {
// 填充bean屬性
populateBean(beanName, mbd, instanceWrapper);
// 初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
// ⑤ 循環(huán)依賴檢查
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
// ⑥ 根據(jù)bean的作用域注冊bean
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
// ⑦ 返回bean實例
return exposedObject;
}
步驟如下:
- ① 實例化bean
- ② 允許MergedBeanDefinitionPostProcessor后處理器修改已合并的bean定義湖饱。
- ③ 提前緩存ObjectFactory以解決bean之間的循環(huán)依賴
- ④ 初始化bean實例 這里大家要與第①步區(qū)分開,到這里bean已經(jīng)完成了實例化,但是還沒有完成初始化的操作,例如bean的屬性填充
- ⑤ 循環(huán)依賴檢查
- ⑥ 根據(jù)bean的作用域注冊bean
- ⑦ 返回bean實例
這些步驟中涉及的知識點很多掖蛤,我們逐步分析。
1.實例化bean
createBeanInstance方法完成了對bean的實例化操作井厌,打開該方法蚓庭。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 確保此時beanClass已經(jīng)被解析
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// beanClass不為空,且beanClass的修飾符為不為public,且不允許訪問非公共構(gòu)造函數(shù)和方法,則拋出異常
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
// ① Spring5.0新增的實例化策略,如果設(shè)置了該策略,將會覆蓋構(gòu)造方法和工廠方法實例化策略
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// ② 如果有工廠方法的話,則使用工廠方法實例化bean
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// ③ 當創(chuàng)建一個相同的bean時,使用之間保存的快照
// 這里可能會有一個疑問,什么時候會創(chuàng)建相同的bean呢?
// ③-->① 單例模式: Spring不會緩存該模式的實例,那么對于單例模式的bean,什么時候會用到該實例化策略呢?
// 我們知道對于IoC容器除了可以索取bean之外,還能銷毀bean,當我們調(diào)用xmlBeanFactory.destroyBean(myBeanName,myBeanInstance),
// 銷毀bean時,容器是不會銷毀已經(jīng)解析的構(gòu)造函數(shù)快照的,如果再次調(diào)用xmlBeanFactory.getBean(myBeanName)時,就會使用該策略了.
// ③-->② 原型模式: 對于該模式的理解就簡單了,IoC容器不會緩存原型模式bean的實例,當我們第二次向容器索取同一個bean時,就會使用該策略了.
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
// 如果該bean已經(jīng)被解析過
if (resolved) {
// 使用已經(jīng)解析過的構(gòu)造函數(shù)實例化
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
// 使用默認無參構(gòu)造函數(shù)實例化
else {
return instantiateBean(beanName, mbd);
}
}
// ④ 確定需要使用的構(gòu)造函數(shù)
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// ⑤ 無任何的特殊處理,則使用默認的無參構(gòu)造函數(shù)實例化bean
return instantiateBean(beanName, mbd);
}
從該方法里我們看到了Spring實例化bean的策略:
- 工廠方法(實例工廠和靜態(tài)工廠)
- 構(gòu)造函數(shù)實例化(無參構(gòu)造和有參構(gòu)造)
- 通過實例提供者實例化(Spring5新增的實例化策略)
先從最簡單的無參構(gòu)造函數(shù)實例化分析致讥,因為其他的實例化策略,如有參構(gòu)造函數(shù)實例化會涉及到構(gòu)造函數(shù)解析器赞,該過程也是非常復(fù)雜垢袱,所以先分析最簡單的無參構(gòu)造函數(shù)實例化。也就是createBeanInstance方法中的第五步港柜,歷經(jīng)前幾個步驟的處理之后仍然無法實例化bean并返回其實例的話请契,那么就采用默認構(gòu)造函數(shù)實例化。
2.默認構(gòu)造函數(shù)實例化Bean
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
// 1夏醉、如果權(quán)限管理器不為空,需要校驗
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
getInstantiationStrategy().instantiate(mbd, beanName, parent),
getAccessControlContext());
}
else {
// 2爽锥、獲取實例化策略并實例化bean
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
// 3、實例并初始化BeanWrapper對象
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
- 獲取實例化策略
創(chuàng)建一個類的實例對象除了通過new關(guān)鍵字之外畔柔,還可以通過JDK的反射機制
或CGLIB動態(tài)代理
來創(chuàng)建對象實例氯夷,這也是Spring實例化bean的兩種策略。所以首先通過getInstantiationStrategy方法來獲取實例化bean的策略靶擦。從下圖中可以看到腮考,如果無特殊配置,Spring將采用CGLIB動態(tài)代理機制作為實例化bean的默認策略奢啥。
- 反射機制和CGLIB使用時機
Spring何時使用反射何時使用CGLIB創(chuàng)建bean的實例呢秸仙?答案很簡單嘴拢,如果沒有使用方法覆蓋(replace-method或lookup-method注入),則直接使用反射創(chuàng)建bean的實例桩盲;否則必須使用CGLIB機制。Spring通過instantiate方法來確定具體使用哪種機制席吴。
3. instantiate方法獲取實例化機制
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// 1赌结、如果沒有使用方法覆蓋(replace-method或lookup-method注入),則直接使用反射創(chuàng)建bean的實例
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
// 嘗試獲取已經(jīng)解析的構(gòu)造方法
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged((PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
}
else {
// 未能獲取到已經(jīng)解析過的構(gòu)造方法,則通過getDeclaredConstructor方法獲取構(gòu)造方法
constructorToUse = clazz.getDeclaredConstructor();
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
// 通過BeanUtils類實例化bean
return BeanUtils.instantiateClass(constructorToUse);
}
// 2孝冒、否則必須使用CGLIB實例化策略
else {
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
判斷的方式很簡單柬姚,通過BeanDefinition判斷有沒有replace-method或lookup-method注入即可;如果沒有則默認使用反射機制實例化bean庄涡,否則必須使用CGLIB實例bean量承。
4.反射機制實例化bean
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
try {
ReflectionUtils.makeAccessible(ctor);
// KotlinDetector,Spring5.0新增的類穴店,用于檢測Kotlin的存在和識別Kotlin類型撕捍。
return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
通過ctor.newInstance(args)
方法創(chuàng)建了Bean的實例,后續(xù)代碼已經(jīng)屬于JDK源碼泣洞,感興趣的同學(xué)可以自行分析忧风。
5.CGLIB實例bean
打開我們之前分析的test9(測試replace-method注入)和test10(測試replace-method注入),分析CGLIB實例化球凰。傳送:07--lookup-method和replace-method注入狮腿,這里我們只分析一下replace_method方法腿宰。
@Override
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
return instantiateWithMethodInjection(bd, beanName, owner, null);
}
@Override
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Constructor<?> ctor, @Nullable Object... args) {
// Must generate CGLIB subclass...
return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
}
public Object instantiate(@Nullable Constructor<?> ctor, @Nullable Object... args) {
// 1、生成增強子類
Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
Object instance;
// 2缘厢、實例化增強子類
if (ctor == null) {
instance = BeanUtils.instantiateClass(subclass);
}
else {
try {
Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
instance = enhancedSubclassConstructor.newInstance(args);
}
catch (Exception ex) {
throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
"Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
}
}
// 3吃度、設(shè)置回調(diào)
// SPR-10785: set callbacks directly on the instance instead of in the
// enhanced class (via the Enhancer) in order to avoid memory leaks.
Factory factory = (Factory) instance;
factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
return instance;
}
對于增強子類的實例化,依然采用了jdk的反射機制昧绣。我們回到測試類中规肴。查看生成的實例。
-
查看實例信息:
image.png
-
實例詳細信息
image.png
當代碼運行到originalDog.sayHello("輸出結(jié)果已經(jīng)被替換了夜畴。拖刃。。");
時贪绘,會被CglibSubclassingInstantiationStrategy類的intercept方法攔截兑牡。
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
// 1、獲取覆蓋方法信息
ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
Assert.state(ro != null, "ReplaceOverride not found");
// TODO could cache if a singleton for minor performance optimization
// 2税灌、實例化覆蓋方法
MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
// 3均函、調(diào)用覆蓋方法
return mr.reimplement(obj, method, args);
}
當代碼執(zhí)行到第三步時就會調(diào)用reimplement方法了。
/**
* @author: LiYanChao
* @create: 2018-09-06 00:02
*/
public class ReplaceDog implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("Hello, I am a white dog...");
Arrays.stream(args).forEach(str -> System.out.println("參數(shù):" + str));
return obj;
}
}
6.總結(jié)
到這里通過無參構(gòu)造方法實例化bean就分析完了菱涤,這里大家需要記住Spring實例化bean的方式以及何時使用何種方式苞也。如果使用了replace-method或lookup-method注入,則直接使用CGLIB實例化bean,否則直接使用反射實例化bean粘秆。