來源:小小木的博客
www.cnblogs.com/wyc1994666/p/10650480.html
本次主要想寫spring bean的實體化相關的內容,創建spring bean 實體是spring bean 生命周期的第一階段,
bean 的生命周期主要有如下幾個步驟:
-
創建bean的實體
-
給實體化出來的bean填充屬性
-
初始化bean
-
通過IOC容器使用bean
-
容器關閉時銷毀bean
在實體化bean之前在BeanDefinition里頭已經有了所有需要實體化時用到的元資料,接下來spring 只需要選擇合適的實體化方法以及策略即可,實體化方法有兩大類分別是工廠方法和構造方法實體化,后者是最常見的,
spring默認的實體化方法就是無參建構式實體化,
如我們在xml里定義的 <bean id="xxx" /> 以及用注解標識的bean都是通過默認實體化方法實體化的,
-
兩種實體化方法(建構式 和 工廠方法)
-
原始碼閱讀
-
實體化策略(cglib or 反射)
兩種實體化方
使用適當的實體化方法為指定的bean創建新實體:工廠方法,建構式實體化,
代碼演示
啟動容器時會實體化所有注冊的bean(lazy-init懶加載的bean除外),對于所有單例非懶加載的bean來說當從容器里獲取bean(getBean(String name))的時候不會觸發,實體化階段,而是直接從快取獲取已準備好的bean,而生成bean的時機則是下面這行代碼運行時觸發的,
@Test
public void testBeanInstance(){
// 啟動容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
}
一 使用工廠方法實體化(很少用)
1.靜態工廠方法
public class FactoryInstance {
public FactoryInstance() {
System.out.println("instance by FactoryInstance");
}
}
public class MyBeanFactory { public static FactoryInstance getInstanceStatic(){ return new FactoryInstance();
}
}
<?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="factoryInstance" factory-method="getInstanceStatic"/>
</beans>
輸出結果為:
instance by FactoryInstance
2.實體工廠方法
public class MyBeanFactory {
/**
* 實體工廠創建bean實體
*
* @return
*/
public FactoryInstance getInstance() { return new FactoryInstance();
}
}
<?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 工廠實體 -- >
<bean id="myBeanFactory" />
<bean id="factoryInstance" factory-bean="myBeanFactory" factory-method="getInstance"/>
</beans>
輸出結果為:
instance by FactoryInstance
二 使用建構式實體化(無參建構式 & 有參建構式)
1.無參建構式實體化(默認的)
public class ConstructorInstance {
public ConstructorInstance() {
System.out.println("ConstructorInstance none args");
}
}
<?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="constructorInstance" />
</beans>
輸出結果為:
ConstructorInstance none args
1.有參建構式實體化
public class ConstructorInstance {
private String name;
public ConstructorInstance(String name) {
System.out.println("ConstructorInstance with args");
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
<?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="constructorInstance" >
<constructor-arg index="0" name="name" value="https://www.cnblogs.com/javastack/p/test constructor with args"/>
</bean>
</beans>
輸出結果為:
ConstructorInstance with args
原始碼閱讀
下面這段是 有關spring bean生命周期的代碼,也是我們本次要討論的bean 實體化的入口,bean 為什么默認單例?推薦看下,關注Java技術堆疊公眾號在后臺回復Spring閱讀更多Spring系列教程,
doCreateBean方法具體實作在doCreateBeanAbstractAutowireCapableBeanFactory類,感興趣的朋友可以進去看看呼叫鏈,
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
//第一步 創建bean實體 還未進行屬性填充和各種特性的初始化
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
Object exposedObject = bean;
try {
// 第二步 進行屬性填充
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 第三步 初始化bean 執行初始化方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}catch (Throwable ex) {
// 拋相應的例外
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
我們這里只需關注第一步創建bean實體的流程即可
instanceWrapper = createBeanInstance(beanName, mbd, args);
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 使用工廠方法進行實體化
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Need to determine the constructor...
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 使用帶參建構式初始化
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// 默認實體化方式 無參構造實體化
return instantiateBean(beanName, mbd);
}
上面代碼就是spring 實作bean實體創建的核心代碼,這一步主要根據BeanDefinition里的元資料定義決定使用哪種實體化方法,主要有下面三種:
-
instantiateUsingFactoryMethod 工廠方法實體化的具體實作
-
autowireConstructor 有參建構式實體化的具體實作
-
instantiateBean 默認實體化具體實作(無參建構式)
實體化策略(cglib or 反射)
工廠方法的實體化手段沒有選擇策略直接用了發射實作的
實體化策略都是對于建構式實體化而言的
上面說到的兩建構式實體化方法不管是哪一種都會選一個實體化策略進行,到底選哪一種策略也是根據BeanDefinition里的定義決定的,
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
上面這一行代碼就是選擇實體化策略的代碼,進入到上面兩種方法的實作之后發現都有這段代碼,
下面選一個instantiateBean的實作來介紹
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
return getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
}, getAccessControlContext());
}
else {
// 在這里選擇一種策略進行實體化
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);
}
}
選擇使用反射還是cglib
先判斷如果beanDefinition.getMethodOverrides()為空也就是用戶沒有使用replace或者lookup的配置方法,那么直接使用反射的方式,簡單快捷,但是如果使用了這兩個特性,在直接使用反射的方式創建實體就不妥了,因為需要將這兩個配置提供的功能切入進去,所以就必須要使用動態代理的方式將包含兩個特性所對應的邏輯的攔截增強器設定進去,這樣才可以保證在呼叫方法的時候會被相應的攔截器增強,回傳值為包含攔截器的代理實體,---參考自《spring 原始碼深度剖析》這本書
<bean id="constructorInstance" > <lookup-method name="getName" bean="xxx"/>
<replaced-method name="getName" replacer="yyy"/>
</bean>
如果使用了lookup或者replaced的配置的話會使用cglib,否則直接使用反射,
具體lookup-method和replaced-method的用法可以查閱相關資料,
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (bd.getMethodOverrides().isEmpty()) {
constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
return BeanUtils.instantiateClass(constructorToUse);
}else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
由于篇幅省略了部分代碼,
關注公眾號Java技術堆疊回復"面試"獲取我整理的2020最全面試題及答案,
推薦去我的博客閱讀更多:
1.Java JVM、集合、多執行緒、新特性系列教程
2.Spring MVC、Spring Boot、Spring Cloud 系列教程
3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程
4.Java、后端、架構、阿里巴巴等大廠最新面試題
覺得不錯,別忘了點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/110747.html
標籤:Java
