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


    实现功能

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

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

  • 相关阅读:
    Windows 8.1 应用开发 – 触控操作
    Windows Phone App Studio 无码开发手机应用
    尝试HTML + JavaScript 编写Windows App
    Ubuntu远程连接MySQL(connection refused)解决方法
    [MySQL]查看用户权限与GRANT用法
    mysql 创建用户命令-grant
    搭建memcached使用:/usr/bin/phpize 安装memcached扩展的时候报错
    在linux中使用phpize安装php扩展模块
    跟锦数学全部资料
    33套2020数分高代试题参考解答[2020/04/14更新,更多请关注跟锦数学微信公众号]
  • 原文地址:https://www.cnblogs.com/zhuyuejiu/p/7820255.html
Copyright © 2020-2023  润新知