用了這麼多年的Spring 你還記得?

本文的閱讀最好的方式結合代碼一起看,在很久沒有回顧的時候可能會忘記一些spring的擴展點,已經spring 到底怎麼玩的?本文是作者瞭解spring 到工作的第四個年頭 從實習開始2016年,這裏做個總結,用了這麼多,估計寫着crud 忘了spring的奧祕了。

一、yy一下Bean創建的過程

首先得承認spring 這個太強大,一般的java開發者根本創造不出來這麼強大的一個玩意,從擴展性和維護性(…不知道誕生多少年了,反正我一畢業就開始學習),程序員都是模仿動物,都是不斷的在模仿中學習和進步,和小孩子學會說話、喫奶一個道理,要想自己更強大,還是站在巨人的肩膀上。

1.1 簡述Ioc 創建Bean

玩過spring的都知道,如果讓我寫一個類似spring的玩意。

  • 首先呢得模仿spring @conpentScan 首先要收集那些類被管理,那麼首先自己得編寫一波代碼掃描需要收集的類;
  • spring 支持xml、註解兩種配置方式,現在大佬們都是談各種需要創建領域、元數據,那麼針對一個Bean的創建 我們需要那些數據呢?翻翻自己的代碼看一下子,注入了哪些其他的Bean?對於哪些Bean有依賴,注入了哪些屬性值… 這個Bean的名稱、類型,這個Bean是哪個class ,有哪些註解呢等等,這個玩意叫做模型,用來描述一個Bean構建過程需要的一些基礎元數據信息,spring 中 通過BeanDefinition來定義, BeanDefinition是Bean 管理的基礎元數據 信息,Spring IoC實際上是Bean的管理容器,BeanDefinition是有關Bean定義信息的抽象,後續框架中要使用bean定義信息來實例化一個Bean,在spring 中無論 通過xml或annotation兩種方式構建Bean 都是轉化爲BeanDefinition來表示。
  • 有了 BeanDefinition 的信息,我們就可以通過這個來實例化一個Bean,簡單粗暴,通過class 反射創建一個對象,然後注入屬性,需要注入其他的Bean,獲取緩存有沒有有沒有繼續遞歸創建(不考慮循環依賴),完事~。

在這裏插入圖片描述

二、spring 擴展點的理解

2.1 BeanFactoryPostProcessor

BeanFactoryPostProcessor是針對容器的擴展點,加載收集BeanDefinition之後容器就會觸發該擴展點,實現了該接口的擴展點可以修改元信息也就是BeanDefinition對象中的信息,(See PropertyResourceConfigurer and its concrete implementations for out-of-the-box solutions that address such configuration needs. spring 的佔位符替換就是這樣的一種開箱即可使用的方案,在bean 還沒有初始化的時候就將數據中特殊標記熟悉進行佔位符的替換) 。

代碼地址 : org.springframework.beans.factory.config.PropertyResourceConfigurer
根據下面的代碼自己好好研究一下即可簡單的瞭解這個字符串替換的邏輯


   // org.springframework.beans.factory.config.PropertyResourceConfigurer#postProcessBeanFactory
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		try {
			Properties mergedProps = mergeProperties();

			// Convert the merged properties, if necessary.
			convertProperties(mergedProps);

			// Let the subclass process the properties.
			processProperties(beanFactory, mergedProps);
		}
		catch (IOException ex) {
			throw new BeanInitializationException("Could not load properties", ex);
		}
	}

   // org.springframework.beans.factory.config.PlaceholderConfigurerSupport#doProcessProperties 
     protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			StringValueResolver valueResolver) {

		BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

		String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
		for (String curName : beanNames) {
			// Check that we're not parsing our own bean definition,
			// to avoid failing on unresolvable placeholders in properties file locations.
			if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
				BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
				try {
                    // 替換屬性值
					visitor.visitBeanDefinition(bd);
				}
				catch (Exception ex) {
					throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
				}
			}
		}
	}

2.1.1 BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor 繼承了 BeanFactoryPostProcessor,允許在常規BeanFactoryPostProcessor檢測開始之前註冊更多的bean定義,因此這個子類是在BeanFactoryPostProcessor 之前被調用,很多的中間件註冊通過編碼註冊bean 就是採用這種形式,一個非常好的例子就是掃描 @Configuration 這種spring 4.0 後續增加的通過javaBean的方式註冊的Bean。

代碼例子: org.springframework.context.annotation.ConfigurationClassPostProcessor 掃描 @Configuration 註冊bean

代碼例子: org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor 掃描收集dubbo 提供的服務 其實dubbo的這個處理就是一個非常好的學習的例子,讓我們感受到了收集、註冊Bean的過程。同時可以模仿處理自定義.


//org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor#buildServiceBeanDefinition
private AbstractBeanDefinition buildServiceBeanDefinition(Service service, Class<?> interfaceClass,
                                                              String annotatedServiceBeanName) {

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServiceBean.class);

        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();

        String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
                "interface", "interfaceName", "parameters");

        propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(service, environment, ignoreAttributeNames));

        // References "ref" property to annotated-@Service Bean
        addPropertyReference(builder, "ref", annotatedServiceBeanName);
        // Set interface
        builder.addPropertyValue("interface", interfaceClass.getName());
        // Convert parameters into map
        builder.addPropertyValue("parameters", convertParameters(service.parameters()));
        return builder.getBeanDefinition();

}


// org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor#registerServiceBean
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                     DubboClassPathBeanDefinitionScanner scanner) {

        Class<?> beanClass = resolveClass(beanDefinitionHolder);

        Service service = findMergedAnnotation(beanClass, Service.class);

        Class<?> interfaceClass = resolveServiceInterfaceClass(beanClass, service);

        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, interfaceClass, annotatedServiceBeanName);

        // ServiceBean Bean name
        String beanName = generateServiceBeanName(service, interfaceClass);
        
        // 註冊bean
        registry.registerBeanDefinition(beanName, serviceBeanDefinition);
         
}

需要注意: BeanFactoryPostProcessor可以與bean定義交互並修改它們,但不能與bean實例交互。這樣做可能會導致過早的bean實例化,違反容器並導致意外的副作用。如果需要bean實例交互,請考慮實現 BeanPostProcessor 替換。

2.2 BeanPostProcessor

BeanPostProcessor 擴展點是針對Bean 實例,允許自定義修改新bean實例的工廠的擴展,通常在代理Bean、標記接口(xxxAware的實現方案)。這裏要記住的一點,這個的擴展是針對對象的,證明當前對象已經通過反射創建出來啦。

2.2.1 標記接口

比如我們常見的EnvironmentAware、ResourceLoaderAware、ApplicationContextAware等等 這些接口的標記,標記的處理結果就是針對當前對象進行當前接口的會掉的處理。 比如下面兩個代碼地址非常值得學習。

代碼地址: org.springframework.context.support.ApplicationContextAwareProcessor
org.springframework.web.context.support.ServletContextAwareProcessor
標記接口、非常簡單,存在這個接口做特定的事情處理一下這個bean 。

// Configure the bean factory with context callbacks.
// org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory  
//這個實例的初始化是在spring 容器啓動的時候處理的!

// beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
// beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
// beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
// beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
// beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
// beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
    if (bean instanceof Aware) {
        if (bean instanceof EnvironmentAware) {
            ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
        }
        if (bean instanceof EmbeddedValueResolverAware) {
            ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
        }
        if (bean instanceof ResourceLoaderAware) {
            ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
        }
        if (bean instanceof ApplicationEventPublisherAware) {
            ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
        }
        if (bean instanceof MessageSourceAware) {
            ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
        }
        if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
        }
	}
   return bean;
}

2.2.2 代理包裹

能夠處理代理的地方不僅僅只有這一個擴展的地方,蠻多的~ 但是可以相信的是都是這個類的子類!

代碼地址: org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor
這裏就是針對性創建代理對象,比如async 異步註解的實現 org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean instanceof AopInfrastructureBean) {
        // Ignore AOP infrastructure such as scoped proxies.
        return bean;
    }

    if (bean instanceof Advised) {
        Advised advised = (Advised) bean;
        if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
            // Add our local Advisor to the existing proxy's Advisor chain...
            if (this.beforeExistingAdvisors) {
                advised.addAdvisor(0, this.advisor);
            }
            else {
                advised.addAdvisor(this.advisor);
            }
            return bean;
        }
    }

    if (isEligible(bean, beanName)) {
        ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
        if (!proxyFactory.isProxyTargetClass()) {
            evaluateProxyInterfaces(bean.getClass(), proxyFactory);
        }
        proxyFactory.addAdvisor(this.advisor);
        customizeProxyFactory(proxyFactory);
        return proxyFactory.getProxy(getProxyClassLoader());
    }

    // No proxy needed.
    return bean;
}

2.3 Aware 擴展

開發中有一個常見場景,一個不受Spring管理的對象,需要引用或獲取Spring容器中的bean,我們需要先拿到ApplicationContext對象,然後調用getBean方法獲取容器中的bean。那ApplicationContext又怎樣獲取呢,系統提供了一個擴展點ApplicationContextAware,實現了該接口在創建對象後會把上下文通過接口定義的set方法傳給使用方。根據上面的方法很明顯的知道了實現原理。arthas-idea 插件中的Static Spring context invoke method field 就是通過獲取靜態的ApplicationContext 實現getBean的。其實這個也是一種BeanPostProcessor的變體。

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * 提供給arthas ognl 獲取context的信息
 *
 * @author 汪小哥
 * @date 30-28-2020
 */
@Component
public class ApplicationContextProvider implements ApplicationContextAware {
    private static ApplicationContext context;

    public ApplicationContext getApplicationContext() {
        return context;
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        context = ctx;
    }
}

2.4 生命週期初始化

2.4.1 InitializingBean && @PostConstruct

對象初始化接口,該接口有一個afterPropertiesSet方法,對象初始化&屬性注入之後調用方法
實現類: InitDestroyAnnotationBeanPostProcessor 處理 @PostConstruct

// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization
@Override
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;
}

2.4.2 DisposableBean && @PreDestroy

有初始化接口必然就有銷燬接口, 擴展點會在對象被銷燬時調用。
DestructionAwareBeanPostProcessor -> BeanPostProcessor

將此BeanPostProcessor應用於給定的bean實例,然後再對其進行銷燬,例如調用自定義銷燬回調。與DisposableBean的{@code destroy}和自定義的destroy方法一樣,此回調將只應用於容器完全管理其生命週期的Bean。
實現類: InitDestroyAnnotationBeanPostProcessor

// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeDestruction
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    try {
        metadata.invokeDestroyMethods(bean, beanName);
    }
    catch (InvocationTargetException ex) {
        String msg = "Invocation of destroy method failed on bean with name '" + beanName + "'";
        if (logger.isDebugEnabled()) {
            logger.warn(msg, ex.getTargetException());
        }
        else {
            logger.warn(msg + ": " + ex.getTargetException());
        }
    }
    catch (Throwable ex) {
        logger.error("Failed to invoke destroy method on bean with name '" + beanName + "'", ex);
    }
}

2.5 生命週期小例子

/**
 * 生命週期小例子
 *
 * @author 汪小哥
 * @date 22-06-2020
 */
@Slf4j
public class TestLifeService implements InitializingBean, DisposableBean, ApplicationContextAware {
    @PostConstruct
    public void postConstruct() {
        log.info("postConstruct");
    }

    @PreDestroy
    public void preDestroy() {
        log.info("preDestroy");
    }

    @Override
    public void destroy() throws Exception {
        log.info("destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("afterPropertiesSet");
    }

    public void initMethod() {
        log.info("initMethod");
    }

    public void destroyMethod() {
        log.info("destroyMethod");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("applicationContextAware");
    }
}
@Configuration
public class TestLifeConfiguration {

    @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
    public TestLifeService testLifeService() {
        return new TestLifeService();
    }
}

//  1  : applicationContextAware --> ApplicationContextAware
//  2  : postConstruct--------->@PostConstruct
//  3  : afterPropertiesSet--------->InitializingBean#afterPropertiesSet
//  4  : initMethod--------->@Bean(initMethod)
//  5  : preDestroy--------->@PreDestroy
//  6  : destroy--------->DisposableBean#destroy
//  7  : destroyMethod--------->@Bean(destroyMethod)

2.6 擴展點流程

org.springframework.context.support.AbstractApplicationContext#refresh
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
你會找到答案的~ 跟蹤代碼更好學習
在這裏插入圖片描述

1、收集BeanDefintion;

從xml 或者註解中收集到的BeanDefintion,從這裏開始 org.springframework.context.support.AbstractApplicationContext#refresh

2、postProcessBeanDefinitionRegistry

spring 擴展點 手動註冊 BeanDefintion 到 BeanDefinitionRegistry

例子 手動註冊Bean
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

RootBeanDefinition iabpp = new RootBeanDefinition(ImportAwareBeanPostProcessor.class);
iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);

3、postProcessBeanFactory;

BeanFactoryPostProcessor是針對容器的擴展點,加載BeanDefinition之後容器就會觸發該擴展點,實現了該接口的擴展點可以修改元信息也就是BeanDefinition對象中的信息,spring 的佔位符替換就是這樣的一種開箱即可使用的方案,在bean 還沒有初始化的時候就將數據中特殊標記熟悉進行佔位符的替換org.springframework.beans.factory.config.PropertyResourceConfigurer
代碼: BeanFactoryPostProcessor#postProcessBeanFactory;

4、doGetBean;

常見的applicationContext.getBean 這個是一切實例創建的起點,最終調用方法doGetBean
創建Bean的實例,後續的都是和 Bean實例相關,比如常見的實例代理、標記等等的實現都在實例擴展
BeanFactoryPostProcessor 容器擴展相關,BeanPostProcessor 實例擴展相關
代碼: org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

5、postProcessBeforeInstantiation;

這裏的回調是都還沒有沒有實例化,也就是還沒有反射初始化。
如果返回對象,直接結束Bean的創建
在目標bean被實例化之前應用這個BeanPostProcessor。
返回的bean對象可以是要使用的代理,而不是目標bean,從而有效地抑制了目標bean的默認實例化。
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation(Class<?> beanClass, String beanName)

5、doCreateBean;

創建Bean的實例,進行生命週期的初始化回調

6、createBeanInstance;

創建Bean的實例,進行生命週期的初始化回調
AbstractAutowireCapableBeanFactory#createBeanInstance

7、determineCandidateConstructors;

在createBeanInstance 內部 尋找構造函數的一個擴展
SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors

8、postProcessMergedBeanDefinition;

看名字合併bean的定義信息,運行時合併bean定義的後處理器回調接口。
The postProcessMergedBeanDefinition method may for example introspect the bean definition in order to prepare some cached metadata before post-processing actual instances of a bean.
1、在後處理bean的實際實例之前準備一些緩存的元數據
2、允許修改bean定義,但僅限於實際用於併發修改的定義屬性(只能修改 RootBeanDefinition)
AutowiredAnnotationBeanPostProcessor 的實現就是利用上訴兩個屬性

MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition

9、getEarlyBeanReference;

addSingletonFactory 將當前的實例創建到提前初始化的工廠裏面,singletonFactories 插入ObjectFactory 這個也是循環依賴的解決方案 doGetBean 獲取singletonObjects->earlySingletonObjects->singletonFactories
singletonFactories獲取到了將earlySingletonObjects插入。如果被引用直接返回一個代理對象getEarlyBeanReference

SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference

10、populateBean;

這裏將進行屬性填充

11、postProcessAfterInstantiation;

populateBean內部還有一個是否進行屬性填充的擴展,返回一個boolean
InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation

12、MutablePropertyValues;

populateBean內部 收集autowireByName、type的值放值在MutablePropertyValues
容器中,這個時候還沒有注入到Bean中

13、postProcessPropertyValues;

populateBean內部 給定屬性值應用於給定bean之前,對其進行後處理。允許檢查是否滿足所有依賴項,例如基於bean屬性設置器上的“必需”註釋。還允許替換要應用的屬性值,通常是通過基於原始屬性值創建新的MutablePropertyValues實例,添加或刪除特定值。
AutowiredAnnotationBeanPostProcessor 也是在這個時候基於之前收集的元數據進行注入屬性值
InstantiationAwareBeanPostProcessor#postProcessPropertyValues

14、applyPropertyValues;

populateBean內部 將收集到的MutablePropertyValues
注入到bean的實例中

15、initializeBean 填充後初始化Bean的生命週期;

  • invokeAwareMethods 特殊的Aware

BeanNameAware
BeanClassLoaderAware
BeanFactoryAware

  • 標記接口(ohter Aware)、代理等等處理都行

在invokeInitMethods之前的回調。
BeanPostProcessor#postProcessBeforeInitialization
@PostConstruct、

  • invokeInitMethods 由此 @PostConstruct、在初始化方法之前

InitializingBean、invokeCustomInitMethod

  • 初始化方法之後的回調,這裏也可以返回代理

BeanPostProcessor#postProcessAfterInitialization

16、檢查是否已經提前初始化;

earlySingletonReference 將這個對象作爲最後的值返回。
讓之前的引用保持一個

17、registerDisposableBeanIfNecessary;

處理最後的銷燬

三、spring 中的一些概念

如何理解BeanDefinition?

顧名思義,BeanDefinition是用來描述一個Bean的,Spring會根據BeanDefinition來生成一個Bean。

對象和Bean的區別?

所謂的bean也是一個java對象,只不過這個對象是通過spring定義的,早期的版本JavaBean xml 中管理 bean 所以叫做bean。

普通對象和Bean對象還有其他區別,因爲Bean對象是由Spring生成的,Spring在生成Bean對象的過程中,會歷經很多其他步驟,比如屬性注入,aop,new實例,調用初始化方法。

BeanFactory和FactoryBean的區別

BeanFactory

BeanFactory是Spring IOC容器的頂級接口,其實現類有XMLBeanFactory,DefaultListableBeanFactory以及AnnotationConfigApplicationContext等。BeanFactory爲Spring管理Bean提供了一套通用的規範。可以通過BeanFactory獲得Bean。

FactoryBean

FactoryBean首先也是一個Bean,但不是簡單的Bean,而是一個能生產對象的工廠Bean,可以通過定義FactoryBean中的getObject()方法來創建生成過程比較複雜的Bean。比如常見的使用場景 代理AOP的實現,dubbo 消費者實現等等。

如何理解BeanDefinitionRegistry和BeanFactory?

BeanFactory表示Bean工廠,可以利用BeanFactory來生成bean。
BeanDefinitionRegistry表示BeanDefinition的註冊表,可以用來添加或移除BeanDefinition。

如何理解@Import與ImportBeanDefinitionRegistrar?

通常我們實現EnableXXXX 都是通過@Import 跟上某個特殊的接口。
@EnableAsync、@EnableConfigurationProperties 等等 都是通過 @Configuration 在 BeanDefinitionRegistryPostProcessor 擴展中註冊Bean定義~ 然後間接了實現了部分的ImportBeanDefinitionRegistrar 這種特殊的接口的特殊處理,帶來了一些選擇性 ,比如@EnableAsync 選擇哪種代理類型、@EnableConfigurationProperties 註冊多個Bean的定義。

Import註解

@Import首先是一個註解,在Spring中是用來向Spring容器中導入Bean的。換個角度理解,就是我們一般都是通過在某個類上加上@Component註解來標誌一個bean的,但如果我們希望以一種更靈活的方式去定義bean的話,就可以利用@Import註解。
@Import註解所指定的類,在Spring啓動過程中會對指定的類進行判斷,判斷當前類是不是實現了比較特殊的接口,比如ImportBeanDefinitionRegistrar,如果存在特殊的接口就執行特殊的邏輯,如果沒有則生成該類對應的BeanDefinition並放入BeanFactory中。

ImportBeanDefinitionRegistrar

通過Import註解可以註冊bean,雖然它也支持同時註冊多個bean,但是不方便,特別是當我們想通過實現一些複雜邏輯來註冊bean的話,僅僅通過Import註解是不方便的,這時就可以使用ImportBeanDefinitionRegistrar這個接口來動態的註冊bean了,我這裏說的註冊bean指的是:通過生成BeanDefinition,並且把BeanDefinition放入BeanFactory中。

四、看完之後需要理解的幾個問題

循環依賴如何解決的?

https://blog.csdn.net/chejinqiang/article/details/80003868

有哪幾個地方可以返回代理,代理如何處理的?

  • 在實例化之前返回代理對象org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
  • 循環依賴處理返回代理對象org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference
Object getEarlyBeanReference(Object bean, String beanName) throws BeansException;
  • 正常的後置處理器返回代理對象
org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization

org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization

BeanFactoryPostProcessor and BeanPostProcessor

BeanFactoryPostProcessor是針對容器的擴展點,加載BeanDefinition之後容器就會觸發該擴展點,實現了該接口的擴展點可以修改元信息也就是BeanDefinition對象中的信息。
BeanPostProcessor 是實例的擴展點,(在熟悉填充完成之後,對於實例的擴展),但是這個不是絕對的哦~ 具體看上面的流程。
在這裏插入圖片描述

手動編程如何注入一個Bean?

https://blog.csdn.net/qq_22076345/article/details/105892077 上面講解擴展點的時候說dubbo 手動註冊bean ,這個是理解spring的一個核心,各種各樣的框架對於spring的擴展都是基於這個,那這個什麼時機好呢?BeanDefinitionRegistryPostProcessor 這個不能再好了。
**

接着上一個問題 如何注入一個Bean,@Configuration 如何實現?

@Configuration 是一個非常重要的註解 不光是@Bean @Enable… @PropertySource @ComponentScan @ImportResource 等等的實現,其實也就是 BeanDefinitionRegistryPostProcessor 擴展點中去處理的,然後通過BeanDefinitionRegistry.registerBeanDefinition bean 實現,然後注入到BeanDefinitionRegistry bean 定義的元數據倉庫中,具體可以查看,那麼對於好舒服、dubbo、nacos等等註解處理原理理解還難? org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
這裏需要理解好,實例的創建,和 BeanDefinition 信息的構造兩個問題。切記: 不要違反spring的處理原則,在BeanFactoryPostProcessor 實例化bean。

@PropertySource 和 PropertyResourceConfigurer

上面的問題@Configuration 實現了這麼多邏輯,那麼 @PropertySource和 PropertyResourceConfigurer熟悉值替換衝突?回答是不衝突的~
BeanDefinitionRegistryPostProcessor 繼承了 BeanFactoryPostProcessor,允許在常規BeanFactoryPostProcessor檢測開始之前註冊更多的bean定義,PropertyResourceConfigurer 繼承了 BeanFactoryPostProcessor ,由於 BeanDefinitionRegistryPostProcessor 優先於父類,因此這裏的收集的環境屬性優先於 PropertyResourceConfigurer 處理環境變量值的替換。

五、參考

Spring循環依賴及解決方式
Spring BeanWrapper分析
Spring 手動註冊bean
Spring - lookup-method方式實現依賴注入
Spring生成bean的過程
Spring擴展點總結

六、總結

spring 代碼較多,看着也比較頭疼,可能看着看着就忘了… 畢竟開發框架、中間件 擴展的機會還是比較少,但是對於spring的使用還是必須銘記於心,本文主要是給自己總結~沒事的時候回來看看。–2020-06-22 於杭州

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章