前言
在上一篇文章中,我們深入探討了 Spring 框架中 Bean 的例項化過程,該過程包括從 Bean 定義中載入當前類、尋找所有實現了 InstantiationAwareBeanPostProcessor 介面的類並呼叫例項化前的方法、進行例項化、呼叫 applyMergedBeanDefinitionPostProcessors 方法等多個步驟,最終生成了一個真正的 Bean 例項。但是,這個 Bean 例項還沒有被初始化和注入屬性,還不能真正發揮作用。
在今天的文章中,我們將深入探討 Bean 的屬性注入和初始化流程,從而使其成為一個真正意義上的 Bean。這個過程包括屬性注入、Aware 介面回撥、BeanPostProcessor 的前置和後置處理等多個步驟,透過本文的學習,讀者將能夠更深入地瞭解 Spring 框架中 Bean 的屬性注入和初始化過程,為後續的學習和實踐打下堅實的基礎。
populateBean
在 Spring 框架中,屬性注入是 Bean 初始化過程中的一個重要環節。在 Bean 例項化完成後,Spring 框架會根據 Bean 定義中的屬性設定進行屬性注入,同時還會呼叫一些 Aware 介面回撥方法,以及一些 BeanPostProcessor 的前置和後置處理方法,最終完成 Bean 的初始化過程。好的,拋去不用看的,我們來看下剩下的原始碼:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
}
......
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
// MutablePropertyValues是PropertyValues具體的實現類
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;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
// 這裡會呼叫AutowiredAnnotationBeanPostProcessor的postProcessProperties()方法,會直接給物件中的屬性賦值
// AutowiredAnnotationBeanPostProcessor內部並不會處理pvs,直接返回了
PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
......
// 如果當前Bean中的BeanDefinition中設定了PropertyValues,那麼最終將是PropertyValues中的值,覆蓋@Autowired
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
PropertyValues
在 Spring 框架中,PropertyValues 物件是從 Bean 定義中獲取的,而我們自己定義的 Bean 並沒有這個屬性值。一般情況下,這一步會被跳過,但如果需要注入屬性值,我們可以透過實現 MergedBeanDefinitionPostProcessor 介面的 postProcessMergedBeanDefinition 方法來對 Bean 定義進行修改,從而新增需要注入的屬性值。
具體來說,我們可以定義一個實現了 MergedBeanDefinitionPostProcessor 介面的類,比如下面這個例子::
@Component
public class MyInstantiationAwareBeanPostProcessors implements InstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (beanName.equals("userService")) {
beanDefinition.setPropertyValues(new MutablePropertyValues().add("orderService", new First()));
}
}
}
在這個例子中,我們判斷如果 Bean 的名稱是 "userService",則新增一個名為 "orderService" 的屬性,並將其值設定為 First 類的一個例項。需要注意的是,為了能夠正常注入屬性值,我們需要在 Bean 中定義一個名為 "setOrderService" 的 setter 方法,這樣就可以注入進去,當然我寫的這個是報錯的狀態,這樣大家可以找到他是在哪裡進行呼叫的。
autowireByName/autowireByType
講解之前,我先宣告一下他跟我們的@autowired註解沒有半毛錢關係,除了上面一種我們人為干預的,還有一種Spring自帶的方式,在我們配置類中:
@Bean(autowire = Autowire.BY_NAME)
public UserService userService(){
return new UserService();
}
這樣定義時,他就會自動掃描你這個當前類中所有的set方法,是所有的、而且不區分的。這裡以autowireByName為例講解,autowireByType類似:
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
// 當前Bean中能進行自動注入的屬性名
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
// 遍歷每個屬性名,並去獲取Bean物件,並設定到pvs中
for (String propertyName : propertyNames) {
if (containsBean(propertyName)) {
Object bean = getBean(propertyName);
pvs.add(propertyName, bean);
// 記錄一下propertyName對應的Bean被beanName給依賴了
registerDependentBean(propertyName, beanName);
if (logger.isTraceEnabled()) {
logger.trace("Added autowiring by name from bean name '" + beanName +
"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
"' by name: no matching bean found");
}
}
}
}
- unsatisfiedNonSimpleProperties:找到所有set方法
- getBean:按照set方法名字獲取bean
- pvs.add(propertyName, bean):設定到MutablePropertyValues屬性中,不是對我們的bean進行屬性注入
那有些同學可能會想到了,為什麼Spring已經預設提供了一套注入方式還有弄一個@autowired註解呢?主要是因為它們各自有不同的優點和適用場景。
預設的注入方式非常靈活,它會遍歷 Bean 中所有的 setter 方法,對每個屬性進行注入,從而實現自動裝配。這種方式適用於大多數情況,因為它能夠自動識別並注入所有需要的依賴項,並且不需要進行任何額外的配置。
而 @Autowired 註解則提供了更加精細的控制,它可以指定需要注入的屬性或方法,並且還可以指定注入的方式、名稱、是否必須等屬性。這種方式適用於需要更加精細的控制和配置的情況,@Autowired 註解是一個可插拔的元件,它只有在 Spring 容器啟動時掃描到該註解時才能夠進行自動裝配。如果我們使用 XML 配置的方式啟動 Spring 容器,需要在配置檔案中新增 context:component-scan 元素來開啟自動掃描功能,否則即使寫了 @Autowired 註解也不會進行注入。
postProcessProperties
這一步將會對@autowired註解進行屬性注入,其他的不看,這裡只看下AutowiredAnnotationBeanPostProcessor對屬性或者方法的注入:
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
// 如果一個Bean的型別是String...,那麼則根本不需要進行依賴注入
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// 遍歷targetClass中的所有Field
ReflectionUtils.doWithLocalFields(targetClass, field -> {
// field上是否存在@Autowired、@Value、@Inject中的其中一個
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
// static filed不是注入點,不會進行自動注入
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// 構造注入點
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 遍歷targetClass中的所有Method
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
// method上是否存在@Autowired、@Value、@Inject中的其中一個
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// static method不是注入點,不會進行自動注入
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
// set方法最好有入參
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
- 如果一個Bean的型別是String...,那麼則根本不需要進行依賴注入
- 遍歷targetClass中的所有Field,static filed不是注入點,不會進行自動注入
- 遍歷targetClass中的所有Method,static method不是注入點,不會進行自動注入
- 上面的注入點構造好後,會在外層直接invoke呼叫注入
這裡強調一下在對方法注入點進行注入時,會先判斷一下是否有PropertyValues,如果有的話則跳過注入,AutowiredMethodElement原始碼如下:
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 如果pvs中已經有當前注入點的值了,則跳過注入
if (checkPropertySkipping(pvs)) {
return;
}
......
}
applyPropertyValues
直接應用PropertyValues注入屬性,可以看到這一步在我們的@autowired解析注入之後,如果你有的屬性欄位已經被@autowired注入了,但是又有一個PropertyValues那麼這個set方法會把你的@Autowired之前注入進去的物件值覆蓋,原始碼很多為了篇幅就不看了。知道這個方法是幹啥的就行。
initializeBean
屬性填充完之後,終於進入到了初始化階段,為什麼需要初始化這一步呢?這是對bean的最終處理,該方法返回的物件才是Spring管理的最終物件,Spring AOP就是對初始化這一步做 的擴充套件。
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
// 初始化前
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
// 初始化
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
// 初始化後 AOP
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
invokeAwareMethods
該方法就是Aware介面的實現
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
applyBeanPostProcessorsBeforeInitialization
初始化前的類處理,我們主講兩個類:ApplicationContextAwareProcessor、
InitDestroyAnnotationBeanPostProcessor透過這兩個類看看可以初始化前我們可以做哪些內容:
ApplicationContextAwareProcessor
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware ||
bean instanceof ApplicationStartupAware)) {
return bean;
}
......
// 執行aware方法
invokeAwareInterfaces(bean);
}
return bean;
}
初始化前會判斷當前是否是某個Aware類,那麼則執行aware方法進行回撥。
InitDestroyAnnotationBeanPostProcessor
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}
- findLifecycleMetadata:好奇的小夥伴可以看下這個方法,他會構造@PostConstruct、@PreDestroy執行點
- metadata.invokeInitMethods:執行帶有@PostConstruct方法
invokeInitMethods
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
......
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
- 如果當前類實現了InitializingBean介面,那麼執行afterPropertiesSet方法進行初始化
- initMethodName:如果當前類指定了初始方法,那麼直接invoke執行
applyBeanPostProcessorsAfterInitialization
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
執行完postProcessAfterInitialization方法後,那麼這個物件終於初始化成功了
總結
今天我們主講bean的初始化,主要流程如下:
- 屬性注入,執行@autowired、PropertyValues注入等
- 初始化前置方法,執行@PostConstruct方法、回撥Aware介面等
- 初始化,呼叫afterPropertiesSet或者initMethod
- 初始化後置方法
最後一節我們會講bean的銷燬,那麼bean的生命週期系列文章會結束,實際上 Spring 框架還有很多其他的功能和特性,例如 AOP、事務管理、Web 開發等等,博主還會進行對Spring系列繼續更新,請大家繼續跟緊學習。