淺談spring-createBean

2022-07-22 12:05:53

找到BeanClass並且載入類

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		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);
		}
  .......省略與此步驟無關的程式碼
}

注意:上面程式碼中的 resolveBeanClass(mbd, beanName) 方法,就是去查詢BeanClass的,下面看看 resolveBeanClass 方法的程式碼

@Nullable
protected Class<?> resolveBeanClass(RootBeanDefinition mbd, String beanName, Class<?>... typesToMatch)
      throws CannotLoadBeanClassException {

   try {
      // 判斷 BeanDefinition 中的 beanClass 屬性是不是 Class 型別的
      if (mbd.hasBeanClass()) {
         return mbd.getBeanClass();
      }
      // 執行搜尋 Bean class
      return doResolveBeanClass(mbd, typesToMatch);
   }  ...省略catch 程式碼
   }
}

注意程式碼中有一個 mbd.hasBeanClass() 的判斷, 這個地方比較有迷惑性,並不是判斷beanClass屬性是否存在,而是判斷

beanClass 屬性是不是屬於 Class型別的,因為在spring最開始的掃描過程中,給BeanDefiniton 中 beanClass 屬性存入的是對應 BeanDefinition 的類名稱,下面是 hasBeanClass() 方法的程式碼:

public boolean hasBeanClass() {

   // 判斷 BeanDefinition 中的 beanClass 屬性是不是屬於 Class 的
   // 因為最開始的時候存入的是 BeanDefinition 對應的類的類名
   return (this.beanClass instanceof Class);
}

如果判斷 beanClass 屬性 是一個CLass 物件則直接返回,否則進入doResolceBeanClass(mad, typesToMatch) 方法

private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
      throws ClassNotFoundException {

   // 獲取類載入器
   ClassLoader beanClassLoader = getBeanClassLoader();
   ClassLoader dynamicLoader = beanClassLoader;
   boolean freshResolve = false;

   .... 省略程式碼
     
   // 這裡就是拿的 RootBeanDefinition 中的 beanClass屬性
   String className = mbd.getBeanClassName();
   if (className != null) {
      // 解析 spring 自己定義的表示式---沒有去了解
      Object evaluated = evaluateBeanDefinitionString(className, mbd);
      if (!className.equals(evaluated)) {
         // A dynamically resolved expression, supported as of 4.2...
         if (evaluated instanceof Class) {
            return (Class<?>) evaluated;
         } else if (evaluated instanceof String) {
            className = (String) evaluated;
            freshResolve = true;
         } else {
            throw new IllegalStateException("Invalid class name expression result: " + evaluated);
         }
      }
      if (freshResolve) {
         // When resolving against a temporary class loader, exit early in order
         // to avoid storing the resolved Class in the bean definition.
         if (dynamicLoader != null) {
            try {
               // 載入類,當前 需要建立的 Bean 的 Class檔案
               return dynamicLoader.loadClass(className);
            } catch (ClassNotFoundException ex) {
               if (logger.isTraceEnabled()) {
                  logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);
               }
            }
         }
        	// 內部 使用了 Class.forName() 去載入這個類:Class.forName(name, false, clToUse);
         return ClassUtils.forName(className, dynamicLoader);
      }
   }
   // 定期解析,將結果快取在 BeanDefinition 中
   // Resolve regularly, caching the result in the BeanDefinition...
   return mbd.resolveBeanClass(beanClassLoader);
}

首先我們注意到方法進入時就有一個獲取BeanClassLoader的方法 getBeanClassLoader(),最終該方法的程式碼是如下:

@Nullable
public static ClassLoader getDefaultClassLoader() {
   ClassLoader cl = null;
   try {
      // 獲取當前執行緒的類載入器,可以設定的 Thread.currentThread().setContextClassLoader();
      cl = Thread.currentThread().getContextClassLoader();
   }
   catch (Throwable ex) {
      // Cannot access thread context ClassLoader - falling back...
   }
  // 使用當前類的載入器去載入,有可能返回空,因為 lib 下面的 jar包使用 bootstrap 類載入器去載入的
   if (cl == null) {
      // No thread context class loader -> use class loader of this class.
      cl = ClassUtils.class.getClassLoader();
      if (cl == null) {
         // getClassLoader() returning null indicates the bootstrap ClassLoader
         try {
            // 獲取系統的載入器
            cl = ClassLoader.getSystemClassLoader();
         }
         catch (Throwable ex) {
            // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
         }
      }
   }
   return cl;
}

就是拿到類載入器,最終就是使用當前的類載入器,去載入mbd.getBeanClassName()方法拿出來的類名稱className

這樣將 BeanClass 檔案就已經被載入了,緊接著就是進入範例化,在範例化前,還有一個步驟就是:範例化前

範例化前

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {

	...... 省略上一步驟的程式碼

   try {
      // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
      // 範例化前  InstantiationAwareBeanPostProcessor  使用的是這個 BeanPostprocessor
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      // 如果範例化前,由自己建立類物件則直接返回
      if (bean != null) {
         return bean;
      }
   }
  	// doCreateBean 執行建立bean的方法,此方法中就會去範例化物件
  Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		... 省略紀錄檔列印
			return beanInstance;
  ..... 省略此步驟無關程式碼
}

這裡主要關注的就是範例化前的 InstantiationAwareBeanPostProcessor 介面,介面中有三個預設的方法,這裡只討論,postProcessBeforeInstantiation(Class<?> beanClass, String beanName) 初始化前的方法

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
	@Nullable
	default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}
  .... 省略另外兩個方法
}

該方法的執行時機就是在範例化前,從給出的createBean方法原始碼中可以體現出來,這裡就給了我們許多的操作空間。

resolveBeforeInstantiation(beanName, mbdToUse); 在這個方法裡面就回去執行初始化前的呼叫:

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
   Object bean = null;
   if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
      // Make sure bean class is actually resolved at this point.
      if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
         Class<?> targetType = determineTargetType(beanName, mbd);
         if (targetType != null) {
            // 在範例化前應用BeanPostProcessor
            bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
            if (bean != null) {
               // 初始化後的 BeanPostProcessor
               bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
            }
         }
      }
      mbd.beforeInstantiationResolved = (bean != null);
   }
   return bean;
}

可以看到在初始化前呼叫之後判斷了一次 返回的 bean物件是不是空,因為在初始化前方法中給傳入BeanClass 物件,在此之前就已經給 beanClass 賦值過了,這裡我們可以自己去建立一個物件返回,如果是這樣,表示不需要Spring來範例化了,並且後續的Spring依賴注入也不會進行了,會跳過一些步驟,直接執行初始化後這一步。在執行範例化前這裡還有一個小的知識,就是當同時存在很多的範例化前 postProcessor ,只要一直行到 postProcessBeforeInstantiation 方法返回的bean不是空的的情況下,剩下所有的 初始化前postProcessor都不會在執行了。

	protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
    // 這裡拿到的就是 InstantiationAwareBeanPostProcessor 型別的 postProcessor
		for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
			Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
			// 因為這裡是初始化前,所以在執行到 beanPostprocessor 返回有物件的時候就直接返回,不會執行後續的 InstantiationAwareBeanPostProcessor
			// 如果第一個處理器就返回了 物件範例,則不會再去執行其他的 InstantiationAwareBeanPostProcessor
			if (result != null) {
				return result;
			}
		}
		return null;
	}

在for迴圈中 getBeanPostProcessorCache().instantiationAware方法拿到的就是,InstantiationAwareBeanPostProcessor型別的postProcessor,原因是:spring對postProcessor進行了分類的:

工廠方法建立物件

方法一

如果沒有設定Supplier,則檢查BeanDefinition中是否設定了factoryMethod,也就是工廠方法,有兩種方式可以設定factoryMethod,比如:

<bean id="userService" class="cn.baldhead.service.UserService" factory-method="createUserService" />

對應的UserService 程式碼

public class UserService {

	public static UserService createUserService() {
		System.out.println("執行createUserService()");
		UserService userService = new UserService();
		return userService;
	}

	public void test() {
		System.out.println("test");
	}

}

方法二

<bean id="commonService" class="cn.baldhead.service.CommonService"/>
<bean id="userService1" factory-bean="commonService" factory-method="createUserService" />

Spring發現當前BeanDefinition方法設定了工廠方法後,就會區分這兩種方式,然後呼叫工廠方法得到物件。

值得注意的是,我們通過@Bean所定義的BeanDefinition,是存在factoryMethod和factoryBean的,也就是和上面的方式二非常類似,@Bean所註解的方法就是factoryMethod,AppConfig物件就是factoryBean。如果@Bean所所註解的方法是static的,那麼對應的就是方式一。

推斷構造方法

推斷完構造方法後,就會使用構造方法來進行範例化了。

額外的,在推斷構造方法邏輯中除開會去選擇構造方法以及查詢入參物件意外,會還判斷是否在對應的類中是否存在使用@Lookup註解了方法。如果存在則把該方法封裝為LookupOverride物件並新增到BeanDefinition中。

@Lookup註解就是方法注入,例如demo如下:

@Component
public class UserService {

	private OrderService orderService;

	public void test() {
		OrderService orderService = createOrderService();
		System.out.println(orderService);
	}

	@Lookup("orderService")
	public OrderService createOrderService() {
		return null;
	}

}

在範例化時,如果判斷出來當前BeanDefinition中沒有LookupOverride,那就直接用構造方法反射得到一個範例物件。如果存在LookupOverride物件,也就是類中存在@Lookup註解了的方法,那就會生成一個代理物件。

BeanDefionition 的後置處理

Bean物件範例化出來之後,接下來就應該給物件的屬性賦值了。在真正給屬性賦值之前,Spring又提供了一個擴充套件點MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(),可以對此時的BeanDefinition進行加工,比如:

@Component
public class BaldHeadMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        // 可以手動給 beanDefinition 得ptopertyValues 新增一個屬性賦值,屬性名-值(bean中的屬性賦值)
		if ("baldHeadService".equals(beanName)) {
			beanDefinition.getPropertyValues().add("orderService", new OrderService());
		}
	}
}

原始碼在--doCreateBean()

/ Allow post-processors to modify the merged bean definition.
		// 允許(MergedBeanDefinitionPostProcessor)增強器修改合併的bean definition 修改BD資訊
		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;
			}
		}

在Spring原始碼中,AutowiredAnnotationBeanPostProcessor就是一個MergedBeanDefinitionPostProcessor,它的postProcessMergedBeanDefinition()中會去查詢注入點,並快取在AutowiredAnnotationBeanPostProcessor物件的一個Map中(injectionMetadataCache)。

範例化後

AbstractAutowireCapableBeanFactory.poputlateBean()

// 設定屬性注入   之前bean的狀態,例如,屬性賦值之前後置處理器可以提前處理些東西
// 支援欄位注入  (但是在此處什麼事都沒做)
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
   for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
      if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
         return;
      }
   }
}

屬性填充

AbstractAutowireCapableBeanFactory.populateBean()

spring的注入

​ 必須要有對應屬性的set方法,type:根據引數的型別去找到對應的Bean,name:根據方法setxxx後面的一串去找到對應的 Bean ,例如當前就是用的 xxx 作為name去找

只要是set 方法 Spring 都會去呼叫,不管這個set方法是做什麼的,都會去呼叫

​ BY_TYPE,BY_NAME

	// 獲取所有屬性的值
		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// Add property values based on autowire by name if applicable. 通過名稱自動注入引數的值
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable. 通過型別注入引數的值
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}

自動注入

處理屬性

這個步驟中,就會處理@Autowired、@Resource、@Value等註解,也是通過InstantiationAwareBeanPostProcessor.postProcessProperties()擴充套件點來實現的,比如我們甚至可以實現一個自己的自動注入功能,比如:

@Component
public class BaldHeadInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
		if ("baldHeadService".equals(beanName)) {
			for (Field field : bean.getClass().getFields()) {
				if (field.isAnnotationPresent(BaldHeadInject.class)) {
					field.setAccessible(true);
					try {
						field.set(bean, "123");
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				}
			}
		}

		return pvs;
	}
}

Aware回撥

AbstractAutowireCapableBeanFactory.initializeBean(..);

回撥執行Aware介面

完成了屬性賦值之後,Spring會執行一些回撥,包括:

  1. BeanNameAware:回傳beanName給bean物件。
  2. BeanClassLoaderAware:回傳classLoader給bean物件。
  3. BeanFactoryAware:回傳beanFactory給物件。

初始化前

初始化前,也是Spring提供的一個擴充套件點:BeanPostProcessor.postProcessBeforeInitialization(),比如

@Component
public class BaldHeadBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if ("baldHeadService".equals(beanName)) {
			System.out.println("初始化前");
		}

		return bean;
	}
}

利用初始化前,可以對進行了依賴注入的Bean進行處理。

在Spring原始碼中:

  1. InitDestroyAnnotationBeanPostProcessor會在初始化前這個步驟中執行@PostConstruct的方法,

  2. ApplicationContextAwareProcessor會在初始化前這個步驟中進行其他Aware的回撥:

    1. EnvironmentAware:回傳環境變數
    2. EmbeddedValueResolverAware:回傳預留位置解析器
    3. ResourceLoaderAware:回傳資源載入器
    4. ApplicationEventPublisherAware:回傳事件釋出器
    5. MessageSourceAware:回傳國際化資源
    6. ApplicationStartupAware:回傳應用其他監聽物件,可忽略
    7. ApplicationContextAware:回傳Spring容器ApplicationContext
  3. @PostConstruct @PreDestory 也是在初始化前這一步進行的解析,並做了一個快取

  4. InitDestroyAnnotationBeanPostProcessor.buildLifecycleMetadata()

  5. 	private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
    		if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
    			return this.emptyLifecycleMetadata;
    		}
    
            // 這裡面就會有 @PostConstruct 的,並且初始化方法有先後執行順序,父類別的排在前面,子類的在後面
            // 父類別優先執行
    		List<LifecycleElement> initMethods = new ArrayList<>();
            // 這裡面會有 @PreDestroy 的
    		List<LifecycleElement> destroyMethods = new ArrayList<>();
    		Class<?> targetClass = clazz;
    
    		do {
    			final List<LifecycleElement> currInitMethods = new ArrayList<>();
    			final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
    
    			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
    				if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
    					LifecycleElement element = new LifecycleElement(method);
    					currInitMethods.add(element);
    					if (logger.isTraceEnabled()) {
    						logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
    					}
    				}
    				if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
    					currDestroyMethods.add(new LifecycleElement(method));
    					if (logger.isTraceEnabled()) {
    						logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
    					}
    				}
    			});
    			// 父類別的初始化方法在前,也就是有一個先後順序,先執行父類別的 init-method 方法
    			initMethods.addAll(0, currInitMethods);
    			destroyMethods.addAll(currDestroyMethods);
    			targetClass = targetClass.getSuperclass();
    		}
    		while (targetClass != null && targetClass != Object.class);
    
    		return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
    				new LifecycleMetadata(clazz, initMethods, destroyMethods));
    	}
    

初始化

  1. 檢視當前Bean物件是否實現了InitializingBean介面,如果實現了就呼叫其afterPropertiesSet()方法

    protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
    			throws Throwable {
    		// 檢查是否實現了 InitializingBean 介面
    		boolean isInitializingBean = (bean instanceof InitializingBean);
    		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
    			if (logger.isTraceEnabled()) {
    				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
    			}
    			// 實現了 InitializingBean 介面,執行呼叫 afterPropertiesSet 方法
    			// 使用了多型的操作方式 將 bean 轉換為一個介面(initializingBean)
    			((InitializingBean) bean).afterPropertiesSet();
    		}
    
    		if (mbd != null && bean.getClass() != NullBean.class) {
    			// 獲取自定一個init-method方法
    			String initMethodName = mbd.getInitMethodName();
    			if (StringUtils.hasLength(initMethodName) &&
    					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
    					!mbd.isExternallyManagedInitMethod(initMethodName)) {
    				// 執行自定一個init-method方法,通過反射 method.invoke()
    				invokeCustomInitMethod(beanName, bean, mbd);
    			}
    		}
    	}
    
  2. 執行BeanDefinition中指定的初始化方法

    mbd.getInitMethodName()

初始化後

spring在初始化後也提供了一個擴充套件點,BeanPostProcessor.postProcessAfterInitialization()->例如:

@Component
public class BaldHeadBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if ("baldHeadService".equals(beanName)) {
			System.out.println("初始化後");
		}

		return bean;
	}
}
if (mbd == null || !mbd.isSynthetic()) { // 初始化後執行,postProcessor
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

可以在這個步驟中,對Bean最終進行處理,Spring中的AOP就是基於初始化後實現的,初始化後返回的物件才是最終的Bean物件

總結BeanPostProcessor

  1. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
  2. 範例化
  3. MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()
  4. InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
  5. 自動注入
  6. InstantiationAwareBeanPostProcessor.postProcessProperties()
  7. Aware物件
  8. BeanPostProcessor.postProcessBeforeInitialization()
  9. 初始化
  10. BeanPostProcessor.postProcessAfterInitialization()

bean的銷燬

目前這一塊未總結
如文中又錯誤請指出或者聯絡我:[email protected]