(Spring Boot教程二 )關於pom.xml文件和註解的一些深入探索

    緊接着上一個教程博客(Spring Boot教程一 )Spring Boot入門(簡介,一個HelloWorld項目構建、內部原理分析)。上一篇博客我們編輯了項目的pom.xml文件,並在編寫項目的時候爲類、方法添加了一些註解,那麼這些內容是如何在Spring Boot當中工作的呢?他們的的作用又是什麼?作爲一個純新手,我表示十分好奇。接下來的內容基於網絡資料和自己的理解,可能不是很官方,如果有什麼地方理解不到位,希望看到的大佬幫我及時指出,謝謝。

一、關於pom.xml文件

    回顧一下我們之前配置的pom.xml文件,完整版如下圖。pom文件是maven項目的配置基礎,包括版本號、artifactId等多個信息,它讓我們的項目自動導入了很多相關的依賴,也就是我們在項目的External Library中所看見的jar包們。

    在之前的HelloWorld的項目中,我們主要填充的部分是父pom、依賴組、項目構建所需依賴,即下圖畫框的這三個部分。其他內容皆爲項目自動生成。這三個部分就足夠支撐我們的項目運行起來。我們這裏也會分成三部分介紹。

1、父pom:依賴管理。我們現在使用的是org.springframework.boot中的spring-boot-starter-parent作爲我們的父pom,我們ctrl點擊進去,發現spring-boot-starter-parent的父pom是spring-boot-dependencies。在spring-boot-dependencies的文件中,我們可以看到規定了一些依賴的版本號,也有人因此稱這個文件爲Spring Boot版本的仲裁中心。

    但注意兩點。

(1)不是這裏的所有依賴都是會被引入進來的。項目具體會引入哪些依賴還是要看我們dependencies裏寫了什麼,所以這裏很多地方會報紅,不要擔心。

(2)如果我們需要引入的依賴這裏沒有出現,還是需要寫版本號的。

    比如,我們在項目的pom文件中引入的spring-boot-starter-web,在pring-boot-dependencies中可以看到它的完整引入,如下。

2、依賴pring-boot-starter-web。這是spring boot的web場景啓動器,我們仍然打開這個文件,看到下圖中的內容。如果我們的項目引入了這個依賴,就相當於我們導入了所有能使web模塊正常運行的相關組件依賴,依賴版本受父pom仲裁。

    這裏我們有必要單獨說一下starter——啓動器,Spring Boot包含了很多針對於不同場景的啓動器。我們在Spring Boot的官網上可以看到不同版本的Spring Boot,我們隨便選擇一個,查看它的Reference,如下圖。

    啓動器是一些項目中的一些列依賴。Spring Boot將一些常見的場景(功能)及它所需要的一些依賴進行抽取,做成啓動器,那麼只需要引入啓動器,就可以引入全部的相關依賴。

    在這個文檔裏也可以看到裏面介紹了Spring Boot這個版本的相關信息,包含的不同Starter和它的簡要描述以及pom引入方法。

3、maven構建配置:Spring Boot的maven插件。裏面是maven的一些相關的依賴。如果沒有這個部分,我們還是可以將項目打包成jar包,但是,我們無法使用“java -jar 包名”進行佈置,會報錯。

二、關於@SpringBootApplication註解

    我們主要起到作用的註解是@SpringBootApplication,這是一個Spring Boot應用註解,用於標註一個類,說明這是程序的主程序類,也是程序的入口,程序應該從這個類的main方法進入。通過這個註解我們完成了依賴的導入和項目的運行的一些配置,使我們的Spring Boot使用起來更加方便。

    我們通過ctrl+鼠標點擊進入這個文件。和我們前面分析pom文件類似,因爲我們前面需要研究的是配置之間的關係,所以我們點進文件中主要看到是文件的配置信息,這裏我們點進去主要關注的是文件的註解。@SpringBootApplication文件的完整註解如下圖,大致分爲兩個部分,四個元註解和其他的註解。四個元註解是我們看Spring Boot註解文件時經常看到的,它們專門用來註解註解文件,我們先來介紹他們,然後介紹真正讓@SpringBootApplication起到作用的另外三個註解。

    元註解歸屬於java.lang.annotation這個包下,我們在IDEA的左側邊欄看一下這個包的情況,如下圖,因爲java的包太多,所以我們的圖片展示是拼接而成,如果你想要直接看到這個包,點任意一個元註解的源文件上面的包信息就可以看到。這個包內一共有六個註解文件,但只有@Target、@Retention、@Documented、@Inherited四個被稱爲元註解。

1、元註解@Target({ElementType.TYPE}):表示這個文件所定義的註解文件是用來註解類、接口、枚舉、註解類型,事實上,它所定義的註解@SpringBootApplication的確是用來註解Spring Boot主入口類的。

    我們來更詳細地說一下@Target這個註解。我們之前說過元註解是專門用來註解註解文件的。這個註解的直譯是目標,也就是說,@Target生命這個註解作用的範圍是什麼,它可以註解什麼。後面只允許帶一個枚舉類型的ElementType參量。這個參量一共有10個類型,我們點進去看看它的源碼。下圖我做了一些標註,前8個除了ANNOTATION_TYPE之外都是比較常用的,我們在Spring Boot註解文件中通常看到的都是TYPE類型。

2、元註解@Retention(RetentionPolicy.RUNTIME),Retention原譯爲滯留,這個註解是用來告訴編譯器所定義的註解的保留時間,裏面仍然只能帶一個枚舉類型變量,有三個取值。在解釋這三個取值時,我們必須要了解java運行的幾個步驟,一個是java文件在運行前會被編譯器編譯成.class文件;一個是java的反射機制,允許java獲取自身信息。簡單來理解反射機制的一個功能,就是你在編譯器中輸入@,編譯器就會自動幫你聯想到所有當前類可引用的註釋信息一樣。

    下面我們來解釋這三個枚舉變量的取值,對所定義註解所產生的影響。

3、元註解@Documented:被標註的註解所標註的註解會被javadoc記錄,併成爲API的一部分。舉個例子,註解A被標註@Documented,B被A標註,則用javadoc對B類生成API文檔時,在類聲明上會出現註解A。

4、元註解@Inherited:說明定義的標註所標註的子類可以繼承這個標註,比如註解A被@Inherited標註,類B被註解A標註,類C繼承類B,那麼即使類C的文件中不出現註解A依然默認繼承了這個註解。

5、@SpringBootConfiguration:Spring Boot配置文件,表示所標註的類爲一個Spring Boot配置類。我們依次往下點擊文件內容,如下圖,可以看出來@SpringBootConfiguration的底層是Spring的@Configuration配置標註,用來標註Spring配置類的一個註解。配置類實際上就是配置文件,是一個組件,相當於更加便利地實現配置文件的功能。

6、@EnableAutoConfiguration:啓動自動配置註解,使Spring Boot開啓自動配置,進行組件掃描等操作。它的文件內部有兩個註解,如下圖。@AutoConfigurationPackage爲自動配置包註解,幫助編譯器獲得@SpringBootApplication所標記的主配置類所在目錄下的所有組件。@Import({AutoConfigurationImportSelector.class})自動配置選擇器,爲容器導入該場景下的所需要的自動配置組件,並以全類名的方式返回,並配置好這些組件。

    這裏先展示一下相關文件的一個調用關係和起到作用的函數:

(1)我們詳細來看一下@AutoConfigurationPackage是如何實現功能的。我們點擊獲得@AutoConfigurationPackage的文件內容,其中含有@Import({Registrar.class})的註解。@Import是Spring的底層註解,表示引入()中的類,那麼表示起作用的即爲Registrar.class。我們點開Registrar.class,裏面有一個函數爲registerBeanDefinitions(),這個函數起到的作用就是獲得主配置類下全部的組件放入註冊器中。下面是registerBeanDefinitions()的完整代碼:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
        }
    }

        通過調試,我們可以看到metadata和執行後的registry的數據。metadata裏面的introspectedClass裏面是我們的項目名稱,(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()經過計算後的結果爲com.springboot,即我們通過這個程序語句獲得了項目所在的包,然後我們將包下的組件注入到registry中,可以看到beanDefinitionMap中已經注入了我們項目中的兩個組件。

(2)接下來我們看一下@Import({AutoConfigurationImportSelector.class})中的AutoConfigurationImportSelector.class自動配置引用選擇器如何工作。AutoConfigurationImportSelector.class中主要起到作用的getAutoConfigurationEntry函數。因爲這個函數在我查閱資料的過程中,發現內容有所出入,所以我這裏就不粘函數的代碼了,還是以你當前的版本爲主。但是基本上的返回值都與一個List<String>有關,我們通過調試之後發現返回參數內容如下,都是一些自動配置的文件。

7、@ComponentScan:依然在這裏介紹一篇很全面的博客——Spring註解——使用@ComponentScan自動掃描組件,@ComponentScan主要就是定義掃描的路徑從中找出標識了需要裝配的類自動裝配到spring的bean容器中,我們也可以使用這個註解排除一些不需要裝配的類。

    @ComponentScan源文件中展示的參數有很多,但主要用到的參數只有四個:value、includeFilters()、excludeFilters、useDefaultFilters()。value確認掃描範圍,includeFilters()和excludeFilters()可用於指定加載和排除哪些裝配,useDefaultFilters()的值默認爲true,即默認掃描指定路徑下的所有Component,像@Controller、@Service、@Repository等註解的底層都是@Component,所以也會被掃描進去。

   單獨說一下includeFilters()和excludeFilters(),它所指定的類型都是@Filter的,所以我們一起看,@Filter代碼比較簡單,如下

public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;

        @AliasFor("classes")
        Class<?>[] value() default {};

        @AliasFor("value")
        Class<?>[] classes() default {};

        String[] pattern() default {};
    }

    classes用來放class文件,裏面是關於裝配類的限制。type類型爲FilterType,源代碼如下,通常使用的是ANNOTATION和CUSTOM。

三、關於Controller文件中的註解

    Controller文件中的註解相比主配置類的簡單一些。下面的代碼是我們之前項目中的Controller代碼,有三個註解,@Controller、@ResponseBody、@RequestMapping這三個註解。

package com.springboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloController {
    @ResponseBody
    @RequestMapping("/hello")
    public String hello(){
        return "Hello World";
    }
}

1、@Controller表示這是一個控制類,用於控制類之前使用,底層是Spring的@Component組件。

2、@ResponseBody響應體註解。首先我們要先對REST API表現層狀態轉移有一定的瞭解。其實就是一種構建風格,淺層理解上來說,就是從傳統的頁面跳轉,編程直接進行頁面請求,然後將數據交給網頁進行加載。以我們的項目爲例子,我們直接通過網址向網頁發出“\hello”界面的請求,然後把“hello world”信息交給網頁,讓其進行展示。

     那麼@ResponseBody是REST API開發的一個必要的註解。它可用於標記在方法上或者是類上,如果加在類上表示這個類的所有方法返回的數據直接寫給瀏覽器。@ResponseBody和@Controller組合使用標記類的時候可以直接使用@RestController替代。

    @ResponseBody的作用有兩個,轉自@RequestBody, @ResponseBody 註解詳解

(1)用於讀取Request請求的body部分數據,使用系統默認配置的HttpMessageConverter進行解析,然後把相應的數據綁定到要返回的對象上;

(2)再把HttpMessageConverter返回的對象數據綁定到 controller中方法的參數上。

3、@RequestMapping:用於標記方法,會將 HTTP 請求映射到 MVC 和 REST 控制器的處理方法上,也是REST API開發的重要註解。

 

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