首页后端开发其他后端知识自定义MVC框架怎样实现,有哪些要点?

自定义MVC框架怎样实现,有哪些要点?

时间2024-03-25 01:24:03发布访客分类其他后端知识浏览640
导读:相信很多人对“自定义MVC框架怎样实现,有哪些要点?”都不太了解,下文有实例供大家参考,对大家了解操作过程或相关知识有一定的帮助,而且内容详细,逻辑清晰,接下来小编就为你详细解释一下这个问题。 一、让中央控制器动态加载存储子控制器...
相信很多人对“自定义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 ModelDrivenT>
 {
    
//    拿到将要封装的类实例   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 ModelDrivenBook>
{
    
    
        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框架怎样实现,有哪些要点?的介绍,本文内容仅供参考,有需要的朋友可以借鉴了解看看,希望对大家学习或工作,想要了解更多欢迎关注网络,小编每天都会为大家更新不同的知识。

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!


若转载请注明出处: 自定义MVC框架怎样实现,有哪些要点?
本文地址: https://pptw.com/jishu/652447.html
php依赖注入是什么意思 watch和watchEffect对比是怎样的,怎样应用呢?

游客 回复需填写必要信息