天天看點

實作SpringMVC架構實作SpringMVC架構

實作SpringMVC架構

一、依賴管理工具

優點:

  • 自動化管理依賴
  • 解決依賴沖突
  • 能監管項目

三種

  • Ant
  • Maven
  • Gradle
    • 發揚了Maven的約定大于配置的優點
    • 使用DSL語言提倡函數支援
    • 友善性上:Json,免安裝

Spring子產品劃分

core(核心層)

Beans

Core

Context

SpEL

應用層

Data:JDBC、ORM。

Web:Mvc,Servlet

我們需要實作Core子產品,包括core、beans、context包

實作web功能,內建web和webmvc

添加,starter,實作spring1-boot的啟動方式

使用

建立四個兩個子產品,删除主項目的src目錄,分别是framework和test,建立兩個子產品之間的練習,之後直接從test子產品中測試。建立五個包,分别是Beans 、Core、Context、starter、web。

在測試模闆中放入一個核心啟動類這裡叫Application.java。裡面編寫主方法。

子產品間的聯系:

package com.sang.sdg;

import com.sang.sdg.starter.MiniApplication;

public class Application {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        MiniApplication.run(Application.class,args);
    }
}
           

配置:text子產品中的test.gradle檔案

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile(project(':framework'))
}
jar{
    manifest {
        attributes "Main-Class" : "com.sang.sdg.Application"
    }
    from{
        configurations.compile.collect{
            it.isDirectory() ? it : zipTree(it)
        }
    }
}
           

這樣就建立了兩個聯系,可以在text的jar檔案中對jar包進行測試

web模型

web伺服器

  • 監聽一個tcp端口
  • 轉發請求,回收響應
  • 本身沒有業務邏輯,連接配接作業系統和應用程式代碼

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-CliY03SN-1627543787658)(image-20210722092546090.png)]

servlet

  • 一種規範:限制了java伺服器與業務類的通信方式
  • 一個接口:javax.servlet.Servlet
  • 一種java類:實作了Servlet接口的應用程式類

tomcat

  • 原生java語言開發,運作在JVM上
  • 多種并發模型,高性能
  • 支援嵌入應用程式
擴充:

Tomcat容器等級:Tomcat容器分為四個等級

1. Container 容器,集裝箱
2.Engine 引擎容器
3.Host 主機()
4.Servlet容器(tomcat最低級别的容器)裡面包含了一個或多個Context
—— Context (上下文,背景,環境) 一個context對應一個web項目
—— Wrapper 容器裡的封裝部分

Engine:Servlet 的頂層容器,包含一 個或多個 Host 子容器;
Host:虛拟主機,負責 web 應用的部 署和 Context 的建立;
Context:Web 應用上下文,包含多個 Wrapper,負責 web 配置的解析、管 理所有的 Web 資源;
Wrapper:最底層的容器,是對 Servlet 的封裝,負責 Servlet 執行個體的創 建、執行和銷毀。

Container是容器的父接口,該容器的設計用的是典型的責任鍊的設計模式,它由四個自容器元件構成,分别是Engine、Host、Context、Wrapper。這四個元件是負責關系,存在包含關系。通常一個Servlet class對應一個Wrapper,如果有多個Servlet定義多個Wrapper,如果有多個Wrapper就要定義一個更高的Container,如Context。
           
Tomcat中的檔案作用
bin: 啟動和關閉tomcat的bat檔案
conf: 配置檔案 
-->server.xml : 該檔案用于配置和 server 相關的資訊, 比如 tomcat啟動端口後,配置Host,  配置Context 即web應用 
-->web.xml : 該檔案配置與 web應用(web應用就相當于是一個 web站點)
-->tomcat-users.xml: 該檔案使用者配置tomcat 的使用者密碼 和 權限
lib 目錄: 該目錄放置運作tomcat 運作需要的jar包
logs 目錄:存放日志, 當我們需要去檢視日志的時候,很有用!,當我們啟動tomcat錯誤時候,可以查詢資訊.
webapps 目錄: 該目錄下,放置我們的web應用(web 站點), 比如:
建立  web1 目錄  下面放置我們的html 檔案 jsp 檔案..圖檔... 則 web1就被當做一個web應用管理起來(特别說明tomcat 6.0 以後支援 tomcat 5 版本 還有别的設定)
work: 工作目錄: 該目錄用于存放jsp被通路後 生成的對應的 server檔案 和.class檔案
           
執行流程

1.請求被發送到本機端口8080,被在那裡偵聽的CoyoteHTTP/1.1 Connector獲得

2.Connector把請求交給他所在的Service的Engine來處理,并等待Engine的回應

3.Engine擷取請求localhost:8080/test/index.jsp,比對它所有虛拟主機Host

4.Engine比對到名為localhost的Host(即使比對不到也把請求交給該host處理,因為該Host被定為Engine預設主機)

5.localhost Host獲得請求/test/index.jsp,比對它所擁有的Context

6.Host比對到路徑為/test的Context(如果比對不到就把請求交給路徑為“”的Context去處理)

7.path="test"的Context擷取請求/index.jsp,在他的mapping table中尋找對應的servlet

8.Context比對到URL PATTERN為*。jsp的servlet,對應與JspServlet類

9.構造HttpServletRequest對象和HttpServletResponse對象,作為參數調用JspServlet的doGet或doPost方法。

10.Context把執行完了之後的HttpServletResponse對象傳回給Host

11.Host把HttpServletResponse對象傳回給Engine

12.Engine把HttpServletResponse對象傳回給Connector

13.Connector把HttpServletResponse對象傳回給客戶browser

内部組成

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-Nsj8lpHi-1627543787661)(1484390-20190305164203128-1958072095.png)]

Tomcat 組成如下圖:

主要有 Container 和 Connector 以及相關元件構成。

Server:指的就是整個 Tomcat 服 務器,包含多組服務,負責管理和 啟動各個 Service,同時監聽 8005 端口發過來的 shutdown 指令,用 于關閉整個容器 ;

Service:Tomcat 封裝的、對外提 供完整的、基于元件的 web 服務, 包含 Connectors、Container 兩個 核心元件,以及多個功能元件,各 個 Service 之間是獨立的,但是共享 同一 JVM 的資源 ;

Connector:Tomcat 與外部世界的連接配接器,監聽固定端口接收外部請求,傳遞給 Container,并 将 Container 處理的結果傳回給外部;

Container:Catalina,Servlet 容器,内部有多層容器組成,用于管理 Servlet 生命周期,調用 servlet 相關方法。

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-Doi2gTH0-1627543787664)(1484390-20190305164122581-2069214907.jpg)]

示範:

第一步:

首先選擇tomcat伺服器,在maven倉庫裡下載下傳tomcat核心包

第二步:

編寫tomcat服務類,tomcatServer.java

package com.sang.sdg.web.server;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;

public class TomcatServer {
    private Tomcat tomcat;
    private String[] args;

    TomcatServer(String[] args){
        this.args = args;
    }
    public void startServer() throws LifecycleException {
        tomcat = new Tomcat();//建立tomcat
        tomcat.setPort(6699);//設定端口
        tomcat.start();//開啟tomcat服務
        //防止伺服器中途退出,添加常駐線程
        Thread awaitThread = new Thread("tomcat_await_Thread"){
            @Override
            public void run() {
                TomcatServer.this.tomcat.getServer().await();//使線程一直等待
            }
        };
        awaitThread.setDaemon(false);//設定線程為非守護線程
        awaitThread.start();
    }
}

           

第三步

啟動tomcatServer,在主類中

package com.sang.sdg.starter;

import com.sang.sdg.web.server.TomcatServer;
import org.apache.catalina.LifecycleException;

public class MiniApplication {
  public static void run(Class<?> cla,String[] args){
      System.out.println("Hello Mini-Application");
        //開啟tomcat服務
      TomcatServer tomcatServer = new TomcatServer(args);
      try {
          tomcatServer.startServer();
      } catch (LifecycleException e) {
          e.printStackTrace();
      }


  }
}

           

重新打包然後運作測試。

第四步

現在伺服器中什麼都沒有,在浏覽器中通路本地tomcat伺服器什麼都沒有,找不到要顯示的頁面。

是以現在,建立一個servlet類,使tomcat能夠顯示,因為tomcat本身就是一個servlet類

建立一個TestTomcat.java,實作Servlet接口,在service簡單寫點代碼

package com.sang.sdg.web.servlet;
import javax.servlet.*;
import java.io.IOException;
public class TestServlet implements Servlet {
    @Override
    public void init(ServletConfig config) throws ServletException {
    }
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        res.getWriter().println("Hello SpringMVC!");
    }
    @Override
    public String getServletInfo() {
        return null;
    }
    @Override
    public void destroy() {
    }
}
           

在tomcat服務類中配置Context容器

Context context = new StandardContext();//new一個标準Context容器
        context.setPath("");//設定通路路徑
        context.addLifecycleListener(new Tomcat.FixContextListener());//添加context生命周期,這裡使用預設
        Tomcat.addServlet(context,"testServlet",new TestServlet()).setAsyncSupported(true);//添加servlet
        context.addServletMappingDecoded("/t","testServlet");//設定路徑映射
        tomcat.getHost().addChild(context);//獲得tomcat的虛拟主機,将context容器添加進去
           

重新打包,測試,注意這裡的端口号為6699,項目名為空

Servlet維護

請求分發

最初的使用:

  • 由伺服器進行配置,将寫好的Servlet配置在其他容器中,然後配置在伺服器中統一進行查找
  • 使用web.xml 中心化配置,将寫好的Servlet配置在xml裡通過添加映射來進行管理,發送來的路徑在xml裡逐一進行查找有部分問題。每種業務添加一個Servlet,對應一個URI .

問題:

  • 配置集中,大而雜,不易管理
  • 多次實作Servlet接口,造成代碼重複

SpringMVC

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-swBcZCAK-1627543787667)(image-20210725173046581.png)]

優勢:

  • 使用注解,實作簡單,按需實作
  • 配置分散,不雜亂
  • 容器内實作,更簡單

是以我們采用SpringMVC的架構模式

Spring架構模式仿寫實作

第一步

将以上寫好的TestServlet改名為DispatcherServlet或者從新定義一個dispatcherServlet,之後再修改TomcatServer中的Servlet的聯系方式

Context context = new StandardContext();
        context.setPath("");
        context.addLifecycleListener(new Tomcat.FixContextListener());
        Tomcat.addServlet(context,"dispatcherServlet",new dispatcherServlet()).setAsyncSupported(true);
        context.addServletMappingDecoded("/","dispatcherServlet");
        tomcat.getHost().addChild(context);
           

第二步

然後自定義三個注解,使用基本注解進行定義。分别是 Controller,RequestMapping,RequestParam

@Controller
package com.sang.sdg.web.mvc;

import java.lang.annotation.*;

@Documented//用javadoc指令生成API文檔
@Retention(RetentionPolicy.RUNTIME)//表示生命周期
@Target(ElementType.TYPE)//表示這個注解能放在什麼位置上
public @interface Controller {
}
           
@RequestMapping
package com.sang.sdg.web.mvc;

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)//作用于方法上
public @interface RequestMapping {
    String value();//添加一個屬性用以儲存需要映射的URI
}

           
@RequestParam
package com.sang.sdg.web.mvc;

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface RequestParam {
    String value();
}
           

第三步

現在架構并不知道我們寫的這些注解,沒有分發請求,想要讓我們寫的注解被架構知道,使用類加載器

類加載器:ClassLoader

  • 通過類的權限的名 擷取類的二進制位元組流
  • 解析二進制位元組流,擷取 Class類執行個體
  • 加載classpath下的靜态資源

是以需要一個類掃描器ClassScanner來對這些類進行掃描

在core包内定義一個ClasssCanner類

package com.sang.sdg.core;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassScanner {
    public static List<Class<?>> scannerClasses(String packageName) throws IOException, ClassNotFoundException {
        //聲明一個容器用來存儲掃描到的類
        List<Class<?>> classList = new ArrayList<>();
        //利用包名來擷取檔案路徑,将包名裡的點換成斜杠就OK了
        String path = packageName.replace(".", "/");
        //使用類加載器,通過路徑來加載檔案
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> resource = classLoader.getResources(path);
        //周遊這些資源
        while(resource.hasMoreElements()){
            URL url = resource.nextElement();
            //判斷資源的類型
            if (url.getProtocol().contains("jar")) {
            //擷取jar包的絕對路徑1
                JarURLConnection urlConnection =(JarURLConnection) url.openConnection();
                String jarFilePath = urlConnection.getJarFile().getName();

                classList.addAll(getClassesFromJar(jarFilePath,path));
                //隻處理了資源為jar包的形式,其他暫時留白,可以自己寫
            }else if(url.getProtocol().contains("file")){
                String filePath = url.getPath();
                List<Class<?>> classes = new ArrayList<>();
                getClassesFromDirectory(classes,filePath,packageName);
                classList.addAll(classes);
            }
        }
        return classList;
    }
    //通過jar包的路徑來擷取jar包下所有的類
    private static List<Class<?>> getClassesFromJar(String jarFilePath,String path) throws IOException, ClassNotFoundException {
        List<Class<?>> classes = new ArrayList<>();
        JarFile jarFile = new JarFile(jarFilePath);

        Enumeration<JarEntry> jarEntries = jarFile.entries();
        while (jarEntries.hasMoreElements()){
            JarEntry jarEntry = jarEntries.nextElement();
            String entryName = jarEntry.getName();//com/sang/sdg/test/Test.class
            if (entryName.startsWith(path) && entryName.endsWith(".class")) {
                String classFullName = entryName.replace("/", ".").substring(0, entryName.length() - 6);
                classes.add(Class.forName(classFullName));
            }
        }
        return classes;
    }

    private static void getClassesFromDirectory( List<Class<?>> classes ,String filePath, String packageName) {
        File file = new File(filePath);
        List<File> fileList = Arrays.asList(file.listFiles());
        fileList.forEach(it->{
            if(it.isDirectory()){
                getClassesFromDirectory(classes,it.getPath(),packageName);
            }else{
                String tmp = it.getPath().replace("/",".");
                String className = tmp.substring(tmp.indexOf(packageName),tmp.length()-6);
                try {
                    classes.add(Class.forName(className));
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
           

測試一下,在MiniApplication.java裡

List<Class<?>> classList = ClassScanner.scannerClasses(cla.getPackage().getName());
          classList.forEach(it -> System.out.println(it.getName()));
           

結果

将所有的類(包括注解)都列印出來

控制器初始化

可以使用反射,将所有類中的Controller注解的類提取出來

反射
  • 活躍于運作時
  • 可以擷取屬性和方法的執行個體
  • 可以動态的執行個體化一個類

Mapping Handler

第一步

添加一個MappingHandler的包及類,每一個MappingHandler都是一個請求控制器

package com.sang.sdg.web.handler;

import jdk.nashorn.internal.ir.RuntimeNode;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//請求控制器
public class MappingHandler {
    private String uri;
    private Method method;
    private Class<?> controller;
    private String[] args;

    public MappingHandler(String uri,Method method,Class<?> controller,String[] args){
        this.uri = uri;
        this.method = method;
        this.controller = controller;
        this.args = args;
    }
//請求處理方法
    public boolean handle(ServletRequest req, ServletResponse res) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException {
        String requestURI = ((HttpServletRequest) req).getRequestURI();
        if (!requestURI.equals(uri)) {
            return false;
        }
        Object[] parameters = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            parameters[i] = req.getParameter(args[i]);
        }
        Object ctl = controller.newInstance();
        Object response = method.invoke(ctl, parameters);
        res.getWriter().println(response.toString());
        return true;
    }
}
           
第二步

添加一個管理器來管理這些Handler

package com.sang.sdg.web.handler;

import com.sang.sdg.web.mvc.Controller;
import com.sang.sdg.web.mvc.RequestMapping;
import com.sang.sdg.web.mvc.RequestParam;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

public class HandlerManager {
    public static List<MappingHandler> mappingHandlers = new ArrayList<>();//容器,儲存MappingHandler類
//把Controller類加載出來
    public static void resolveMappingHandler(List<Class<?>> classList){
        for (Class<?> cls: classList) {
            if (cls.isAnnotationPresent(Controller.class)) {
                parseHandler(cls);//解析Controller類
            }
        }
    }
    
    public static void parseHandler(Class<?> cls){
        Method[] methods = cls.getDeclaredMethods();
      for (Method method: methods) {//如果方法沒有被RequestMapping注解,不處理
            if (!method.isAnnotationPresent(RequestMapping.class)) {
                continue;
            }
            String uri = method.getDeclaredAnnotation(RequestMapping.class).value();
            List<String> paramList = new ArrayList<>();
            for (Parameter parameter: method.getParameters()) {
                if (parameter.isAnnotationPresent(RequestParam.class)) {
                    paramList.add(parameter.getDeclaredAnnotation(RequestParam.class).value());
                }
            }
            String[] params = paramList.toArray(new String[paramList.size()]);
            MappingHandler mappingHandler = new MappingHandler(uri, method, cls, params);
            HandlerManager.mappingHandlers.add(mappingHandler);
        }
    }
}
           
第三步

在dispatcherServlet中使用這個Handle

//當請求來時,判斷能否處理這個請求
for (MappingHandler mappingHandler: HandlerManager.mappingHandlers) {
            try {
                if (mappingHandler.handle(req,res)) {
                    return;
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
           
小結
  • DispatcjerServlet的重要意義
    • Servlet太多時,使用web伺服器配置來維護Servlet的困難,了解了為什麼Spring要提出DispatcherServlet以及他的實作
  • 通過java的類加載機制,實作類的掃描
    • 擷取到所有的類,并且從其中判斷出Controller注解的類
  • 通過反射實作MappingHandler
    • 真正實作了Spring裡的DispatcherServlet

beans管理

bean本質上也是一種對象

  • 生命周期較長,在jvm啟動服務初始化時開始在服務結束jvm銷毀時結束
  • 在整個虛拟機内可見
  • 維護成本較高

優勢

  • 運作期效率高
  • 統一維護,便于管理和擴充

Spring架構的實作方式

  • 包掃描并自動裝配(在服務剛啟動時,使用反射實作執行個體化)執行個體化後的bean怎麼存,存到哪去都由Spring說的算。
  • 通過BeanFactory統一管理和維護
  • 依賴注入
    • 把有依賴關系的類放到容器中,解析出這些類的執行個體,就是依賴注入。
    • 目的是實作類的解耦。
什麼是依賴注入?

依賴注入和控制反轉是同一個概念。具體含義是:當某個角色(可能是一個Java執行個體,調用者)需要另一個角色(另一個Java執行個體,被調用者)的協助時,在 傳統的程式設計過程中,通常由調用者來建立被調用者的執行個體。但在Spring裡,建立被調用者的工作不再由調用者來完成,是以稱為控制反轉;建立被調用者 執行個體的工作通常由Spring容器來完成,然後注入調用者,是以也稱為依賴注入。

控制反轉:

IOC(Inversion Of Control):思想

  • 是面向對象的一種設計原則,也是一種思想,用來降低代碼之間的耦合度

DI(Dependency Injection):方式

  • 是實作控制反轉的一種方式
實作依賴注入
  • 掃描包獲得類定義
  • 使用反射初始化bean,并實作依賴注入
  • 解決bean的初始化順序
    • 擷取項目中的所有類定義,并把它們放入到一個Class容器裡,進行循環周遊處理
    • 判斷bean需不需要依賴,看屬性,如果沒有依賴,直接執行個體化,并放入到beanFactory裡
    • 如果需要依賴其他的bean,檢查被依賴的bean在beanFactory是否已經存在
    • 若已存在,則在beanFactory内取出被依賴的bean,用反射set到正在建立的bean裡完成bean的建立
    • 如果bean的依賴在beanFactory内找不到,就暫時先放棄他,建立下一個bean
    • 等到所有的bean都建立完成,在從頭建立失敗的bean
    • 還有一種較複雜的情況,bean之間的互相依賴,解決方法較為複雜,暫時先不解決

實作bean的建立

第一步

首先,先仿照Spring,建立兩個注解

@interface Bean

package com.sang.sdg.beans;

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Bean {

}
           

@interface AutoWired

package com.sang.sdg.beans;

import java.lang.annotation.*;

//使用在bean的屬性上
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AutoWired {
}

           

第二步

編寫BeanFactory類

package com.sang.sdg.beans;

import com.sang.sdg.web.mvc.Controller;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class BeanFactory {
    //添加一個屬性。用來處理bean類型到bean執行個體的映射
    private static Map<Class<?>,Object> classToBean = new ConcurrentHashMap<>();
    //從映射裡擷取bean
    public static Object getBean(Class<?> cls){
        return classToBean.get(cls);
    }
    //初始化bean
    public static void initBean(List<Class<?>> classList) throws Exception {
        List<Class<?>> toCreate = new ArrayList<>();
        while (toCreate.size() != 0){
            int remainSize = toCreate.size();//儲存目前的容器大小
            //開始周遊所有的類
            for (int i = 0; i < toCreate.size(); i++) {
                //如果完成了建立,就将它從容器中删除
                if (finishCreate(toCreate.get(i))) {
                    toCreate.remove(i);
                }
            }
            //判斷容器大小有沒有變化
            if (remainSize == toCreate.size()) {
                throw new Exception("cycle dependency!");
            }
        }
    }

    private static boolean finishCreate(Class<?> cls) throws IllegalAccessException, InstantiationException {
        if (!cls.isAnnotationPresent(Bean.class) && !cls.isAnnotationPresent(Controller.class)) {
            return true;
        }
        Object bean = cls.newInstance();
        for (Field field : cls.getDeclaredFields()) {
            if (field.isAnnotationPresent(AutoWired.class)) {
                Class<?> fieldType = field.getType();
                Object reliantBean = BeanFactory.getBean(fieldType);
                if (reliantBean == null) {
                    return false;
                }
                field.setAccessible(true);
                field.set(bean,reliantBean);
            }
        }
        classToBean.put(cls,bean);
        return true;
    }
}
           

第三步

在主方法裡調用BeanFactory的初始化方法

package com.sang.sdg.starter;

import com.sang.sdg.beans.BeanFactory;
import com.sang.sdg.core.ClassScanner;
import com.sang.sdg.web.handler.HandlerManager;
import com.sang.sdg.web.server.TomcatServer;
import org.apache.catalina.LifecycleException;

import java.util.List;

public class MiniApplication {
  public static void run(Class<?> cla,String[] args){
      System.out.println("Hello Mini-Application");

      TomcatServer tomcatServer = new TomcatServer(args);
      try {
          tomcatServer.startServer();
          List<Class<?>> classList = ClassScanner.scannerClasses(cla.getPackage().getName());
          BeanFactory.initBean(classList);
          HandlerManager.resolveMappingHandler(classList);
          classList.forEach(it -> System.out.println(it.getName()));
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
}
           

更改MappingHandler中的handle方法,使handle也能擷取bean

package com.sang.sdg.web.handler;

import com.sang.sdg.beans.BeanFactory;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//請求控制器
public class MappingHandler {
    private String uri;
    private Method method;
    private Class<?> controller;
    private String[] args;

    public MappingHandler(String uri,Method method,Class<?> controller,String[] args){
        this.uri = uri;
        this.method = method;
        this.controller = controller;
        this.args = args;
    }

    public boolean handle(ServletRequest req, ServletResponse res) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException {
        String requestURI = ((HttpServletRequest) req).getRequestURI();
        if (!requestURI.equals(uri)) {
            return false;
        }
        Object[] parameters = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            parameters[i] = req.getParameter(args[i]);
        }
        Object ctl = BeanFactory.getBean(controller);
        Object response = method.invoke(ctl, parameters);
        res.getWriter().println(response.toString());
        return true;
    }

}
           

測試和小結

SalaryServer.java

package com.sang.sdg.web.service;

import com.sang.sdg.beans.Bean;

@Bean
public class SalaryServer {
    public Integer calSalary(Integer experience){
        return experience * 10;
    }
}

           

SalaryController.java

package com.sang.sdg.web.controllers;

import com.sang.sdg.beans.AutoWired;
import com.sang.sdg.web.mvc.Controller;
import com.sang.sdg.web.mvc.RequestMapping;
import com.sang.sdg.web.mvc.RequestParam;
import com.sang.sdg.web.service.SalaryServer;

@Controller
public class SalaryController {
@AutoWired
public SalaryServer salaryServer;
    @RequestMapping("/get.json")
    public Integer gerSalary(@RequestParam("name") String name,@RequestParam("experience") String experience){
        return salaryServer.calSalary(Integer.parseInt(experience));
    }
}

           

浏覽器測試輸入:

http://localhost:6699/get.json?experience=50&name=lis

ava

package com.sang.sdg.web.service;

import com.sang.sdg.beans.Bean;

@Bean
public class SalaryServer {
    public Integer calSalary(Integer experience){
        return experience * 10;
    }
}

           

SalaryController.java

package com.sang.sdg.web.controllers;

import com.sang.sdg.beans.AutoWired;
import com.sang.sdg.web.mvc.Controller;
import com.sang.sdg.web.mvc.RequestMapping;
import com.sang.sdg.web.mvc.RequestParam;
import com.sang.sdg.web.service.SalaryServer;

@Controller
public class SalaryController {
@AutoWired
public SalaryServer salaryServer;
    @RequestMapping("/get.json")
    public Integer gerSalary(@RequestParam("name") String name,@RequestParam("experience") String experience){
        return salaryServer.calSalary(Integer.parseInt(experience));
    }
}

           

浏覽器測試輸入:

http://localhost:6699/get.json?experience=50&name=lis

所有内容皆為慕課網:

手寫SpringMVC,劍指優秀開源架構靈魂 實戰課自己聽課時手敲的聽課筆記,上課連結:

https://coding.imooc.com/learn/list/350.html