手寫源碼(三):自己實現SpringMVC

NO IMAGE

手寫SpringMVC

SpringMVC原理

  • SpringMVC基於Servlet實現的(單例的,Servlet會產生線程安全問題)
    手寫源碼(三):自己實現SpringMVC

實現步驟

  • 創建DispatcherServlet攔截所有的請求
  • 初始化,重寫Servlet的init方法
    • 掃包,獲取需要注入到SpringMVC容器中的類(如@Controller註解`)
    • 把URL和方法經行關聯:利用反射機制找到Controller類上的方法是否存在@RequestMapping註解,存在就把方法名稱存起來,把URL和方法對應起來
  • 處理請求 重寫GET和POST方法
    • 獲取請求URL,去URLBean裡獲取實例,再去URLMethod裡獲取方法實例,使用反射執行方法

Maven依賴

<dependencies>
<!--servlet依賴-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--各種工具-->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.5</version>
</dependency>
</dependencies>

定義自己的註解@ExtController@ExtRequestMapping,定義一個自己的Controller

註解如下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtController {
}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtRequestMapping {
String value() default "";
}

自己定義的controller如下

@ExtController
@ExtRequestMapping("/")
public class IndexController {
@ExtRequestMapping
public String getIndex() {
System.out.println("自定義的MVC框架");
return "index";
}
}

重點!構建DispatcherServlet

由於是個Web項目,我們在Web.xml裡面配置好自己的DispatcherServlet的映射

<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">
<servlet>
<servlet-name>disaptcher</servlet-name>
<servlet-class>com.libi.mvc.servlet.ExtDispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>disaptcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

第一步、在init方法裡掃包,判斷是否有Controller註解,再把URL和映射經行關聯

  • 在DispatcherServlet裡定義三個Bean容器
    /**mvc的Bean容器,存入Controller對象*/
private ConcurrentHashMap<String, Object> controllerBeans = new ConcurrentHashMap<String, Object>();
/**mvc的請求地址,把Url和Controller建立映射*/
private ConcurrentHashMap<String, Object> urlBeans = new ConcurrentHashMap<String, Object>();
/**mvc的請求方法名稱,把Url和方法名建立映射*/
private ConcurrentHashMap<String, Object> urlMethods = new ConcurrentHashMap<String, Object>();
  • 在重寫了Servlet的init方法裡掃描指定包下面所有帶有@ExtController註解的類,實例化後放在mvc的bean容器裡
    public void init() throws ServletException {
//掃描Controller包下面的類,並且找到所有帶@Controller的註解倒到Bean容器裡
List<Class<?>> classes = ClassUtils.getClasses("com.controller");
try {
findClassMvcAnnoation(classes);
} catch (Exception e) {
e.printStackTrace();
} 
}
/**找到帶有Controller註解的類然後初始化放入bean容器裡*/
private void findClassMvcAnnoation(List<Class<?>> classes) throws IllegalAccessException, InstantiationException {
for (Class<?> classInfo : classes) {
ExtController extController = classInfo.getAnnotation(ExtController.class);
if (extController != null) {
String beanId = ClassUtils.toLowerCaseFirstOne(classInfo.getSimpleName());
mvcBeans.put(beanId, classInfo.newInstance());
}
}
}

第二步 把method和url進行映射

  • 利用反射機制獲取有@RequestMapping的類和方法,把URL和方法經行關聯,放在Map裡
    /**將URL映射和方法關聯起來*/
private void handleMapping() {
//遍歷Controller,判斷類上是否有RequestMapping註解
for (Map.Entry<String, Object> entry : controllerBeans.entrySet()) {
Object controller = entry.getValue();
//判斷類是否加上了@RequestMapping註解
Class<?> classInfo = controller.getClass();
ExtRequestMapping classRequestMapping = classInfo.getDeclaredAnnotation(ExtRequestMapping.class);
String baseUrl = "";
if (classRequestMapping != null) {
//獲取Url映射的地址
baseUrl = classRequestMapping.value();
}
//判斷方法上是否加上了RequestMapping
Method[] methods = classInfo.getDeclaredMethods();
for (Method method : methods) {
//獲取方法上的映射
ExtRequestMapping methodRequestMapping = method.getDeclaredAnnotation(ExtRequestMapping.class);
if (methodRequestMapping != null) {
String methodUrl = baseUrl + methodRequestMapping.value();
//裝入映射表
System.out.println("put url:"+methodUrl);
urlBeans.put(methodUrl, controller);
urlMethods.put(methodUrl, method);
}
}
}
}

上面這一步在init()方法裡,創建完controller實例之後執行

第三步 重寫doPost方法

  • 利用反射機制獲取到需要的方法,利用反射機制執行存在Map裡的方法
    /**處理請求*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//獲取請求url地址
String url = req.getRequestURI();
System.out.println("url:"+url);
//從map集合中獲取controller
Object controller = urlBeans.get(url);
if (controller == null) {
//到這裡說明頁面不存在
resp.getWriter().println("404 not find url");
return;
}
//從map中獲取方法,調用並且獲取返回結果
Method method = urlMethods.get(url);
if (method == null) {
//到這裡說明頁面不存在
resp.getWriter().println("404 not find method");
return;
}
String result = (String) methodInvoke(method, controller);
resp.getWriter().println(result);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
private Object methodInvoke(Method method, Object controller) {
Object result = null;
try {
result = method.invoke(controller);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return result;
}

寫到這裡的效果如下

手寫源碼(三):自己實現SpringMVC

手寫源碼(三):自己實現SpringMVC

第四步 配置視圖解析器

    /**視圖解析器*/
private void extResourceViewResolver(String pageName, HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
String prefix = "/WEB-INF/view/";
String suffix = ".jsp";
request.getRequestDispatcher(prefix+pageName+suffix).forward(request,response);
}

以上代碼在doGet裡執行

/**處理請求*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//獲取請求url地址
String url = req.getRequestURI();
System.out.println("url:"+url);
//從map集合中獲取controller
Object controller = urlBeans.get(url);
if (controller == null) {
//到這裡說明頁面不存在
resp.getWriter().println("404 not find url");
return;
}
//從map中獲取方法,調用並且獲取返回結果
Method method = urlMethods.get(url);
if (method == null) {
//到這裡說明頁面不存在
resp.getWriter().println("404 not find method");
return;
}
String result = (String) methodInvoke(method, controller);
//-----------------使用視圖轉換器渲染頁面
extResourceViewResolver(result,req,resp);
//------------------
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}

/WEB-INF/view/index.jsp內容如下

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<h1>自己手寫mvc——index.jsp</h1>
</body>
</html>

執行後效果如下

手寫源碼(三):自己實現SpringMVC

相關文章

團長大人的學習筆記——RabbitMQ

團長大人的學習筆記——Hibernate

團長大人的學習筆記——自定義Java註解

手寫源碼(四):自己實現Mybatis