博客 / 詳情

返回

Spring中毒太深,離開了Spring,我居然連最基本的接口都不會寫了¯\_(ツ)_/¯

前言

眾所周知,Java必學的框架其中就是SSM,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 文件中引入依賴(省略了少部分屬性):

<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/d...雙子孤狼 進行測試,正常輸出: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>

這裏面定義了 selvlet 和 servlet-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?n...雙子孤狼,即可得到返回: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 中方法的映射關係存入變量 handlerMappingMap,key 值為請求路徑,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、新建一個 HelloController 和 HelloService 來進行測試:


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////hell...雙子孤狼,

進行測試發現可以正常輸出:

Hello:雙子孤狼。

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

總結

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

喜歡的朋友不妨點個關注,即可收到我的每日更新哦,如果覺得本文不錯可以給我點個贊,評論,收藏,一鍵三連哦,感謝各位看官,我們明天見。

user avatar maenj_ba_lah 頭像
1 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.