spring源碼

spring源碼

一、IOC模組:組件註冊

1.原始方式,是通過配置xml配置文件,寫bean類。使用如下ClassPathXmlApplicationContext獲取類。

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");  
RegisterDAO registerDAO = (RegisterDAO)ac.getBean("RegisterDAO");  

2.註解方式,通過@Configuration+@Bean的方式。其中@Scope可以配置單例(只需一份實例)還是多例(適用於並發環境,但其實我們還是更傾向於使用程式碼控制並發),request還是session。區別:單例在spring啟動時,不管調不調用直接進入spring容器(因為是單實例嘛,所以乾脆直接加入了)。而多例只在使用時才加入,且每次使用都會初始化一個新的類。

由此才有了@Lazy去懶載入,保證默認的單例不會加入容器,只有使用時才會。

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        //ctx.register(AppConfig.class);相當於讀哪份配置文件
        ctx.refresh();
        Entitlement ent = (Entitlement)ctx.getBean("entitlement");

重點:@Component主要注入自己寫的類交給spring管理,@Bean注入第三方包交給spring管理。

但是實際上對於無參的第三方包可以直接通過@import(類.class)導入,方便多了

3.包掃描作用:讓@Component,@Controller這些註解自動注入spring管理,並且自動裝配。否則得用application對象完成這些操作。

同樣的,xml需要有一句話配置,註解也需要有個註解。@ComponentScan

@ComponentScan(裡面的確還有很多配置,比如掃描的包,去除和只包含哪些)其實註解裡面方法就是這樣配置 方法名=””

@ComponentScan(value = "com.nrsc.springstudy.c1_componentscan1.study2.config2_test",
        includeFilters = {
                @Filter(type = FilterType.ANNOTATION, classes = {Component.class})},
        useDefaultFilters = false)

總結:

1.配置的所有@Component等註解,spring不能主動進行管理。所以得要我們告訴他哪些需要管理,所以才有了@ComponentScan。

二、生命周期

可自定義bean的初始化和銷毀。@Bean(initMethod= ,destoryMethod=)

1.通過@Bean註解中配置初始化和銷毀的方法。(單例是執行初始化和銷毀的方法。但是多例是不會調用銷毀方法,spring要求自己銷毀)
2.通過bean實現介面initializingBean,afterPropertieset()方法定義初始化,通過實現DisposableBean介面,destory()定義銷毀方法。

(Timing:對象創建並賦值之後)

3.@PostConstruct配置初始化,@PreDestory配置銷毀

(Timing:對象創建並賦值之後)

4.bean的後置處理器beanPostProcessor介面。postProcessBeforeInitialization,postProcessAfterInitialization

(Timing:對象創建並賦值之後,在其他任何初始化調用之前,在初始化調用之後。初始化前後。作用:在bean初始化前後完成操作)

註:1.spring底層用beanPostProcessor也用的非常多。2.必須在對象裝配完屬性之後

大概過程:

  1. bean實例化

    finishBeanFactoryInitialization
    getBean(就是doGetBean,只是一層包裝)
    doGetBean 
    getSingleton
    createBean
    doCreateBean(包含了populateBean,initializeBean的全過程,即使通過這個方法來完成後面的方法)
    以下是2,3,4是docreateBean中的執行方法。
    
  2. createBeanInstance 通過bean的name得到一個BeanWrapper,通過這個調用getWrappedInstance就可以得到我們要的bean。

  3. 屬性賦值 Populate populateBean() -> 屬性賦值 (AutowiredAnnotationBeanPostProcessor,一般是這個進行屬性裝配)

  4. 初始化 Initialization initializeBean() -> 初始化 (上面的4種方法都是圍繞這個)

    initializeBean的過程如下:

    applyBeanPostProcessorsBeforeInitialization 遍歷取出所有的beanPostProcessor,進行執行相應的前置處理
    invokeInitMethods 初始化方法(就是我們定義的前幾種初始化的方法,執行init-method、postConstract註解的方法、afterPropertiesSet)
    applyBeanPostProcessorsAfterInitialization  遍歷取出所有的beanPostProcessor,進行執行相應的後置處理
    
  5. 銷毀 Destruction

bean的賦值(@value),注入其他組件(xxxAware所有介面),@Autowired(AutowiredAnnotationBeanPostProcessor,其實查看源碼可以發現,他還支援@value,@autowired,@inject),生命周期註解功能(@PostConstruct屬於InitDestroyAnnotationBeanPostProcessor)…..等等功能,都是beanPostProcessor的實現類實現的。

三、快速獲取application對象

1.實現Spring的ApplicationContextAware介面,重寫setApplicationContext方法,將得到的ApplicationContext對象保存到一個靜態變數中,有了這個上下文對象,就可以在項目的任意地方用它來得到任意Bean;

2.直接注入對象

 @Autowired
 private ApplicationContext applicationContext;

四、屬性賦值(AutowiredAnnotationBeanPostProcessor)

1.@Vlaue(${}):取出配置文件中的值(可以指定讀取配置)。@Vlaue(#{}):使用Spel表達式取值

2.@Autoired:默認是按類型去容器中找組件。如果找到多個,默認按屬性名作為id。加上@Qualifer可以直接指定名字。默認必須裝配成功,裝配失敗則報錯。當然裡面也有屬性可以配置。@Autowired(required =false)

3.@Resource java規範註解,僅提供按名稱注入。

4.@Inject java規範註解額,需要額外導包,和@Autoired效果一樣。但是不支援required。

重點介紹:@Autoired

作用域:@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

即可以加到構造器,方法,屬性等上。

@Autoired//加在方法上,傳入的參數從容器中拿。加在參數上也是一樣。用構造器也是這樣,但是對於只有一個有參構造器,可以省略註解
public void setCar(Car car){
		this.car=car;
}

5.獲取spring底層組件,實現XXXAware介面。(Aware類型的介面可以得到很多spring的組件)

如:ApplicationContextAware,BeanFactoryAware等。也不難,在實現這些介面,都會要求重寫set方法,底層無非就是通過判斷當前類是否實現了XXXAware,如果是,則調用剛才實現的set方法進行注入。

底層依賴ApplicationContextAwareProcessor,是BeanPostProcessor的實現類。所以對於獲得spring底層組件的類基本都是利用了BeanPostProcessor,且多以XXXAware結尾。並在postProcessBeforeInitialization中進行判斷是否是ApplicationContextAware類(裡面還判斷是否其他類,如上下文環境等),如果是,則賦值applicationContext。

對於這類方法必定會有一個setxxxxx()方法。用於注入對象。

五、AOP

JoinPoint這個參數必須寫參數表的第一位,寫在後面無法生效。

1.寫切面記得用@Aspect。

2.在切面的通知方法中必須標註註解,並寫切入點表達式。

3.開啟aop註解模式@EnableAspectJAutoProxy,不然上面註解都不能生效。

源碼:@EnableAspectJAutoProxy做了哪些事?

通過導入@Import(AspectJAutoProxyRegistrar.class),這個類創建一個自動代理創建器AnnotationAwareAspectJAutoProxyCreator。

可以發現最後還是實現了BeanPostProcessor介面和Aware介面。

diagram

Tags: