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 檔案中引入依賴(省略了少部分屬性):
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
    <relativePath/>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
  • 2、新建一個 HelloController 類:
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 啟動類:
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 完整的資訊:
<packaging>war</packaging> 
<dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.4</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>
    </dependencies>
  • 2、在 src/main 下面新建資料夾 webapp/WEB-INF,然後在 WEB-INF 下面新建一個 web.xml 檔案:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	version="2.4">
	<display-name>Lonely Wolf Web Application</display-name>
	<servlet>
		<servlet-name>helloServlet</servlet-name>
		<servlet-class>com.lonely.wolf.mini.spring.servlet.HelloServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>helloServlet</servlet-name>
		<url-pattern>/hello/*</url-pattern>
	</servlet-mapping>
</web-app>

這裡面定義了 selvletservlet-mapping 兩個標籤,這兩個標籤必須一一對應,上面的標籤定義了 servlet 的位置,而下面的 servlet-mapping 檔案定義了路徑的對映,這兩個標籤通過 servlet-name 標籤對應。

  • 3、新建一個 HelloServlet 類繼承 HttpServlet
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 包:

  • 5、RUN-->Edit Configurations,然後點選左上角的 + 號,新建一個 Tomcat Server,如果是第一次配置,預設沒有 Tomcat Server 選項,需要點選底部的 xx more items...

  • 6、點選右邊的 Deployment,然後按照下圖依次點選,最後在彈框內找到上面打包好的 war 包檔案:

  • 7、選中之後,需要注意的是,下面 Application Context 預設會帶上 war 包名,為了方便,我們需要把它刪掉,即不用上下文路徑,只保留一個根路徑 / (當然上下文也可以保留,但是每次請求都要帶上這一部分), 再選擇 Apply,點選 OK,即可完成部署:

  • 8、最後我們在瀏覽器輸入請求路徑http://localhost:8080/hello?name=雙子孤狼,即可得到返回:Hello:雙子孤狼

上面我們就完成了一個簡單的 基於Servlet 的介面開發,可以看到,配置非常麻煩,每增加一個 Servlet 都需要增加對應的配置,所以才會有許多框架的出現來幫我們簡化開發,比如原來很流行的 Struts2 框架,當然現在除了一些比較老的專案,一般我們都很少使用,而更多的是選擇 Spring 框架來進行開發。

模仿Spring

Spring 的原始碼體系非常龐大,大部分人對其原始碼都敬而遠之。確實,Spring 畢竟經過了這麼多年的迭代,功能豐富,專案龐大,不是一下子就能看懂的。雖然 Spring 難以理解,但是其最核心的思想仍然是我們上面介紹的幾點,接下來就基於 Spring 最核心的部分來模擬,自己動手實現一個超級迷你版本的 Spring(此版本並不包含 AOP 功能)。

  • 1、pom 依賴和上面保持不變,然後 web.xml 作如下改變,這裡會攔截所有的介面 /*,然後多配置了一個引數,這個引數其實也是為了更形象的模擬 Spring
<servlet>
    <servlet-name>myDispatcherServlet</servlet-name>
    <servlet-class>com.lonely.wolf.mini.spring.v1.MyDispatcherServlet</servlet-class>
    <init-param>
        <param-name>defaultConfig</param-name>
        <param-value>application.properties</param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>myDispatcherServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
  • 2、在 respurces 下面新建一個配置檔案 application.properties,用來定義掃描的基本路徑:
basePackages=com.lonely.wolf.mini.spring
  • 3、建立一些相關的註解類:
package com.lonely.wolf.mini.spring.annotation;

import java.lang.annotation.*;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WolfAutowired {
    String value() default "";
}
package com.lonely.wolf.mini.spring.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WolfController {
    String value() default "";
}
package com.lonely.wolf.mini.spring.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WolfGetMapping {
    String value() default "";
}
package com.lonely.wolf.mini.spring.annotation;

import java.lang.annotation.*;

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WolfRequestParam {
    String value() default "";
}
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 類了:
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();
    private List<String> classNameList = new ArrayList<String>();

    private Map<String,Object> iocContainerMap = new HashMap<>();
    private Map<String,HandlerMapping> handlerMappingMap = new HashMap<>();

    @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 {
        try {
            this.doDispatch(request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception{
        String requestUrl = this.formatUrl(request.getRequestURI());
        HandlerMapping handlerMapping = handlerMappingMap.get(requestUrl);
        if (null == handlerMapping){
            response.getWriter().write("404 Not Found");
            return;
        }

        //獲取方法中的引數型別
        Class<?>[] paramTypeArr = handlerMapping.getMethod().getParameterTypes();
        Object[] paramArr = new Object[paramTypeArr.length];

        for (int i=0;i<paramTypeArr.length;i++){
            Class<?> clazz = paramTypeArr[i];
            //引數只考慮三種型別,其他不考慮
            if (clazz == HttpServletRequest.class){
                paramArr[i] = request;
            }else if (clazz == HttpServletResponse.class){
                paramArr[i] = response;
            } else if (clazz == String.class){
                Map<Integer,String> methodParam = handlerMapping.getMethodParams();
                paramArr[i] = request.getParameter(methodParam.get(i));
            }else{
                System.out.println("暫不支援的引數型別");
            }
        }
        //反射呼叫controller方法
        handlerMapping.getMethod().invoke(handlerMapping.getTarget(), paramArr);
    }

    private String formatUrl(String requestUrl) {
        requestUrl = requestUrl.replaceAll("/+","/");
        if (requestUrl.lastIndexOf("/") == requestUrl.length() -1){
            requestUrl = requestUrl.substring(0,requestUrl.length() -1);
        }
        return requestUrl;
    }


    @Override
    public void init(ServletConfig config) throws ServletException {
        //1.載入配置檔案
        try {
            doLoadConfig(config.getInitParameter("defaultConfig"));
        } catch (Exception e) {
            System.out.println("載入配置檔案失敗");
            return;
        }

        //2.根據獲取到的掃描路徑進行掃描
        doScanPacakge(myConfig.getBasePackages());

        //3.將掃描到的類進行初始化,並存放到IOC容器
        doInitializedClass();

        //4.依賴注入
        doDependencyInjection();

        System.out.println("DispatchServlet Init End..." );
    }


    private void doDependencyInjection() {
        if (iocContainerMap.size() == 0){
            return;
        }
        //迴圈IOC容器中的類
        Iterator<Map.Entry<String,Object>> iterator = iocContainerMap.entrySet().iterator();

        while (iterator.hasNext()){
            Map.Entry<String,Object> entry = iterator.next();
            Class<?> clazz = entry.getValue().getClass();
            Field[] fields = clazz.getDeclaredFields();

            //屬性注入
            for (Field field : fields){
                //如果屬性有WolfAutowired註解則注入值(暫時不考慮其他註解)
                if (field.isAnnotationPresent(WolfAutowired.class)){
                    String value = toLowerFirstLetterCase(field.getType().getSimpleName());//預設bean的value為類名首字母小寫
                    if (field.getType().isAnnotationPresent(WolfService.class)){
                        WolfService wolfService = field.getType().getAnnotation(WolfService.class);
                        value = wolfService.value();
                    }
                    field.setAccessible(true);
                    try {
                        Object target = iocContainerMap.get(beanName);
                        if (null == target){
                            System.out.println(clazz.getName() + "required bean:" + beanName + ",but we not found it");
                        }
                        field.set(entry.getValue(),iocContainerMap.get(beanName));//初始化物件,後面注入
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

            //初始化HanderMapping
            String requestUrl = "";
            //獲取Controller類上的請求路徑
            if (clazz.isAnnotationPresent(WolfController.class)){
                requestUrl = clazz.getAnnotation(WolfController.class).value();
            }

            //迴圈類中的方法,獲取方法上的路徑
            Method[] methods = clazz.getMethods();
            for (Method method : methods){
                //假設只有WolfGetMapping這一種註解
                if(!method.isAnnotationPresent(WolfGetMapping.class)){
                    continue;
                }
                WolfGetMapping wolfGetMapping = method.getDeclaredAnnotation(WolfGetMapping.class);
                requestUrl = requestUrl + "/" + wolfGetMapping.value();//拼成完成的請求路徑

                //不考慮正則匹配路徑/xx/* 的情況,只考慮完全匹配的情況
                if (handlerMappingMap.containsKey(requestUrl)){
                    System.out.println("重複路徑");
                    continue;
                }

                Annotation[][] annotationArr = method.getParameterAnnotations();//獲取方法中引數的註解

                Map<Integer,String> methodParam = new HashMap<>();//儲存引數的順序和引數名
                retryParam:
                for (int i=0;i<annotationArr.length;i++){
                    for (Annotation annotation : annotationArr[i]){
                        if (annotation instanceof WolfRequestParam){
                            WolfRequestParam wolfRequestParam = (WolfRequestParam) annotation;
                            methodParam.put(i,wolfRequestParam.value());//儲存引數的位置和註解中定義的引數名
                            continue retryParam;
                        }
                    }
                }

                requestUrl = this.formatUrl(requestUrl);//主要是防止路徑多了/導致路徑匹配不上
                HandlerMapping handlerMapping = new HandlerMapping();
                handlerMapping.setRequestUrl(requestUrl);//請求路徑
                handlerMapping.setMethod(method);//請求方法
                handlerMapping.setTarget(entry.getValue());//請求方法所在controller物件
                handlerMapping.setMethodParams(methodParam);//請求方法的引數資訊
                handlerMappingMap.put(requestUrl,handlerMapping);//存入hashmap
            }
        }
    }


    /**
     * 初始化類,並放入容器iocContainerMap內
     */
    private void doInitializedClass() {
        if (classNameList.isEmpty()){
            return;
        }
        for (String className : classNameList){
            if (StringUtils.isEmpty(className)){
                continue;
            }
            Class clazz;
            try {
                clazz = Class.forName(className);//反射獲取物件
                if (clazz.isAnnotationPresent(WolfController.class)){
                    String value = ((WolfController)clazz.getAnnotation(WolfController.class)).value();
                    //如果直接指定了value則取value,否則取首字母小寫類名作為key值儲存類的例項物件
                    iocContainerMap.put(StringUtils.isBlank(value) ? toLowerFirstLetterCase(clazz.getSimpleName()) : value,clazz.newInstance());
                }else if(clazz.isAnnotationPresent(WolfService.class)){
                    String value = ((WolfService)clazz.getAnnotation(WolfService.class)).value();
                    iocContainerMap.put(StringUtils.isBlank(value) ? toLowerFirstLetterCase(clazz.getSimpleName()) : value,clazz.newInstance());
                }else{
                    System.out.println("不考慮其他註解的情況");
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("初始化類失敗,className為" + className);
            }
        }

    }

    /**
     * 將首字母轉換為小寫
     * @param className
     * @return
     */
    private String toLowerFirstLetterCase(String className) {
        if (StringUtils.isBlank(className)){
            return "";
        }
        String firstLetter = className.substring(0,1);
        return firstLetter.toLowerCase() + className.substring(1);
    }


    /**
     * 掃描包下所有檔案獲取全限定類名
     * @param basePackages
     */
    private void doScanPacakge(String basePackages) {
        if (StringUtils.isBlank(basePackages)){
            return;
        }
        //把包名的.替換為/
        String scanPath = "/" + basePackages.replaceAll("\\.","/");
        URL url = this.getClass().getClassLoader().getResource(scanPath);//獲取到當前包所在磁碟的全路徑
        File files = new File(url.getFile());//獲取當前路徑下所有檔案
        for (File file : files.listFiles()){//開始掃描路徑下的所有檔案
            if (file.isDirectory()){//如果是資料夾則遞迴
                doScanPacakge(basePackages + "." + file.getName());
            }else{//如果是檔案則新增到集合。因為上面是通過類載入器獲取到的檔案路徑,所以實際上是class檔案所在路徑
                classNameList.add(basePackages + "." + file.getName().replace(".class",""));
            }
        }

    }


    /**
     * 載入配置檔案
     * @param configPath - 配置檔案所在路徑
     */
    private void doLoadConfig(String configPath) {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(configPath);
        Properties properties = new Properties();
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("載入配置檔案失敗");
        }

        properties.forEach((k, v) -> {
            try {
                Field field = myConfig.getClass().getDeclaredField((String)k);
                field.setAccessible(true);
                field.set(myConfig,v);
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("初始化配置類失敗");
                return;
            }
        });
    }
}
  • 5、這個 Servlet 相比較於上面的 HelloServlet 多了一個 init 方法,這個方法中主要做了以下幾件事情:

(1)初始化配置檔案,拿到配置檔案中配置的引數資訊(對應方法:doLoadConfig)。

(2)拿到第 1 步載入出來的配置檔案,獲取到需要掃描的包路徑,然後將包路徑進行轉換成實際的磁碟路徑,並開始遍歷磁碟路徑下的所有 class 檔案,最終經過轉換之後得到掃描路徑下的所有類的全限定型別,儲存到全域性變數 classNameList 中(對應方法:doScanPacakge)。

(3)根據第 2 步中得到的全域性變數 classNameList 中的類通過反射進行初始化(需要注意的是隻會初始化加了指定註解的類)並將得到的對應關係儲存到全域性變數 iocContainerMap 中(即傳說中的 IOC 容器),其中 key 值為註解中的 value 屬性,如 value 屬性為空,則預設取首字母小寫的類名作為 key 值進行儲存(對應方法:doInitializedClass)。

(4)這一步比較關鍵,需要對 IOC 容器中的所有類的屬性進行賦值並且需要對 Controller 中的請求路徑進行對映儲存,為了確保最後能順利呼叫 Controller 中的方法,還需要將方法的引數進行儲存 。對屬性進行對映時只會對加了註解的屬性進行對映,對映時會從 IOC 容器中取出第 3 步中已經初始化的例項物件進行賦值,最後將請求路徑和 Controller 中方法的對映關係存入變數 handlerMappingMapkey 值為請求路徑,value 為方法的相關資訊 (對應方法:doDependencyInjection)。

  • 6、儲存請求路徑和方法的對映關係時,需要用到 HandlerMapping 類來進行儲存:
package com.lonely.wolf.mini.spring.v1;

import java.lang.reflect.Method;
import java.util.Map;

//省略了getter/setter方法
public class HandlerMapping {
    private String requestUrl;
    private Object target;//儲存方法對應的例項
    private Method method;//儲存對映的方法
    private Map<Integer,String> methodParams;//記錄方法引數
}
  • 7、初始化完成之後,因為攔截了 /* ,所以呼叫任意介面都會進入 MyDispatcherServlet ,而且最終都會執行方法 doDispatch,執行這個方法時會拿到請求的路徑,然後和全域性變數 handlerMappingMap 進行匹配,匹配不上則返回 404,匹配的上則取出必要的引數進行賦值,最後通過反射呼叫到 Controller 中的相關方法。
  • 8、新建一個 HelloControllerHelloService 來進行測試:
package com.lonely.wolf.mini.spring.controller;

import com.lonely.wolf.mini.spring.annotation.WolfAutowired;
import com.lonely.wolf.mini.spring.annotation.WolfController;
import com.lonely.wolf.mini.spring.annotation.WolfGetMapping;
import com.lonely.wolf.mini.spring.annotation.WolfRequestParam;
import com.lonely.wolf.mini.spring.service.HelloService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WolfController
public class HelloController {
    @WolfAutowired
    private HelloService helloService;

    @WolfGetMapping("/hello")
    public void query(HttpServletRequest request,HttpServletResponse response, @WolfRequestParam("name") String name) throws IOException {
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("Hello:" + name);
    }
}

package com.lonely.wolf.mini.spring.service;

import com.lonely.wolf.mini.spring.annotation.WolfService;

@WolfService(value = "hello_service")//為了演示能否正常取value屬性
public class HelloService {
}
  • 9、輸入測試路徑:http://localhost:8080////hello?name=雙子孤狼, 進行測試發現可以正常輸出:Hello:雙子孤狼

上面這個例子只是一個簡單的演示,通過這個例子只是希望在沒有任何框架的情況下,我們也能知道如何完成一個簡單的應用開發。例子中很多細節都沒有進行處理,僅僅只是為了體驗一下 Spring 的核心思想,並瞭解 Spring 到底幫助我們做了什麼,實際上 Spring 能幫我們做的事情遠比這個例子中多得多,Spring 體系龐大,設計優雅,經過了多年的迭代優化,是一款非常值得研究的框架。

總結

本文從介紹 Spring 核心功能開始入手,從如何利用 Spring 完成一個應用開發,講述到假如沒有 Spring 我們該如何基於 Servlet 進行開發,最後再通過一個簡單的例子體驗了 Spring 的核心思想。

相關文章