码迷,mamicode.com
首页 > Web开发 > 详细

一起写框架-MVC框架-基础功能-DispacherServlet控制器的实现(三)

时间:2017-11-11 23:44:57      阅读:276      评论:0      收藏:0      [点我收藏+]

标签:end   _id   while   加载   request   use   tran   exec   lan   

实现功能

开发环境配置好,那么我们可以编写代码了

由于BasicMVC框架的对象交由BasicIOC容器托管,所以启动JavaWeb项目时,将程序所有的对象使用Ioc内核的机制加载到容器池里面。

基于这个原因:我们实现DispacherServlet控制器跳转必须首先完成以下三个动作

1.我们必须在Web启动是创建一个ApplicationContext容器操作对象;

2.我们必须需要一个配置类,来获得创建容器的信息

3.根据请求得路径跳转到对应Controller的对应映射路径的方法

 

实现步骤

第一步:我们必须需要一个配置类,来获得创建容器的信息

1.实现Web程序启动BasicIoc容器

 

--在用于测试的web项目,创建一个配置类IocCofing,并在web.xml作为DispacherServlet参数配置,以及编写一个用于测试是否启动了Ioc容器的UserController

技术分享

 

 

--配置类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代码--

 

 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.测试结

技术分享

 

 

一起写框架-MVC框架-基础功能-DispacherServlet控制器的实现(三)

标签:end   _id   while   加载   request   use   tran   exec   lan   

原文地址:http://www.cnblogs.com/zhuyuejiu/p/7820255.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!