【Spring源碼分析】預備篇

前言

最新想學習一下Spring源碼,開篇部落格記錄下學習過程,歡迎一塊交流學習。

作為預備篇,主要演示搭建一個最簡單的Spring項目樣例,對Spring進行最基本梳理。

 構建一個最簡單的spring項目

Spring中最核心的Jar包有四個:spring-beans、spring-context、spring-core、spring-expression。

以前做spring項目有個誤區,什麼包都一個個導入進來,其實一個最最簡單的Spring項目,理論上就需要引入一個jar包spring-context就夠了,靠它的依賴關係,其他核心包都會自動導入進來(只是核心包,實際真實項目需要其他功能再額外導入)。

1、在一個空項目里 添加Spring項目模組       Maven->jdk1.8>webapp 模板

2、這裡在pom中只額外添加 spring-context依賴(它依賴其他幾個核心jar包 會自動導入依賴)

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>

    <spring.version>5.1.3.RELEASE</spring.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>LATEST</version>
    </dependency>

  </dependencies>

 實際引入的包及依賴關係:

 
這樣,一個最簡單的spring項目就搭建好了,我們測試一下:
定義一個實體類:
 1 package cn.eport.jason.bean;
 2 
 3 @Component
 4 public class Student {
 5 
 6     private String username = "WPC";
 7 
 8     private String password;
 9 
10     public String getUsername() {
11         return username;
12     }
13 
14     public void setUsername(String username) {
15         this.username = username;
16     }
17 
18     public String getPassword() {
19         return password;
20     }
21 
22     public void setPassword(String password) {
23         this.password = password;
24     }
25 }

 寫一個測試方法,(因為本例中是在源程式碼下編寫的單元測試,所以需要先在pom中去掉Junit 作用域為test的限制,不然在業務程式碼區使用@Test註解時會提示找不到)

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <!--<scope>test</scope>-->
    </dependency>

 MyTest.java 測試類

package cn.eport.jason.test;

import cn.eport.jason.bean.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {

    @Test
    public void Test1(){
        //基於註解的方式載入Spring容器
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext("cn.eport");

        Student student =(Student) applicationContext.getBean("student");
        System.out.println(student.getUsername());
    }
}

 運行單元測試方法Test1 控制台輸出結果:

D:\DevTool\Java\jdk1.8.0_101\bin\java.exe ... cn.eport.jason.test.MyTest,Test1
WPC

Process finished with exit code 0

 說明Spring ioc容器功能已生效,已屬於spring項目。

spring容器啟動的4種方式

上例中使用的是使用註解的方式載入的Spring容器,其實spring還有其他幾種載入容器的方式:
  • 1、類路徑獲取配置文件載入容器

  ApplicationContext applicationContext= new ClassPathXmlApplicationContext(“spring.xml”);

  • 2、文件系統路徑獲取配置文件【絕對路徑】不常用

  ApplicationContext applicationContext = new FileSystemXmlApplicationContext(“E:\\idea\\public\\springdemo\\src\\main\\resources\\spring.xml”);

  • 3、無配置文件載入容器(註解方式) 單元測試中常用

  ApplicationContext applicationContext = new AnnotationConfigApplicationContext(“cn.eport”);

  • 4、springboot載入容器

  ApplicationContext applicationContext = new EmbeddedWebApplicationContext();

 spring容器啟動可以作為spring的源碼分析的入口,上述四種方式中,除了基於註解的方式外,之前比較常用的就是讀取類路徑下配置文件載入容器了(SpringBoot出現之後,這種方式慢慢過時),我們來使用這種方式做下測試:
 1、新建配置文件:idea下 在resources目錄下右擊new->XML Configuration File->springconfig 創建Spring.xml 配置文件,並添加註冊bean配置(兩種方式 包掃描或者bean定義註冊):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xmlns:context="//www.springframework.org/schema/context"
       xsi:schemaLocation="//www.springframework.org/schema/beans 
          //www.springframework.org/schema/beans/spring-beans.xsd
          //www.springframework.org/schema/context
          //www.springframework.org/schema/context/spring-context.xsd"
> <!-- 方式1、批量註冊 包掃描+註解 --> <context:component-scan base-package="cn.eport"/>
    <!-- 方式2、手動註冊bean 。 id不寫 默認為類全限定名--> 
<bean class="cn.eport.jason.bean.Student"> </bean> </beans>

這裡通過兩種方式註冊了Student實例:

  • 一個ComonentScan掃描+註解的方式(BeanName默認類名小寫)
  • 一個配置註冊bean的方式(默認BeanName為類全限定名)

ps:<context:component-scan>這種帶有前綴的標籤 為自定義標籤,spring原始組件只有 spring-core和spring-bean ,常用的context、aop、tx組件其實都是擴展組件,Spring靠的就是自定義標籤在核心組件上擴展使得spring更加強大,在配置文件中使用擴展標籤需要引入相應的xsd;在標籤對應的jar包下面找到對應的spring.schemas,在這個文件中就會有對應的XSD路徑和命名空間xmlns(xml namespaceUri) (類似於java中的Import)

2、編寫對應的測試方法 使用 ClassPathXmlApplicationContext 讀取配置文件載入容器獲取bean對象 進行測試:

    @Test
    public void Test2(){
        //讀取類路徑下xml配置文件的方式載入spring容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring.xml");

        Student student1 =(Student) applicationContext.getBean("student");//取得為包掃描+註解 方式註冊的 beanId是類名小寫
        Student student2 =(Student) applicationContext.getBean("cn.eport.jason.bean.Student");//取得為配置文件中預設id 的那個bean
        System.out.println(student1.getUsername());//WPC
        System.out.println(student2.getUsername());//WPC

        System.out.println(student1==student2);//false   不同的註冊方式註冊的兩個不同的實例對象
    }

 結果:

WPC
WPC
false

Process finished with exit code 0

Spring容器一樣也載入成功了,由此可以看出項目中只添加Spring-context 的依賴,一個最基礎的Spring的項目即可搭建好(這裡說的不是SpringMVC工程)

 根據測試列印結果可以發現個問題,程式並沒有列印出Spring的日誌,原因為pom中未添加spring日誌依賴logback:

    <!--一個空的spring工程是不能列印日誌的,要導入spring依賴的日誌jar-->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>LATEST</version>
    </dependency>

 再次執行下測試方法,控制台列印出Spring的日誌:

 

 由此,構建一個帶日誌的最基礎Spring項目,只需要在pom中添加context、logback兩個依賴即可

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!--一個空的spring工程是不能列印日誌的,要導入spring依賴的日誌jar-->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>LATEST</version>
    </dependency>

但是實際上由於jar包相互依賴,項目中maven會自動導入其他依賴,實際導入的包遠遠不止這兩個:

實際導入的包及依賴關係如下:

結語

作為開篇內容相對簡單些,經過演示我們看到了pom中只添加context、logback兩個依賴就可以搭建一個帶日誌最基本的spring項目,以及我們知道了spring源碼的入口類,後續我們進行源碼分析~