註解源碼
1.Spring的註解可以分爲兩類,一個是類上的註解,如@Component; 一個是類內部的註解,如@Autowired;Spring對兩種形式的註解的處理是不同的,在Spring的初始化週期中註解生效的時間也不同。
2.使用實例來分析一下(至於項目搭建的步驟見系列第一部分,源碼梳理(一))
App.java
package com.mycompany.app;
import org.springframework.stereotype.Component;
/**
* Hello world!
*
*/
@Component
public class App
{
public String appString = "This is App";
}
God.java
package com.mycompany.app;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class God {
@Autowired
public App app;
public void godSay(){
System.out.println("God.godSay():"+app.appString);
}
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("SpringBean.xml");
God god = (God)applicationContext.getBean("god");
god.godSay();
}
}
SpringBean.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"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<context:component-scan base-package="com.mycompany.app" />
</beans>
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>myapp</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>myapp</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
運行God.java,得到的結果是:
3.首先可以做以下猜想
1)對於上面的兩種註解,Spring讀取相關的字節碼文件,然後找到相關的關鍵字;針對關鍵字做處理。
2)對於類註解,直接註冊類信息,放入DefinitionMap;對於類內部的註解,如何把信息注入進去的?有兩種猜測
A:之前的源碼分析中提到了,BeanFactoryPostProcessor,它也被稱作容器的後置處理器,因爲它是在Spring容器加載完所有的Bean信息之後(是加載Bean,不是初始化)調用的處理器。 所以猜測註解可能是由BeanFactoryPostProcessor來實現的。
B:之前的源碼分析中也提到了BeanPostProcessor,它包括了兩個方法:postProcessBeforeInitialization和postProcessAfterInitialization;它們分別被稱爲Bean的前置和後置處理器;因爲它們分別在Bean的初始化前後調用。所以猜測註解也可能是這兩個方法來實現的。
4.調試,對代碼進行分析
1)首先通過加載配置文件然後解析相關類的過程是在BeanFacroty的初始化過程中;所以我們分析的是obtainFreshBeanFactory方法:
一直debug到前面文章講到過的parseBeanDefinitions方法:
我們的context節點屬於一般性的配置節點,進入parseCustomElement方法:
然後進入parse方法:
它有多個實現類,我們的配置文件中的一個配置是<context: componentScan/>,所以我們進入ComponentScanBeanDefinitionParser方法:
basePackage指的是我們要掃描package,basepackages指的是package下面的java類; doScan方法對其中每一個java類進行掃描(包括類上的和類內部的註解),進入doScan方法:
2)然後順着findCandidateComponents方法
3)通過一點點分析postProcessBeanDefinition方法,發現實際上它是在beanDefinition中設置相關的屬性, 通過設置自動注入的屬性(從而在註冊bean處理器時發現它,使它繼承AutowiredAnnotationBeanPostProcessor接口),然後在初始化時,在Bean的後置處理方法中開始注入。這就說明猜想2中的B是正確的,而且注入實際上是bean後置處理器實現的,既postProcessAfterInitialization。
至於processCommonDefinitionAnnotations方法,就是對類上的bean進行處理, 然後設置beanDefinition中的屬性, 最後在初始化的時候對類進行初始化(沒有註冊BeanPostProcessor的過程),也證實猜想1是正確的。
總結如圖:
類內的註解經過了1,2,3三個方法,而類上的註解經過了1,3兩個方法。 分析到此,註解生效的整個生命週期就很清楚了。