實作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