Spring中毒太深,離開Spring我居然連最基本的介面都不會寫了
阿新 • • 發佈:2020-12-13
# 前言
隨著 `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