20--Spring建立Bean的過程(二),無參建構函式(預設建構函式)例項化
上一章我們已經分析了Spring例項化bean的步驟,以及對例項化方式的解析,本章分析Spring使用無參建構函式例項化bean的過程。
在分析之前先來了解一下Spring例項化bean的策略
- JDK的反射機制
- CGLIB動態代理
對於反射機制,如果拿到其建構函式,引數等相關資訊,就可以通過反射直接建立其例項,但是為什麼Spring提供了兩種例項化的方式呢,答案就是我們之前分析的方法注入。14–Spring lookup-method注入和replace-method注入(二),如果當前bean是通過lookup-method或者replace-method話,那麼這時就需要用到CGLIB進行動態代理,以便在建立代理的同時將動態方法織入到類中。
Spring中的例項化策略通過InstantiationStrategy類來定義,它有兩個實現類,CglibSubclassingInstantiationStrategy和SimpleInstantiationStrategy,其中前者繼承了後者。對於這兩個類有什麼區別呢?
描述 | CglibSubclassingInstantiationStrategy | SimpleInstantiationStrategy |
---|---|---|
是否Spring預設例項化策略 | 是 | 否 |
是否支援方法注入例項化 | 是 | 否 |
是否反射例項化 | 是 | 是 |
接著上一章的分析,我們來看原始碼,開啟AbstractAutowireCapableBeanFactory類的instantiateBean方法
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
// 如果許可權管理器不為空,需要校驗
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged ((PrivilegedAction<Object>) () ->
getInstantiationStrategy().instantiate(mbd, beanName, parent),
getAccessControlContext());
}
else {
// 獲取例項化策略並例項化bean
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
可以看到例項化bean的方法為instantiate,進入到SimpleInstantiationStrategy類的instantiate方法中,繼續檢視:
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// 如果沒有使用方法覆蓋(replace-method或lookup-method注入),則直接使用反射建立bean的例項
// Don't override the class with CGLIB if no overrides.
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
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 {
constructorToUse = clazz.getDeclaredConstructor();
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// 否則必須使用CGLIB例項化策略
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
在該方法中,Spring對使用JDK的反射例項化還是CGLIB例項化進行了判斷。如果沒有方法覆蓋(replace-method或lookup-method注入)則使用JDK的反射機制進行例項化,如果有的話,則使用CGLIB動態代理進行例項化。
具體的例項化程式碼這裡就不分析了,大家可以使用一個普通的bean和使用了replace-method或lookup-method注入的bean進行debug。參考14–Spring lookup-method注入和replace-method注入(二)
Spring預設建構函式的例項化還是比較簡單的,下一篇我們分析對建構函式引數的解析,就比較複雜了!