自定义MVC(超详细介绍,看此篇足矣)

1.MVC模式

1.1.什么是MVC

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范。用一种业务逻辑、数据、界面显示分离的方法,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

MVC结构

  • Model:是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据。

  • View:是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的。

  • Controller:是应用程序中处理用户交互的部分,通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。最典型的MVC就是JSP + servlet + javabean的模式

  • Model:常用javabean去实现,通过各种类来对数据库的数据进行获取,并封装在对象当中。

  • View:常用JSP来实现,通过可直接观察的JSP页面来展示我们从数据库中获取的数据。

  • Controller:常用servlet来实现,通过servlet来获取经过javabean包装过的对象(已存入数据库中的数据),然后再发送数据传输到JSP界面。

1)不能跨层调用; 2)只能由上往下进行调用:View -> Controller -> Model

1.2.三层架构和MVC的区别

  1. 三层架构是基于业务逻辑来分的,而MVC是基于页面来分的;

  2. 三层是种软件架构,通过接口实现编程,MVC模式是一种复合设计模式,一种解决方案;

  3. 三层架构模式是体系结构模式,MVC是设计模式;

  4. 三层架构模式又可归于部署模式,MVC可归于表示模式。

2.自定义MVC工作原理

3.自定义MVC实现

基于IDEA创建mvc项目。

示例一:

  • 创建ActionServlet核心控制器并继承HttpServlet,接受请求、分发请求。

  • 创建Action抽象子控制器并定义execute方法,用于处理具体的核心业务逻辑处理。

  • 创建HelloAction并继承抽象类Action,重写execute方法,实现业务逻辑处理。

  • 完成在ActionServlet中的init方法中的请求路径与子控制器类的对应配置。

  • 完成ActionServlet中的doPost方法的请求处理。

示例二:

  • 创建AddAction并继承抽闲类Action,重写execute方法,实现加法逻辑处理。

  • 在ActionServlet中的init方法中完成AddAction子控制器类与请求路径的对应关系配置。

  • 创建add.jsp和rs.jsp页面,处理加法操作及结果展示。

4.自定义MVC增强

4.1.XML解析建模

4.1.1.导入XML解析建模工具类

具体代码请参考开头中《XML之解析建模》

4.1.2.导入mvc.xml

将mvc.xml导入到自定义MVC项目src根路径下。

4.1.3.修改ActionServlet中init初始化方法

在ActionServlet的init方法中将原有Map集合方式替换成XML建模方式

private Map<String,Action> actions=new HashMap<String,Action>();
private ConfigModel configModel;
@Override
public void init() throws ServletException {
    //Map集合方式
    actions.put("/helloAction", new HelloAction());   
    actions.put("/addAction",new AddAction());
    
    //XML建模方式
    configModel=ConfigModelFactory.createConfigModel();
}

4.1.4.修改ActionServlet逻辑处理流程

修改ActionServlet中的doPost方法的请求处理业务流程,由于获取请求路径及截取请求路径名的操作不变。

  • 根据请求路径名获取ActionModel

/**
     * 3、根据请求路径名获取对应的ActionModel(type和path)
     * @param path 对应config.xml中action节点的path属性(请求路径名)
     * @return 返回config.xml中action节点所对应的建模实体类ActionModel(path和type)
     */
    private ActionModel findAction(String path){
        ActionModel actionModel = configModel.get(path);
        if(null==actionModel) throw new RuntimeException("ActionModel不存在!");
        return actionModel;
    }

注:这里经常会引发ActionModel不存在的问题,请认真仔细核查mvc.xml的配置

  • 反射机制实例化子控制器类

 /**
     * 5、根据ActionModel中的type属性完成反射实例化操作
     * @param type 来自于ActionModel建模中实体类中的type属性对应的config.xml中action中全路径名
     * @return
     */
    private Action createAction(String type){
        try {
            //获取类对象
            Class cla = Class.forName(type);
            //返回反射实例化实例化对象
            return (Action) cla.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("反射机制实例化Action子控制器类错误!");
        }
    }

  • 将请求委托给子控制器类处理

//获取请求路径名
        //http://localhost:8080/项目名/请求路径.action
        String requestURI = req.getServletPath();
        int start=requestURI.indexOf("/");
        int end=requestURI.lastIndexOf(".");
        //获取的是/和.action中的请求路径
        String actionName = requestURI.substring(start, end);
        //根据请求路径名获取子类控制器Action
        //例如:actionName=/HelloAction -->HelloAction extends Action
        Action action = actions.get(actionName);
        //将请求委托给所对应的子控制器Action并执行execute方法
        action.execute(req,resp);

  • 根据请求结果码跳转页面

    private void goToPage(HttpServletRequest req,HttpServletResponse resp,ActionModel actionModel,String jieguoma) throws IOException,ServletException {
        //1、判断请求结果码是否为空
            if(null==jieguoma)
                return;
        //2、根据请求结果码获取ForwardModel对应的建模实体类
        ForwardModel forwardModel = actionModel.get(jieguoma);
        //3、判断ForwardModel是否为空
        if(null==forwardModel)
            throw new RuntimeException("forwardModel不存在!!!!!!");
        //4、转发或重定向
        //isRedirect:检查服务器是否收到的HTTP响应状态码表示一个重定向
        if(forwardModel.isRedirect())
            resp.sendRedirect(req.getContextPath()+forwardModel.getPath());
        else
            req.getRequestDispatcher(forwardModel.getPath()).forward(req,resp);
    }

 

4.2.反射调用方法

创建DispatcherServlet(该类专用于反射调用方法),并继承抽象子控制器类Action,重写execute方法,实现反射方法调用。

/**
 * 该子控制器不做核心业务逻辑层,只负责反射调用方法
 */
public class DispatcherAtion extends Action {
    @Override
    public final String execute(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        try {
            //1、前端必须要传入一个方法名的参数,告知等会要去的
            String methodName=req.getParameter("methodName");
            //2、获取类对象
            Class<? extends DispatcherAtion> cls = this.getClass();
            //反射调用方法
            Method method = cls.getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
            //设置权限
            method.setAccessible(true);
            //调用目标并返回结果
            Object resultCode = method.invoke(this, req, resp);
            //目标方法有可能无返回的方法,resultCode为null
            return null==resultCode?null:resultCode.toString();
        } catch (Exception e) {
            throw new RuntimeException("反射调用方法异常:"+e.getMessage());
        }
    }
}

1.该类中的execute方法是final的,意味着子类无法重写; 2.只要是继承了DispatcherServlet的子控制器类,在调用方法时都必须要传递methodName(可以自行拟定)方法名参数,告知DispatcherServlet类你要调用的具体方法;

重要问题:

  • 为什么在该类中不需要利用反射机制实例化子控制器类?

  • 该类中的this代表谁?

最后,在需要的子控制器类中继承DispatcherServlet类即可,并提供一组方法(增删改查),所有方法都与DispatcherServlet中的execute方法具体相同的参数和返回值,只有方法名不同而已

4.3.反射赋值

创建接口DriverModel<T>,提供getBean方法。

/**
 * 反射赋值
 * 注意:只要是子控制器实现了该接口,则表示需要在调用
 * @param <T>
 */
public interface DriverModel<T> {
    public T getBean();
}

再次修改ActionServlet中的业务逻辑处理流程,在反射调用方法之前,请先进行反射参数赋值操作。

 /**
     * 在目标方法执行之前来完成反射赋值操作
     */
    private void setValue(HttpServletRequest rep,Action action){
        try {
            //判断你子控制器Action是否实现了DriedModel接口
            if(action instanceof DriverModel){
                DriverModel dm = (DriverModel) action;
                Object bean = dm.getBean();
                //调用的三方的方法
                BeanUtils.populate(bean,rep.getParameterMap());
            }
        } catch (Exception e) {
            throw new RuntimeException("反射赋值异常");
        }
    }

最后将setParameter反射赋值方法添加到ActionServlet中的doPost方法中。(必须添加在反射调用方法之前进行反射赋值,如果放置到反射调用方法之后则无意义

        ......
        //7、在目标方法执行之前反射赋值
        this.setValue(req,action);
        //5、将请求委托给具体的子控制器类取处理,并返回请求结果码
        String jieguoma= action.execute(req,resp);
        ......

针对需要进行反射赋值的具体子控制器类,实现该接口DriverModel。

   父类的execute方法举办相同的参数和返回类型
    除了方法名不相同以外
    注意:此时该类可以完成多件事情

public class CalAction extends DispatcherAtion implements DriverModel<CalBean> {
    //注意:一定要提前在子控制器类实例化,不然报错
    private CalBean calBean=new CalBean();
    @Override
    public CalBean getBean() {
        //实例化完毕后,一定要在此数返回
        return calBean;
    }
}

 

只要实现了DriverModel接口,则必须要对实体类进行初始化,并在getModel()方法中返回实例化后的对象。重要!!重要!!重要!!

5.自定义MVC最终版

5.1.什么是框架

框架(Framework),是一套完整的解决方案,使用框架的时候,需要把你的代码放到框架合适的地方,框架会在合适的时机调用你的代码。

特点:

  • 框架规定了自己的编程方式,是一套完整的解决方案

  • 使用框架的时候,由框架控制一切,我们只需要按照规则写代码

  • 框架的侵入性很高(从头到尾)

总之,框架具备入侵性、只是一个半成品,但是框架提供了完整的解决方案,对于代码编写效率得到了大大提升。

5.2.将自定义MVC框架打成jar包

修改web.xml

<!-- 中央控制器 -->
<servlet>
    <servlet-name>ServletAction</servlet-name>
<!-- 核心控制器全路径名 -->
    <servlet-class>com.zking.mvc.framework.ActionServlet</servlet-class>
<!-- 添加自定义配置 -->
    <init-param>
        <param-name>config</param-name>
        <param-value>/mvc.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>ServletAction</servlet-name>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

修改ActionServlet中的init方法

    @Override
    public void init() throws ServletException {
        //读取web.xml文件中的init-param配置文件路径
        String path = this.getInitParameter("config");
        //XML建模方式
        configModel=ConfigModelFactory.createConfigModel(path);
    }

最后,基于IDEA将自定义MVC代码打成jar包。

5.3 综合案例

基于IDEA重新创建一个新的mvcPlus项目,并将自定义MVC的jar包和相关依赖包导入到项目,完成综合案例。

  • 导入自定义MVC的jar包和相关依赖包

  • 导入mvc.xml

  • 配置web.xml

  • 创建BookAction子控制器并继承DispatcherServlet,实现DriverModel接口,定义CRUD方法

  • 创建前端JSP页面实现书本管理的CRUD操作

  • 基于自定义通用分页标签完成实现书本分页查询

  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值