手寫 | springMVC框架
SpringMVC的運行流程
⑴ 用戶發送請求至前端控制器DispatcherServlet
⑵ DispatcherServlet收到請求調用HandlerMapping處理器映射器。
⑶ 處理器映射器根據請求url找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一併返回給DispatcherServlet。
⑷ DispatcherServlet通過HandlerAdapter處理器適配器調用處理器
⑸ 執行處理器(Controller,也叫後端控制器)。
⑹ Controller執行完成返回ModelAndView
⑺ HandlerAdapter將controller執行結果ModelAndView返回給DispatcherServlet
⑻ DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器
⑼ ViewReslover解析後返回具體View
⑽ DispatcherServlet對View進行渲染視圖(即將模型數據填充至視圖中)。
⑾ DispatcherServlet響應用戶。
手寫SpringMVC思路
1.web.xml加載
爲了讀取web.xml中的配置,我們用到ServletConfig這個類,它代表當前Servlet在web.xml中的配置信息。通過web.xml中加載我們自己寫的MyDispatcherServlet和讀取配置文件。
2、初始化階段
在前面我們提到DispatcherServlet的initStrategies方法會初始化9大組件,但是這裏將實現一些SpringMVC的最基本的組件而不是全部,按順序包括:
- 加載配置文件
- 掃描用戶配置包下面所有的類
- 拿到掃描到的類,通過反射機制,實例化。並且放到ioc容器中(Map的鍵值對 beanName-bean) beanName默認是首字母小寫
- 初始化HandlerMapping,這裏其實就是把url和method對應起來放在一個k-v的Map中,在運行階段取出
3、運行階段
每一次請求將會調用doGet或doPost方法,所以統一運行階段都放在doDispatch方法裏處理,它會根據url請求去HandlerMapping中匹配到對應的Method,然後利用反射機制調用Controller中的url對應的方法,並得到結果返回。按順序包括以下功能:
- 異常的攔截
- 獲取請求傳入的參數並處理參數
- 通過初始化好的handlerMapping中拿出url對應的方法名,反射調用
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- Spring MVC 核心控制器 DispatcherServlet 配置 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>com.sym.servlet.ExtDispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!-- 攔截所有/* 的請求,交給DispatcherServlet處理,性能最好 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
定義註解@Controller @RequestMapping
package com.sym.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 孫一鳴 on 2020/3/1
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtController {
}
package com.sym.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 孫一鳴 on 2020/3/1
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtRequestMapping {
String value() default "";
}
編寫處理器
public class ExtDispatcherServlet extends HttpServlet {
// mvc bean key=beanid ,value=對象
private ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<String, Object>();
// mvc 請求方法 key=requestUrl,value=對象
private ConcurrentHashMap<String, Object> mvcBeanUrl = new ConcurrentHashMap<String, Object>();
// mvc 請求方法 key=requestUrl,value=方法
private ConcurrentHashMap<String, String> mvcMethodUrl = new ConcurrentHashMap<String, String>();
/**
* 初始化自定義SpringMVC容器
*/
public void init() throws ServletException {
try {
// 1.獲取當前包下所有的類
List<Class<?>> classes = ClassUtil.getClasses("com.itmayiedu.ext.controller");
// 2.初始化當前包下所有的類,使用Java反射機制初始化對象存放在SpringMVC容器中key(beanId)-value(
// 當前實例對象)
findClassMVCBeans(classes);
// 3.初始化HandlerMapping方法,將url和方法對應上
handlerMapping(mvcBeans);
} catch (Exception e) {
}
}
// 2.初始化當前包下所有的類,使用Java反射機制初始化對象存放在SpringMVC容器中key(beanId)-value(
// 當前實例對象)
public void findClassMVCBeans(List<Class<?>> classes)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
mvcBeans = new ConcurrentHashMap<String, Object>();
for (Class<?> classInfo : classes) {
ExtController extController = classInfo.getDeclaredAnnotation(ExtController.class);
if (extController != null) {
// 默認類名小寫 作爲bean的名稱
String beanId = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());
mvcBeans.put(beanId, ClassUtil.newInstance(classInfo));
}
}
}
// 3.初始化HandlerMapping方法,將url和方法對應上
public void handlerMapping(ConcurrentHashMap<String, Object> mvcBeans) {
// 遍歷mvc bean對象
for (Map.Entry<String, Object> entry : mvcBeans.entrySet()) {
// springmvc 注入object對象
Object mvcObject = entry.getValue();
// 判斷類上是否有@ExtRequestMapping註解
Class<? extends Object> classInfo = mvcObject.getClass();
String requestBaseUrl = null;
ExtRequestMapping classExtRequestMapping = classInfo.getAnnotation(ExtRequestMapping.class);
if (classExtRequestMapping != null) {
requestBaseUrl = classExtRequestMapping.value();
}
// 遍歷當前類的所有方法,判斷方法上是否有註解
Method[] declaredMethods = classInfo.getDeclaredMethods();
for (Method method : declaredMethods) {
ExtRequestMapping methodExtRequestMapping = method.getDeclaredAnnotation(ExtRequestMapping.class);
if (methodExtRequestMapping != null) {
String httpRequestUrl = methodExtRequestMapping.value();
mvcBeanUrl.put(requestBaseUrl + httpRequestUrl, mvcObject);
mvcMethodUrl.put(requestBaseUrl + httpRequestUrl, method.getName());
}
}
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
doDispatch(req, resp);
} catch (Exception e) {
// TODO: handle exception
}
}
public void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
// 1.獲取請求url地址
String requestUrl = req.getRequestURI();
// 2.使用請求url查找對應mvc 控制器bean
Object object = mvcBeanUrl.get(requestUrl);
if (object == null) {
resp.getWriter().println("http ext not found controller 404");
return;
}
// 3.獲取對應的請求方法
String methodName = mvcMethodUrl.get(requestUrl);
if (StringUtils.isEmpty(methodName)) {
resp.getWriter().println("http ext not found Method 404");
return;
}
// 4.使用java反射技術執行方法
Class<? extends Object> classInfo = object.getClass();
String resultPage = (String) methodInvoke(classInfo, object, methodName);
// 5.視圖展示
viewdisplay(resultPage, req, resp);
}
// 執行方法
public Object methodInvoke(Class<? extends Object> classInfo, Object object, String methodName) {
try {
Method method = classInfo.getMethod(methodName);
Object result = method.invoke(object);
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// 視圖展示
public void viewdisplay(String pageName, HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// 獲取後綴信息
String suffix = ".jsp";
// 頁面目錄地址
String prefix = "/";
req.getRequestDispatcher(prefix + pageName + suffix).forward(req, res);
}
}