1. 程式人生 > >Spring中毒太深,離開Spring我居然連最基本的介面都不會寫了

Spring中毒太深,離開Spring我居然連最基本的介面都不會寫了

# 前言 隨著 `Spring` 的崛起以及其功能的完善,現在可能絕大部分專案的開發都是使用 `Spring(全家桶)` 來進行開發,`Spring`也確實和其名字一樣,是開發者的春天,`Spring` 解放了程式設計師的雙手,而等到 `SpringBoot `出來之後配置檔案大大減少,更是進一步解放了程式設計師的雙手,但是也正是因為`Spring`家族產品的強大,使得我們習慣了面向 `Spring` 開發,那麼假如有一天沒有了 `Spring`,是不是感覺心裡一空,可能一下子連最基本的介面都不會寫了,尤其是沒有接觸過`Servlet`程式設計的朋友。因為加入沒有了 `Spring` 等框架,那麼我們就需要利用最原生的 `Servlet` 來自己實現介面路徑的對映,物件也需要自己進行管理。 # Spring 能幫我們做什麼 `Spring` 是為解決企業級應用開發的複雜性而設計的一款框架,`Spring` 的設計理念就是:簡化開發。 在 `Spring` 框架中,一切物件都是 `bean`,所以其通過面向 `bean` 程式設計(BOP),結合其核心思想依賴注入(DI)和麵向切面((AOP)程式設計,`Spring` 實現了其偉大的簡化開發的設計理念。 ## 控制反轉(IOC) `IOC` 全稱為:Inversion of Control。控制反轉的基本概念是:不用建立物件,但是需要描述建立物件的方式。 簡單的說我們本來在程式碼中建立一個物件是通過 `new` 關鍵字,而使用了 `Spring` 之後,我們不在需要自己去 `new` 一個物件了,而是直接通過容器裡面去取出來,再將其自動注入到我們需要的物件之中,即:依賴注入。 也就說建立物件的控制權不在我們程式設計師手上了,全部交由 `Spring` 進行管理,程式要只需要注入就可以了,所以才稱之為控制反轉。 ## 依賴注入(DI) 依賴注入(Dependency Injection,DI)就是 `Spring` 為了實現控制反轉的一種實現方式,所有有時候我們也將控制反轉直接稱之為依賴注入。 ## 面向切面程式設計(AOP) `AOP` 全稱為:Aspect Oriented Programming。`AOP`是一種程式設計思想,其核心構造是方面(切面),即將那些影響多個類的公共行為封裝到可重用的模組中,而使原本的模組內只需關注自身的個性化行為。 `AOP` 程式設計的常用場景有:Authentication(許可權認證)、Auto Caching(自動快取處理)、Error Handling(統一錯誤處理)、Debugging(除錯資訊輸出)、Logging(日誌記錄)、Transactions(事務處理)等。 ## 利用 Spring 來完成 Hello World 最原生的 `Spring` 需要較多的配置檔案,而 `SpringBoot` 省略了許多配置,相比較於原始的 `Spring` 又簡化了不少,在這裡我們就以 `SpringBoot` 為例來完成一個簡單的介面開發。 - 1、新建一個 `maven` 專案,`pom` 檔案中引入依賴(省略了少部分屬性): ```xml ``` - 2、新建一個 `HelloController` 類: ```java package com.lonely.wolf.note.springboot.demo; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/hello") public class HelloController { @GetMapping("/demo") public String helloWorld(String name){ return "Hello:" + name; } } ``` - 3、最後新建一個 `SpringBoot` 啟動類: ```java package com.lonely.wolf.note.springboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication(scanBasePackages = "com.lonely.wolf.note.springboot") class MySpringBootApplication { public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class, args); } } ``` - 4、現在就可以輸入測試路徑:`http://localhost:8080/hello/demo?name=雙子孤狼` 進行測試,正常輸出:`Hello:雙子孤狼`。 我們可以看到,利用 `SpringBoot` 來完成一個簡單的應用開發非常簡單,可以不需要任何配置完成一個簡單的應用,這是因為 `SpringBoot` 內部已經做好了約定(約定優於配置思想),包括容器 `Tomcat` 都被預設整合,所以我們不需要任何配置檔案就可以完成一個簡單的 `demo` 應用。 # 假如沒有了 Spring 通過上面的例子我們可以發現,利用 `Spring` 來完成一個 `Hello World` 非常簡單,但是假如沒有了 `Spring`,我們又該如何完成這樣的一個 `Hello World` 介面呢? ## 基於 Servlet 開發 在還沒有框架之前,程式設計式基於原始的 `Servlet` 進行開發,下面我們就基於原生的 `Servlet` 來完成一個簡單的介面呼叫。 - 1、`pom` 檔案引入依賴,需要注意的是,`package` 屬性要設定成 `war` 包,為了節省篇幅,這裡沒有列出 `pom` 完整的資訊: ```xml ``` - 2、在 `src/main` 下面新建資料夾 `webapp/WEB-INF`,然後在 `WEB-INF` 下面新建一個 `web.xml` 檔案: ```xml ``` 這裡面定義了 `selvlet` 和 `servlet-mapping` 兩個標籤,這兩個標籤必須一一對應,上面的標籤定義了 `servlet` 的位置,而下面的 `servlet-mapping` 檔案定義了路徑的對映,這兩個標籤通過 `servlet-name` 標籤對應。 - 3、新建一個 `HelloServlet` 類繼承 `HttpServlet`: ```java package com.lonely.wolf.mini.spring.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 原始Servlet介面編寫,一般需要實現GET和POST方法,其他方法可以視具體情況選擇性繼承 */ public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); response.getWriter().write("Hello:" + request.getParameter("name")); } } ``` - 4、執行 `maven` 打包命令,確認成功打包成 `war` 包: ![](https://img2020.cnblogs.com/blog/2232223/202012/2232223-20201213102058355-972685274.png) - 5、`RUN-->Edit Configurations`,然後點選左上角的 `+` 號,新建一個 `Tomcat Server`,如果是第一次配置,預設沒有 `Tomcat Server` 選項,需要點選底部的 `xx more items...`: ![](https://img2020.cnblogs.com/blog/2232223/202012/2232223-20201213102124749-1680335127.png) - 6、點選右邊的 `Deployment`,然後按照下圖依次點選,最後在彈框內找到上面打包好的 `war` 包檔案: ![](https://img2020.cnblogs.com/blog/2232223/202012/2232223-20201213102147338-1168179967.png) - 7、選中之後,需要注意的是,下面 `Application Context` 預設會帶上 `war` 包名,為了方便,我們需要把它刪掉,即不用上下文路徑,只保留一個根路徑 `/` (當然上下文也可以保留,但是每次請求都要帶上這一部分), 再選擇 `Apply`,點選 `OK`,即可完成部署: ![](https://img2020.cnblogs.com/blog/2232223/202012/2232223-20201213102207412-1033660516.png) - 8、最後我們在瀏覽器輸入請求路徑`http://localhost:8080/hello?name=雙子孤狼`,即可得到返回:`Hello:雙子孤狼`。 上面我們就完成了一個簡單的 基於`Servlet` 的介面開發,可以看到,配置非常麻煩,每增加一個 `Servlet` 都需要增加對應的配置,所以才會有許多框架的出現來幫我們簡化開發,比如原來很流行的 `Struts2` 框架,當然現在除了一些比較老的專案,一般我們都很少使用,而更多的是選擇 `Spring` 框架來進行開發。 ## 模仿Spring `Spring` 的原始碼體系非常龐大,大部分人對其原始碼都敬而遠之。確實,`Spring` 畢竟經過了這麼多年的迭代,功能豐富,專案龐大,不是一下子就能看懂的。雖然 `Spring` 難以理解,但是其最核心的思想仍然是我們上面介紹的幾點,接下來就基於 `Spring` 最核心的部分來模擬,自己動手實現一個超級迷你版本的 `Spring`(此版本並不包含 `AOP` 功能)。 - 1、`pom` 依賴和上面保持不變,然後 `web.xml` 作如下改變,這裡會攔截所有的介面 `/*`,然後多配置了一個引數,這個引數其實也是為了更形象的模擬 `Spring`: ```xml ``` - 2、在 `respurces` 下面新建一個配置檔案 `application.properties`,用來定義掃描的基本路徑: ```properties basePackages=com.lonely.wolf.mini.spring ``` - 3、建立一些相關的註解類: ```java package com.lonely.wolf.mini.spring.annotation; import java.lang.annotation.*; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WolfAutowired { String value() default ""; } ``` ```java package com.lonely.wolf.mini.spring.annotation; import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WolfController { String value() default ""; } ``` ```java package com.lonely.wolf.mini.spring.annotation; import java.lang.annotation.*; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WolfGetMapping { String value() default ""; } ``` ```java package com.lonely.wolf.mini.spring.annotation; import java.lang.annotation.*; @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WolfRequestParam { String value() default ""; } ``` ```java package com.lonely.wolf.mini.spring.annotation; import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WolfService { String value() default ""; } ``` - 4、這個時候最核心的邏輯就是 `MyDispatcherServlet` 類了: ```java package com.lonely.wolf.mini.spring.v1; import com.lonely.wolf.mini.spring.annotation.*; import com.lonely.wolf.mini.spring.v1.config.MyConfig; import org.apache.commons.lang3.StringUtils; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URL; import java.util.*; public class MyDispatcherServlet extends HttpServlet { private MyConfig myConfig = new MyConfig(); priv