一塊兒寫框架-MVC框架-基礎功能-DispacherServlet控制器的實現(三)

實現功能

開發環境配置好,那麼咱們能夠編寫代碼了html

因爲BasicMVC框架的對象交由BasicIOC容器託管,因此啓動JavaWeb項目時,將程序全部的對象使用Ioc內核的機制加載到容器池裏面。java

基於這個緣由:咱們實現DispacherServlet控制器跳轉必須首先完成如下三個動做web

1.咱們必須在Web啓動是建立一個ApplicationContext容器操做對象;mvc

2.咱們必須須要一個配置類,來得到建立容器的信息app

3.根據請求得路徑跳轉到對應Controller的對應映射路徑的方法框架

 

實現步驟

第一步:咱們必須須要一個配置類,來得到建立容器的信息

1.實現Web程序啓動BasicIoc容器jsp

 

--在用於測試的web項目,建立一個配置類IocCofing,並在web.xml做爲DispacherServlet參數配置,以及編寫一個用於測試是否啓動了Ioc容器的UserControlleride

 

 

--配置類IocConfig代碼--測試

 

 1 package org.webmvc.cofig;
 2 
 3 import ioc.core.annotation.ComponentScan;
 4 import ioc.core.annotation.Configuration;
 5 
 6 //使用定義@Configuration定義該類是一個配置類
 7 @Configuration
 8 //使用ComponentScan設置掃描包的路徑
 9 @ComponentScan(basePackages={"org.webmvc.controller"})
10 public class IocConfig {
11 
12 }

 

--web.xml代碼--ui

 

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
 3   <display-name>webmvc-demo-test-01</display-name>
 4    <servlet>
 5      <servlet-name>dispacherServlet</servlet-name>
 6      <servlet-class>ioc.webmvc.DispacherServlet</servlet-class>
 7      <init-param>
 8        <!-- 設置配置類的類全名 -->
 9        <param-name>cofig</param-name>
10        <param-value>org.webmvc.cofig.IocConfig</param-value>
11      </init-param>
12    </servlet>
13    <servlet-mapping>
14      <servlet-name>dispacherServlet</servlet-name>
15      <url-pattern>*.do</url-pattern>
16    </servlet-mapping>
17 </web-app>

 

-編寫一個用於測試的UserController--

 

 1 package org.webmvc.controller;
 2 
 3 import ioc.core.annotation.stereotype.Controller;
 4 
 5 @Controller
 6 public class UserController {
 7     
 8     public void login(){
 9         System.out.println("-登陸Controller-");
10     }
11 }

 

 

2.編寫BasicMVCMVC框架的DispacherServlet類的init方法。得到配置參數,以及啓動BaiscIoC容器。

 

代碼以下:

 

 1 package ioc.webmvc;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.ServletConfig;
 6 import javax.servlet.ServletException;
 7 import javax.servlet.http.HttpServlet;
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 
11 import ioc.core.impl.AnntationApplicationContext;
12 
13 /**
14  * 核心控制,用於攔截全部的請求,MVC框架的入口。
15  * @author ranger
16  * @date 2017-11-08
17  *
18  */
19 public class DispacherServlet extends HttpServlet {
20 
21     private static final long serialVersionUID = -5969172674760020122L;
22     private AnntationApplicationContext contextApplication=null;
23     
24     
25     /**
26      * 啓動Web程序時,得到配置的參數
27      */
28     @Override
29     public void init(ServletConfig config) throws ServletException {
30         //1.配置參數是配置類的類全名
31         String parameter = config.getInitParameter("config");
32         //2.查看是否有參數了
33         System.out.println(parameter);
34         try {
35             //3.將字符串使用反射技術,轉成一個Class類
36             Class<?> classType = Class.forName(parameter);
37             //4.將對象加載到容器裏面
38             this.contextApplication=new AnntationApplicationContext(classType);
39         } catch (ClassNotFoundException e) {
40             e.printStackTrace();
41         }
42     }
43 
44 
45 
46     @Override
47     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
48          System.out.println("-測試成功--");
49         super.service(request, response);
50     }
51 }

 

3.啓動index.jsp,點擊請求。容器打印出加載的對象內存地址。說明成功。

第二步:根據請求得路徑跳轉到對應Controller的對應映射路徑的方法

確認經過web程序啓動就可使用BaiscIoc容器託管全部對象後。

咱們須要理解請求能夠找到對應的執行方法這個問題?

要解決這個問題,必需要有一個地方標識請求路徑和方法的關係。這裏我經過一個@RequestMapping註解來標識請求路徑和執行方法的關係。

 

實現思路:

(1)在業務控制器設置標識方法對應請求路徑的註解(@RequestMapping),必須指定映射路徑

(2)請求到核心控制器是掃描全部業務控制器。得到對應的方法。

 

實現步驟:

1. 定義一個註解RequestMapping來聲明路徑與方法的關係

 

 1 package ioc.webmvc.annotation;
 2 
 3 import java.lang.annotation.Documented;
 4 import java.lang.annotation.ElementType;
 5 import java.lang.annotation.Retention;
 6 import java.lang.annotation.RetentionPolicy;
 7 import java.lang.annotation.Target;
 8 
 9 @Retention(RetentionPolicy.RUNTIME)
10 /*
11  * 
12  * 指定能夠在方法以及類上面使用面使用
13  * 1.在方法上面,指定請求路徑
14  * 2.在類上面用於指定命名空間
15  */
16 
17 @Target(value={ElementType.METHOD,ElementType.TYPE})
18 @Documented
19 public @interface RequestMapping {
20     /**
21      * 沒有默認值,表示必需要填寫
22      * @return
23      */
24     String value();
25 
26 }

 

2.得到全部Controller類型的對象。用於判斷路徑對應的方法。

在容器的實現類ContextImpl裏面增長一個檢索業務控制器對象的方法。

實現方式分爲兩步:標紅的代碼

(1)在容器定義接口中定義一個方法

 

package ioc.core;

import java.util.Map;

/**
 * Ioc框架的容器接口
 * @author ranger
 *
 */
public interface Context {
    


     /**
      * 用於得到容器中的全部對象
      * @return
      */
     Map<String,Object> getObjects();
     
     /**
      * 用於得到容器中的全部業務控制器對象
      * @return
      */
     Map<String,Object> getControllerObjects();
     
     /**
      * 用於增長容器中的對象
      * @param key
      * @param value
      */
     void addObject(String key, Object value);
     
     /**
      * 根據類的類型以及設置的對象名返回容器對象
      * 若是傳入的類型容器中有對應key的對象,並且返回類型是兼容的,直接返回對應的對象。
      * 若是傳入的類型容器中有沒有對應key的對象,那麼判斷傳入的類型是否和容器的對象的找到惟一配置的。
      * 若是傳入類型惟一匹配,返回對象。若是沒有或者配配多個對象,都報一個RuntimeException異常
      * @param classType
      * @return
      */
     Object getObject(Class<?> classType,String key);
     
     

}

 

(2)在實現類ContextImpl實現該方法,先增長一個getObjectsByComponentType方法編寫經過組件註解類型返回對象,再實現getControllerObjects()方法。

 

 1 /**
 2      * 用於經過傳入標識組件類型的註解,返回對應類型的註解 註解類型分別爲: Controller Service Repository
 3      * Component
 4      * 
 5      * @param componentsType
 6      * @return
 7      */
 8     public Map<String, Object> getObjectsByComponentType(Class<? extends Annotation> componentsType) {
 9         // 1.建立一個存儲指定組件類型的Map
10         Map<String, Object> componentsObjects = new HashMap<String, Object>();
11         // 2.得到全部的容器對象
12         Map<String, Object> objects = this.getObjects();
13         // 3.得到Map中全部的對象的Set集合
14         Set<Entry<String, Object>> entrySet = objects.entrySet();
15         // 4.得到Set集合迭代器
16         Iterator<Entry<String, Object>> iterator = entrySet.iterator();
17         // 5.循環判斷
18         while (iterator.hasNext()) {
19             Entry<String, Object> entry = iterator.next();
20             // 6.得到當前對象的類類型,用於得到該類的類結構中的組件註解
21             Class<?> classType = entry.getValue().getClass();
22             // 7.容器裏的對象是不是指定註解類型的,若是是加入到componentsObjects中
23             Annotation annotation = classType.getDeclaredAnnotation(componentsType);
24             if (annotation != null) {
25                 componentsObjects.put(entry.getKey(), entry.getValue());
26             }
27 
28         }
29         // 8.返回指定組件類型的對象
30         return componentsObjects;
31     }
32 
33     /**
34      * 返回業務控制器Controller註解類型的全部對象
35      */
36     @Override
37     public Map<String, Object> getControllerObjects() {
38         return this.getObjectsByComponentType(Controller.class);
39     }

 

3.測試代碼得到的getObjectsByComponentType方法是否成功,輸出指定的類型對象,說明成功。

 

 1 package ioc.core.test;
 2 
 3 import java.util.Map;
 4 
 5 import org.junit.Test;
 6 
 7 import ioc.core.annotation.stereotype.Service;
 8 import ioc.core.impl.AnntationApplicationContext;
 9 import ioc.core.impl.ContextImpl;
10 import ioc.core.test.config.Config;
11 
12 public class ContextImplTest {
13     
14     
15     @Test
16     public void getObjectsByComponentsType(){
17         AnntationApplicationContext applicationContext=new AnntationApplicationContext(Config.class);
18         ContextImpl context = (ContextImpl) applicationContext.getContext();
19         Map<String, Object> componentObjects = context.getObjectsByComponentType(Service.class);
20         System.out.println(componentObjects+"==========================");
21         
22     }
23 
24 }

 

--測試結果,輸出的就是指定類型的對象

 

3.得到頁面的請求路徑匹配Controller對象中的方法的路徑。

--建立一個ControllerRelolver類編寫,檢索方法的@RequestMapping註解

 

  1 package ioc.webmvc.impl;
  2 
  3 import java.lang.reflect.InvocationTargetException;
  4 import java.lang.reflect.Method;
  5 import java.util.Collection;
  6 import java.util.Iterator;
  7 import java.util.Map;
  8 
  9 import javax.servlet.http.HttpServletRequest;
 10 import javax.servlet.http.HttpServletResponse;
 11 
 12 import ioc.core.Context;
 13 import ioc.webmvc.annotation.RequestMapping;
 14 
 15 public class ControllerRelolver {
 16 
 17     /**
 18      * 經過傳入的參數,執行路徑對應的業務控制器(Controller)的方法
 19      * @param request
 20      * @param response
 21      * @param context
 22      * @return
 23      * @throws IllegalAccessException
 24      * @throws IllegalArgumentException
 25      * @throws InvocationTargetException
 26      */
 27     public String execute(HttpServletRequest request,HttpServletResponse response,Context context) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
 28         //1.得到請求過來的路徑
 29         String uri = request.getRequestURI();
 30         //2.根據路徑規則,得到映射路徑
 31         String path = this.pathRule(uri);
 32         //3.經過路徑得到容器中對應的業務控制器的對象和執行方法
 33         MappingEnttiy mappingEnttiy = this.getRequestMethod(path, context);
 34         //4.得到執行方法
 35         Method method = mappingEnttiy.getMethod();
 36         //5.得到路徑對應的業務控制器
 37         Object controller = mappingEnttiy.getController();
 38         //6.執行方法,執行方法必須有request,response兩個參數
 39          Object resultObject = method.invoke(controller, request,response);
 40          if(resultObject!=null){
 41              //7.返回執行方法返回的映射字符串
 42              return (String) resultObject;
 43          }
 44         return null;
 45     }
 46     /**
 47      * 設置路徑的規則
 48      * 路徑規則爲,保留/,去掉後綴。
 49      * 如:
 50      * 請求路徑:http://localhost:8080/webmvc-demo-test-01/test.do
 51      * 合法的映射路徑爲:/test
 52      * 
 53      * @param url 傳入的爲請求路徑
 54      * @return  符合規則的路徑字符串
 55      *
 56      */
 57     private String pathRule(String url){
 58         //1.建立一個String構建字符串
 59         StringBuilder sb=new StringBuilder(url);
 60         System.out.println(url);
 61         //2.刪除路徑最後一個/以前的全部字符
 62         sb.delete(0, url.lastIndexOf("/"));
 63         System.out.println(sb.toString()+"刪除後的字符串長度:"+sb.length());
 64         //3.刪除(.)後面的後綴
 65         sb.delete(sb.lastIndexOf("."),sb.length());
 66         return sb.toString();
 67     }
 68     
 69     /**
 70      * 經過路徑得到對應的業務控制器對象和執行方法
 71      * @param path
 72      * @param context
 73      * @return
 74      */
 75     private MappingEnttiy getRequestMethod(String path,Context context){
 76         //1.得到Controller全部的Controller對象
 77         Map<String, Object> controllerObjects = context.getControllerObjects();
 78         System.out.println("-getRequestMethod-業務控制器對象池:"+controllerObjects);
 79         //2.得到業務控制器池裏面的全部值
 80         Collection<Object> values = controllerObjects.values();
 81         //3.遍歷
 82         Iterator<Object> iterator = values.iterator();
 83         while (iterator.hasNext()) {
 84             //4.得到業務控制器池中當前對象
 85             Object object = iterator.next();
 86             //5.得到當前對象的類類類型
 87             Class<? extends Object> classType = object.getClass();
 88             //6.經過對象的類類型,得到對象的方法列表
 89             Method[] methods = classType.getMethods();
 90             //7.循環判斷方法是否有RequestMapping註解
 91             for(Method method:methods){
 92                 RequestMapping mapping = method.getDeclaredAnnotation(RequestMapping.class);
 93                 //8.RequestMapping註解存在,並且等於映射路徑,返回該方法的方法和當前對象
 94                 if(mapping!=null&&mapping.value().equals(path)){
 95                     //9.聲明內部類MappingEnttiy來封裝映射的方法和對象。建立這個類的對象
 96                     MappingEnttiy entity=new MappingEnttiy();
 97                     entity.setController(object);
 98                     entity.setMethod(method);
 99                     return entity;
100                 }
101             }
102             
103         }
104         
105         return null;
106     }
107     /**
108      * 聲明一個私有的內部類對象,用於存儲執行檢索業務控制器時返回的數據
109      * @author ranger
110      *
111      */
112     private class MappingEnttiy{
113         //1.當前映射路徑對應的對象
114         private Object controller;
115         //2.當前映射路徑對應的方法
116         private Method method;
117 
118         public Object getController() {
119             return controller;
120         }
121         public void setController(Object controller) {
122             this.controller = controller;
123         }
124         public Method getMethod() {
125             return method;
126         }
127         public void setMethod(Method method) {
128             this.method = method;
129         }
130         
131     }
132 }

 

4.核心控制器(DispacherServlet)調用這個業務控制解釋器(ControllerRelolver),標紅處

 

 1 package ioc.webmvc;
 2 
 3 import java.io.IOException;
 4 import java.lang.reflect.InvocationTargetException;
 5 
 6 import javax.servlet.ServletConfig;
 7 import javax.servlet.ServletException;
 8 import javax.servlet.http.HttpServlet;
 9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11 
12 import ioc.core.impl.AnntationApplicationContext;
13 import ioc.webmvc.impl.ControllerRelolver;
14 
15 /**
16  * 核心控制,用於攔截全部的請求,MVC框架的入口。
17  * 
18  * @author ranger
19  * @date 2017-11-08
20  *
21  */
22 public class DispacherServlet extends HttpServlet {
23 
24     private static final long serialVersionUID = -5969172674760020122L;
25     private AnntationApplicationContext contextApplication = null;
26 
27     /**
28      * 啓動Web程序時,得到配置的參數
29      */
30     @Override
31     public void init(ServletConfig config) throws ServletException {
32         // 1.配置參數是配置類的類全名
33         String parameter = config.getInitParameter("config");
34         // 2.查看是否有參數了
35         System.out.println(parameter);
36         try {
37             // 3.將字符串使用反射技術,轉成一個Class類
38             Class<?> classType = Class.forName(parameter);
39             // 4.將對象加載到容器裏面
40             this.contextApplication = new AnntationApplicationContext(classType);
41 
42         } catch (ClassNotFoundException e) {
43             e.printStackTrace();
44         }
45     }
46 
47     @Override
48     protected void service(HttpServletRequest request, HttpServletResponse response)
49             throws ServletException, IOException {
50        51 
52         ControllerRelolver rs = new ControllerRelolver();
53         try {
54             String result= rs.execute(request, response, contextApplication.getContext());
55             if(result!=null){
56                       //返回執行方法
57                 request.getRequestDispatcher(result).forward(request, response);
58             }
59         } catch (IllegalAccessException e) {
60             // TODO Auto-generated catch block
61             e.printStackTrace();
62         } catch (IllegalArgumentException e) {
63             // TODO Auto-generated catch block
64             e.printStackTrace();
65         } catch (InvocationTargetException e) {
66             // TODO Auto-generated catch block
67             e.printStackTrace();
68         }
69     }
70 }

 

 

測試代碼

1.測試的Web項目代碼目錄

2.代碼以下

--IocConfig--

 

 1 package org.webmvc.cofig;
 2 
 3 import ioc.core.annotation.ComponentScan;
 4 import ioc.core.annotation.Configuration;
 5 
 6 //使用定義@Configuration定義該類是一個配置類
 7 @Configuration
 8 //使用ComponentScan設置掃描包的路徑
 9 @ComponentScan(basePackages={"org.webmvc.controller"})
10 public class IocConfig {
11 
12 }

 

--UserController--

 1 package org.webmvc.controller;
 2 
 3 import javax.servlet.http.HttpServletRequest;
 4 import javax.servlet.http.HttpServletResponse;
 5 
 6 import ioc.core.annotation.stereotype.Controller;
 7 import ioc.webmvc.annotation.RequestMapping;
 8 
 9 @Controller
10 public class UserController {
11     
12     /**
13      * 路徑規則:
14      * 1.必須包括有/開頭
15      * 2.後綴必須忽略不寫
16      * 如:http://localhost:8080/webmvc-demo-test-01/test.do
17      * 對應的映射路徑爲:/test
18      *  
19      */
20     @RequestMapping(value = "/login")
21     public String login(HttpServletRequest request,HttpServletResponse response){
22         System.out.println("-登陸Controller-");
23         return "/login.jsp";
24 
25     }
26 
27 }

---請求頁面index.jsp--

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
  <a href="${pageContext.request.contextPath }/login.do">test</a>
</body>
</html>

---返回頁面login.jsp--

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <title>Insert title here</title>
 8 </head>
 9 <body>
10   登陸成功!
11 </body>
12 </html>

2.測試結

相關文章
相關標籤/搜索