Spring Bean 的創建程序介紹了FactoryBean 的創建方式,那么接下來介紹不是FactoryBean的創建方式,在創建程序中,又會分為單例的Bean的創建,原型型別的Bean的創建等,一般來說在Spring中幾乎所有物件都是單例創建的,除非有其他業務需要設定為其他作用域的Bean,所以重點以創建單例Bean為例,
單例Bean的創建
在創建時會呼叫getBean,然后doGetBean,一般來說在Spring中只要是do開頭方法基本就是真正干活的方法,所以我們看doGetBean方法的原始碼:
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 決議成規范的Bean name ,因為可能是FactoryBean加了& 前綴的Bean或者是有別名的Bean
String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 獲取快取中的Bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// 如果快取中沒有,那么就會按照單例或者多例的方式創建
else {
// 省略代碼....
// Check if bean definition exists in this factory.
// 檢查父類容器
// 省略代碼....
if (!typeCheckOnly) {
// 標記已經被創建
markBeanAsCreated(beanName);
}
try {
// 合并BeanDefinition
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// 判斷是否存在依賴的Bean的創建,比如dependsOn 依賴 A 這個Bean,那么就需要先創建A這個bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
// 省略代碼....
}
// 注冊依賴的Bean,放在集合中
registerDependentBean(dep, beanName);
try {
// 創建Bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
// 省略代碼....
}
}
}
// Create bean instance.
if (mbd.isSingleton()) {
// 如果是單例的,就去創建Bean
sharedInstance = getSingleton(beanName, () -> {
try {
// 創建Bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// 省略代碼....
}
});
// 獲取bean物件,會進行檢查獲取物件是否是FactoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 原型作用的創建方式
else if (mbd.isPrototype()) {
// 省略代碼....
}
else {
// 省略代碼....
}
}
catch (BeansException ex) {
// 省略代碼....
}
}
// 省略代碼....
return (T) bean;
}
去掉不重要的代碼,可以看到首先是從快取中獲取,如果沒有獲取到就進行一些列檢查,最終檢查是否單例的Bean,如果是,那么就會呼叫getSingleton方法,傳入一個beanName,一個ObjectFactory的lambda運算式,運算式中有個createBean方法,這個方法就是創建的Bean方法,
那什么時候呼叫crateBean方法呢?
答案是執行lambda運算式的具體方法時執行,我們先看看這個ObjectFactory介面是啥?
ObjectFactory 物件工廠
直接看原始碼:
@FunctionalInterface
public interface ObjectFactory<T> {
/**
* Return an instance (possibly shared or independent)
* of the object managed by this factory.
* @return the resulting instance
* @throws BeansException in case of creation errors
*/
T getObject() throws BeansException;
}
這個介面是一個函式式介面,可以用于lambda運算式直接使用,在呼叫getObject方法時就是真正執行lambda運算式中的方法,
具體看看getSingleton方法的原始碼:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
// 省略代碼....
}
// 省略代碼....
// 檢查 并添加正在創建的單例物件到集合中
beforeSingletonCreation(beanName);
// 設定為新的單例物件標識
boolean newSingleton = false;
// 設定例外集合,出現例外時將例外加入到集合中
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 執行具體的方法,呼叫crateBean方法
singletonObject = singletonFactory.getObject();
// 標識為新的單例物件
newSingleton = true;
}
catch (IllegalStateException ex) {
// 省略代碼....
}
catch (BeanCreationException ex) {
// 省略代碼....
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 檢查 并移除正在創建的單例物件
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 加入到快取中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
這里執行完之后就會執行到lambda運算式中的createBean方法:
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 省略代碼....
RootBeanDefinition mbdToUse = mbd;
// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
// 決議Bean的Class 用于反射創建物件
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
// 方法覆寫準備 lookup-method replace-method
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
// 省略代碼....
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// 決議提前實體化,使用InstantiationAwareBeanPostProcessor實作
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
// 省略代碼....
}
try {
// 實體化 + 初始化 Bean
// 真正的創建Bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
// 省略代碼....
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// 省略代碼....
}
catch (Throwable ex) {
// 省略代碼....
}
}
首先是進行了Bean的型別的決議,主要是用于后面的反射創建物件時使用,并設定到RootBeanDefinition中,然后進行方法覆寫操作,
Spring方法覆寫實戰
方法覆寫就是使用了lookup-method和replace-method 標簽的時候,就會進行方法的覆寫,方法覆寫有什么用處呢?
一般來說方法覆寫就是解決單例物件參考多例物件的時候使用方法覆寫,
做個實驗試試:
定義一個房子類,用于停車
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public abstract class MyHouse {
public abstract MyCar park();
}
定義我的車
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public interface MyCar {
/**
* 買一輛車
* @return 車
*/
MyCar buy();
}
定義實作類:
寶馬車:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class BMW implements MyCar{
@Override
public MyCar buy() {
return this;
}
}
奔馳車:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class Ben implements MyCar{
@Override
public MyCar buy() {
return this;
}
}
xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="myHouse1" >
<lookup-method name="park" bean="bmw"/>
</bean>
<bean id="myHouse2" >
<lookup-method name="park" bean="ben"/>
</bean>
<!-- 設定為原型-->
<bean id="bmw" scope="prototype"/>
<bean id="ben" />
</beans>
測驗類:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class LookupTest {
/**
* lookup-method 用來解決單例物件多例物件的
*/
@Test
public void lookupTest(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:method-override.xml");
MyHouse myHouse = (MyHouse) context.getBean("myHouse1");
MyHouse myHouse1 = (MyHouse) context.getBean("myHouse1");
System.out.println(myHouse.park());
System.out.println(myHouse1.park());
}
}
輸出:
com.redwinter.test.methodoverride.lookupmethod.BMW@4a765
com.redwinter.test.methodoverride.lookupmethod.BMW@3e6358
這里Myhouse是一個單例的物件,myHouse1 呼叫的方法每次呼叫都是不同的物件,
Spring是如何實作方法覆寫的?
原始碼過于繁瑣和復雜,這里直接看執行流程:

Spring在加載BeanDefinition的時候,執行 parseLookupOverrideSubElements 這個方法的時候只要設定了lookup-method標簽就會創建一個LookupOverride類放入到BeanDefinition 的 MethodOverrides 屬性中,在進行Bean的創建的時候,就會判斷這個屬性值是否有值,如果有那么就會在物件實體化時獲取一個實體化策略,然后執行實體化,就、就會呼叫SimpleInstantiationStrategy#instantiate 方法,然后使用CGLIB進行實體化,創建出一個Enhancer 增強類,并且設定一個回呼型別為:
private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
{NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
最終在執行方法的時候就會呼叫到回呼類LookupOverrideMethodInterceptor 攔截器上,然后執行Bean的創建:
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
// Cast is safe, as CallbackFilter filters are used selectively.
LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
Assert.state(lo != null, "LookupOverride not found");
Object[] argsToUse = (args.length > 0 ? args : null); // if no-arg, don't insist on args at all
if (StringUtils.hasText(lo.getBeanName())) {
// 創建Bean
Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
this.owner.getBean(lo.getBeanName()));
// Detect package-protected NullBean instance through equals(null) check
return (bean.equals(null) ? null : bean);
}
else {
return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
this.owner.getBean(method.getReturnType()));
}
}
這篇文章就介紹到這里,下一篇繼續,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/472251.html
標籤:其他
