一文了解自定义MVC框架实现

目录
  • 一、让中央控制器动态加载存储子控制器
  • 二、参数传递封装优化
  • 三、对于方法执行结果转发重定向优化
  • 四、框架配置可变

一、让中央控制器动态加载存储子控制器

上期回顾,我们说明了自定义MVC工作原理,其中,中央控制器起到了接收浏览器请求,找到对应的处理人的一个作用,但是也存在缺陷,如:

就像在每一次顾客访问前台时,有很多个部门,比如说料理部门,财务部门,每当访问一次,就要new一个此类,代码如下:

public void init() throws ServletException {
 actions.put("/book", new BookAction());
 actions.put("/order", new OrderAction());
 }

解决方案:通过xml建模的知识,到config文件中进行操作。

目的:使代码更加灵活

所以接下来对中央控制器进一步作出优化改进:

以前:

String url = req.getRequestURI();
  //拿到book
  url = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
     ActionSupport action = actions.get(url);
  action.excute(req, resp);

现在改进代码:

1、通过url来找到config文件中对应的action对象

2、然后通过该对象来取到路径名servlet.BookAction

3、然后找到对应的方法执行

DispatcherServlet:

package com.ycx.framework;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ycx.web.BookAction;

/**
 * 中央控制器:
 * 主要职能:接收浏览器请求,找到对应的处理人
 * @author 杨总
 *
 */
@WebServlet("*.action")
public class DispatcherServlet extends HttpServlet{

    /**
     * 通过建模我们可以知道,最终ConfigModel对象会包含config.xml中的所有子控制器信息
     * 同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可
     */
    private ConfigModel configModel;

//    程序启动时,只会加载一次
    @Override
    public void init() throws ServletException {

        try {
            configModel=ConfigModelFactory.build();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @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 {
        //http:localhost:8080/mvc/book.action?methodName=list
        String uri=req.getRequestURI();
//        拿到/book,就是最后一个“/”到最后一个“.”为止
        uri=uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));

//        相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后反射实例化
        ActionModel actionModel = configModel.pop(uri);
        if(actionModel==null) {
            throw new RuntimeException("action 配置错误");
        }
        String type = actionModel.getType();
//        type是Action子控制器的全路径名
        try {
            Action action= (Action) Class.forName(type).newInstance();
            action.execute(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

改进思路:

1、通过url来找到config文件中对应的action对象

2、然后通过该对象来取到路径名servlet.BookAction

3、然后找到对应的方法执行

展示效果:(当自己点击列表删除时,出现了“在同一个servlet中调用 del 方法”这效果,说明用xml建模的知识去优化中央控制器成功!)

但是注意,如果我们的路径名不对的话,就会相应的报错

比如:

原因:

所以:

二、参数传递封装优化

<h3>参数传递封装优化</h3>
<a href="${pageContext.request.contextPath }/book.action?methodName=add & bid=989898 & bname=laoliu & price=89">增加</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=del">删除</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=edit">修改</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=list">查询</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=load">回显</a>

解决参数冗余问题:

由于在做项目过程中,在servlet类中需要接受很多个参数值,所以就想到要解决当前参数冗余问题。比如:一个实体类Book当中有二十个属性,那么你在进行修改时就要接受二十个值,虽然每次接受语句中的属性值不一样,但从根本上来讲,性质是一样,要接收属性。如下所示:(当时此类没有实现Moderdriver接口)

package com.ycx.web;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ycx.entity.Book;
import com.ycx.framework.Action;
import com.ycx.framework.ActionSupport;
import com.ycx.framework.ModelDriven;

public class BookAction extends ActionSupport {

        private Book book=new Book();

        private void load(HttpServletRequest req, HttpServletResponse resp) {
            System.out.println("在同一个servlet中调用 load 方法");
        }

        private void list(HttpServletRequest req, HttpServletResponse resp) {
            System.out.println("在同一个servlet中调用 list 方法");
        }

        private void edit(HttpServletRequest req, HttpServletResponse resp) {
            System.out.println("在同一个servlet中调用 edit 方法");
        }

        private void del(HttpServletRequest req, HttpServletResponse resp) {
            System.out.println("在同一个servlet中调用 del 方法");
        }

        private void add(HttpServletRequest req, HttpServletResponse resp) {
            String bid=req.getParameter("bid");
            String bname=req.getParameter("bname");
            String price=req.getParameter("price");
            Book book=new Book();
            book.setBid(Integer.valueOf(bid));
            book.setBname(bname);
            book.setPrice(Float.valueOf(price));
            bookDao.add(book);
            System.out.println("在同一个servlet中调用 add 方法");
        }

    }

解决方案:建一个模型驱动接口,使BookAction实现该接口,在中央控制器中将所有要接收的参数封装到模型接口中,从而达到简便的效果。

ModelDriven类(驱动接口类):

package com.ycx.framework;

/**
 * 模型驱动接口,接收前台JSP传递的参数,并且封装到实体类中
 * @author 杨总
 *
 * @param <T>
 */
public interface ModelDriven<T> {
//    拿到将要封装的类实例   ModelDriven.getModel() ---> new Book();
    T getModel();
}

DispatcherServlet 中央控制器类:

@Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //http:localhost:8080/mvc/book.action?methodName=list
        String uri=req.getRequestURI();
//        拿到/book,就是最后一个“/”到最后一个“.”为止
        uri=uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
//        Action action = actions.get(uri);
//        相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后反射实例化
        ActionModel actionModel = configModel.pop(uri);
        if(actionModel==null) {
            throw new RuntimeException("action 配置错误");
        }
        String type = actionModel.getType();
//        type是Action子控制器的全路径名
        try {
            Action action= (Action) Class.forName(type).newInstance();
//            action是bookAction
            if(action instanceof ModelDriven) {
                ModelDriven md=(ModelDriven) action;
//                model指的是bookAction中的book实例
                Object model = md.getModel();
//                要给model中的属性赋值,要接收前端jsp参数   req.getParameterMap()
//                PropertyUtils.getProperty(bean, name)
//                将前端所有的参数值封装进实体类
                BeanUtils.populate(model, req.getParameterMap());
                System.out.println(model);
            }

//            正式调用方法前,book中的属性要被赋值
            action.execute(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

BookAction类:(注意,在上一张同一个类里,没有实现ModelDriver接口,而如下bookaction类实现了ModelDriver接口,把接受多个参数的属性值语句注释,达到了简便的效果)

package com.ycx.web;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ycx.entity.Book;
import com.ycx.framework.Action;
import com.ycx.framework.ActionSupport;
import com.ycx.framework.ModelDriven;

public class BookAction extends ActionSupport implements ModelDriven<Book>{

        private Book book=new Book();

        private void load(HttpServletRequest req, HttpServletResponse resp) {
            System.out.println("在同一个servlet中调用 load 方法");
        }

        private void list(HttpServletRequest req, HttpServletResponse resp) {
            System.out.println("在同一个servlet中调用 list 方法");
        }

        private void edit(HttpServletRequest req, HttpServletResponse resp) {
            System.out.println("在同一个servlet中调用 edit 方法");
        }

        private void del(HttpServletRequest req, HttpServletResponse resp) {
            System.out.println("在同一个servlet中调用 del 方法");
        }

        private void add(HttpServletRequest req, HttpServletResponse resp) {
//            String bid=req.getParameter("bid");
//            String bname=req.getParameter("bname");
//            String price=req.getParameter("price");
            Book book=new Book();
//            book.setBid(Integer.valueOf(bid));
//            book.setBname(bname);
//            book.setPrice(Float.valueOf(price));
//            bookDao.add(book);
            System.out.println("在同一个servlet中调用 add 方法");
        }

        @Override
        public Book getModel() {
            return null;
        }

    }

Debug运行效果如下:

其中关于解决参数冗余问题关键代码是:

BeanUtils.populate(bean, req.getParameterMap());
​​​​​​​//将该对象要接受的参数值封装到对应的对象中

三、对于方法执行结果转发重定向优化

解决跳转方式问题:

在我们跳转到另一个界面时,需要许很多关于跳转方式的代码,有重定向,有转发,例如:

重定向:resp.sendRedirect(path);

转发: req.getRequestDispatcher(path).forward(req, resp);

这些代码往往要写很多次,因此通过配置config文件来解决此类问题;

例如这一次我跳转增加页面时是转发,跳转查询界面是重定向:

主界面代码如下:

<a href="${pageContext.request.contextPath }/book.action?methodName=add & bid=989898 & bname=laoliu & price=89">增加</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=list">查询</a>

config.xml :

<?xml version="1.0" encoding="UTF-8"?>
<config>

    <action path="/book" type="com.ycx.web.BookAction">
        <forward name="success" path="/demo2.jsp" redirect="false" />
        <forward name="failed" path="/demo3.jsp" redirect="true" />
    </action>

</config>

思路:

1、当点击增加或者编辑时,首先跳转的是中央控制器类(DispatchServlet):获取到url,url将决定要跳到哪一个实体类,

2、之后进入ActionSupport(子控制器接口实现类)通过methodname要调用什么方法,

3、再然后进入到BookAction中调用methodname方法,找到对应的返回值,

4、通过返回值在进入到config文件找到path属性,之后在中央控制器中进行判断,来决定是重定向还是转发。

中央控制器(DispatcherServlet):

String result = action.execute(req, resp);
            ForwardModel forwardModel = actionModel.pop(result);
//            if(forwardModel==null)
//                throw new RuntimeException("forward config error");
            String path = forwardModel.getPath();
//            拿到是否需要转发的配置
            boolean redirect = forwardModel.isRedirect();
            if(redirect)
                //${pageContext.request.contextPath}
                resp.sendRedirect(req.getServletContext().getContextPath()+path);
            else
                req.getRequestDispatcher(path).forward(req, resp);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

ActionSupport(子控制器接口实现类):

package com.ycx.framework;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ActionSupport implements Action{

    @Override
    public String execute(HttpServletRequest req, HttpServletResponse resp) {
        String methodName = req.getParameter("methodName");
//        methodName可能是多种方法
//        前台传递什么方法就调用当前类的对应方法
        try {
            Method m=this.getClass()// BookServlet.Class
            .getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
            m.setAccessible(true);
//            调用当前类实例的 methodName 方法
            return (String) m.invoke(this, req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

BookAction(图中标记的为返回值):

config文件(图中name为返回值,path为要跳转的界面路径名,redirect为跳转方式):

demo2界面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
转发页面
</body>
</html>

demo3界面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
重定向
</body>
</html>

运行结果:

1、当点击增加时,跳转到demo2界面,显示效果如下

2.当点击查询时,跳转到demo3界面,显示效果如下:

四、框架配置可变

如果config.xml文件名改成mvc.xml,该程序是否还可因运行呢?答案肯定是不行的。

因为 ConfigModelFactory 类里就定义了它的默认路径名如下:

我们可以在DispatcherServlet类里的init初始化里设置它的配置地址:

@Override
    public void init() throws ServletException {
//        actions.put("/book", new BookAction());
//        actions.put("/order", new BookAction());
        try {
            //配置地址
//            getInitParameter的作用是拿到web.xml中的servlet信息配置的参数
            String configLocation = this.getInitParameter("configLocation");
            if(configLocation==null||"".equals(configLocation))
                configModel=ConfigModelFactory.build();
            else
                configModel=ConfigModelFactory.build(configLocation);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

web.xml :

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>T280_mvc</display-name>
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>com.ycx.framework.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>configLocation</param-name>
            <param-value>/yangzong.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
    <url-pattern>*.action</url-pattern>
    </servlet-mapping>
</web-app>

然后把config.xml改成yangzong.xml即可改变框架配置

到此这篇关于一文了解自定义MVC框架实现的文章就介绍到这了,更多相关自定义MVC框架内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 一步步教你写一个SpringMVC框架

    目录 一.介绍 二.程序实践 2.1.创建扫描注解 2.2.编写 DispatcherServlet 类 2.3.编写 controller 类 三.总结 一.介绍 在日常的 web 开发中,熟悉 java 的同学一定知道,Spring MVC 可以说是目前最流行的框架,之所以如此的流行,原因很简单:编程简洁.上手简单! 我记得刚开始入行的时候,最先接触到的是Struts1 + Hibernate + Spring来web系统的整体开发框架,简单的描述一下当时的编程心情:超难用,各种配置项很多,

  • 写简单的mvc框架实例讲解

    这一章先把支持注解的功能加上,这样就不需要经常地修改配置文件了. 至于视图处理的地方,就还是先用json吧,找时间再写. 项目地址在:https://github.com/hjx601496320/aMvc . 测试代码在:https://github.com/hjx601496320/amvc-test. 怎么写呢? 因为在之前写代码的时候,我把每个类要做的事情分的比较清楚,所以在添加这个功能的时候写起来还是比较简单的,需要修改的地方也比较小. 这一章里我们需要干的事情有: 定义一个注解,标识

  • 浅谈MVC框架的优点(翻译)

    传统的ASP.NET Web Forms是一个非常好的主意,但现实需求非常复杂.随着时间的推移,现实世界的项目暴露出Web Forms的一些不足之处: "沉重的"视图状态:现实中在http请求之间维持状态(术语叫视图状态)导致了服务端和客户端巨大的数据块来回传递.典型情况下这个数据块会大到数百K字节,而且这个数据块会在每次请求时来回传输,导致网站访问者访问速度下降,同时增加了服务器的带宽负担. 页面生存周期:作为页面生存周期的一部分,连接客户端事件和服务端事件处理代码的机制,有时会非常

  • MVC框架自定义实现过程

    1.思维导图 2.什么是MVC? MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写, 它是一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码. 3.运行原理 用户发送请求 → 中央控制器接受用户请求 → 分析请求连接/获取到用户需要的类+方法 → 调用相对应的Model → 访问数据库服务器 4.演绎过程 4.1.控制层 BookServlet: package com.tyf.web; impo

  • 一文了解自定义MVC框架实现

    目录 一.让中央控制器动态加载存储子控制器 二.参数传递封装优化 三.对于方法执行结果转发重定向优化 四.框架配置可变 一.让中央控制器动态加载存储子控制器 上期回顾,我们说明了自定义MVC工作原理,其中,中央控制器起到了接收浏览器请求,找到对应的处理人的一个作用,但是也存在缺陷,如: 就像在每一次顾客访问前台时,有很多个部门,比如说料理部门,财务部门,每当访问一次,就要new一个此类,代码如下: public void init() throws ServletException { acti

  • struts2自定义MVC框架

    本文实例为大家分享了struts2自定义MVC框架的方法,供大家参考,具体内容如下 自定义MVC: (首先了解Model1和Model2的概念) Model1与Model2: Model1:就是一种纯jsp开发技术,将业务逻辑代码和视图渲染代码杂糅在一起. Model2:Model2是在Model1的基础上,将业务逻辑的代码分离开来,单独形成一个Servlet,Model2也是基于MVC开发. 总结MVC特点如下: (1)数据的获取和显示分离 (2)控制器将不同的模型和视图组合在一起 (3)应用

  • Spring Web MVC框架学习之配置Spring Web MVC

    这一篇文章讲的是Spring Web MVC各部分的配置方法,包括Java代码配置和XML文件配置以及MVC命名空间的使用方法. 启用MVC Java配置和XML命名空间 默认配置 要启用MVC Java配置(@Controller等各种注解)和XML命名空间,如果使用的是Java配置,在配置类上再添加@EnableWebMvc注解即可. @Configuration @EnableWebMvc public class WebAppConfig { } 如果使用XML配置文件的话,添加下面一行

  • Spring MVC 框架搭建配置方法及详解

    现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了.不过要想灵活运用Spring MVC来应对大多数的Web开发,就必须要掌握它的配置及原理. 一.Spring MVC环境搭建:(Spring 2.5.6 + Hibernate 3.2.0) 1. jar包引入 Spring 2.5.6:spring.jar.spring-webmvc.jar.comm

  • 12种JavaScript常用的MVC框架比较分析

    本文详细讲述了12种JavaScript常用的MVC框架.分享给大家供大家参考,具体如下: Gordon L. Hempton是西雅图的一位黑客和设计师,他花费了几个月的时间研究和比较了12种流行的JavaScript MVC框架,并在博客中总结了每种框架的优缺点,最终的结果是,Ember.js胜出. 此次比较针对的特性标准有四种,分别是: ① UI绑定(UI Bindings) ② 复合视图(Composed Views) ③ Web表现层(Web Presentation Layer) ④

  • 搭建自己的PHP MVC框架详解

    本文详细讲述了搭建自己的PHP MVC框架的方法.分享给大家供大家参考,具体如下: 前言 说到写PHP的MVC框架,大家想到的第一个词--"造轮子",是的,一个还没有深厚功力的程序员,写出的PHP框架肯定不如那些出自大神们之手.经过时间和各种项目考验的框架.但我还是准备并且这么做了,主要是因为: 认为有关PHP的方方面面都了解了,但自己学习PHP的时间还短,基础并不扎实,很多常用函数的参数还偶尔要查手册,而且对于PHP的一些较新的特性如命名空间.反射等只是简单的看过,并没有能实际应用过

  • Zend Framework教程之MVC框架的Controller用法分析

    本文讲述了Zend Framework教程之MVC框架的Controller用法.分享给大家供大家参考,具体如下: 这里简单讲讲MVC模式中Controller的基本使用方法. 基本使用实例: root@coder-671T-M:/www/zf_demo1/application# tree. ├── Bootstrap.php ├── configs │   └── application.ini ├── controllers │   ├── ErrorController.php │  

  • Java 自定义Spring框架与Spring IoC相关接口分析

    在本讲,我们来对Spring IoC功能相关的接口逐一进行分析,分析这些接口的原因就是为了我们自己定义Spring IoC功能提前做好准备. Spring IoC相关接口分析 BeanFactory接口解析 对于BeanFactory接口,我之前只是稍微提到过,并且将就着用了一下它.这里,我将会对BeanFactory接口进行一个具体讲解. Spring中bean的创建是典型的工厂模式,这一系列的bean工厂,即IoC容器,为开发者管理对象之间的依赖关系提供了很多便利和基础服务,在Spring中

随机推荐