模拟Struts2(miniMVC)

框架和最佳实践

一、什么是框架
框架从何而来,为什么使用框架?

1.是一系列jar包的集合,其本质是对JDK功能的拓展.
    什么是jar,jar有什么作用: jar其实就是class文件的打包,而程序运行只需要有字节    码即可.
    JDK/JRE的功能:JDK只能开发和运行JavaSE.
2.框架是一组程序的集合,包含了一系列的最佳实践,作用是解决某一个领域的问题.
           不同类型的框架解决了不同领域的问题.
3.框架其实就是一个半成品,一般的,我们做开发是基于框架,在框架上继续做开发.

二、 什么是最佳实践

最佳实践(Best Practice):实际上是无数程序员经历过无数次尝试之后,总结出来的处理特定问题的特定方法.
如果把程序员的自由发挥看作是一条通往成功的途径,最佳实践就是其中的最短路径,能极大的解放生产力,提高了效率.

最佳实践三要素:可读性,可维护性,可拓展性.
简单就是美:
消除重复
化繁为简
简单必须可读,简单必须可拓展
减少依赖,消除耦合

三、Web中的最佳实践
Web开发中的最佳实践:分层开发模式(技术层面根据功能职责的不同,"分而治之"):
JavaEE开发根据职责的纵向划分:表现层,业务层,持久层: (SSH)
表现层(Predentation Layer):负责处理与界面交互的相关操作 (Struts2/Spring MVC)
业务层(Business Layer):负责复杂的业务逻辑计算和判断 (Spring)
持久层(Persistent Layer):负责将业务逻辑数据进行持久化存储(DAO) Hibernate/MyBatis

我们习惯吧表现层的框架,称之为MVC框架.

MVC设计思想

一、原理

MVC框架的功能作用(WEB开发常见功能):

MVC令程序开发有章可循,撇开框架,但是表现层的困惑也就出来了.
表现层需要处理的功能(回顾之前Servlet做了哪些功能):
设置编码,接受请求参数,参数类型转换,把参数封装成对象,响应视图,
输入验证,文件上传,文件下载,国际化(i18n),令牌机制,自定义标签.

MVC框架至少就必须具有上述的功能.

二、功能职责

MVC架构型模式,它本身并不引入新的功能,只是用来指导我们改善应用程序的架构,使得应用的模型和视图相分离,从而得到更好的开发和维护效率。

数据模型(Model):负责封装应用的状态,并实现应用的功能。通常又分为数据模型和业务逻辑模型,数据模型用来存放业务数据,比如订单信息、用户信息等;而业务逻辑模型包含应用的业务操作,比如订单的添加或者修改等。

视图展现(View):界面,用来将模型的内容展现给用户,用户可以通过视图来请求模型进行更新。视图从模型获得要展示的数据,然后用自己的方式展现给用户,相当于提供界面来与用户进行人机交互;用户在界面上操作或者填写完成后,会点击提交按钮或是以其它触发事件的方式,来向控制器发出请求。

控制器(Controller):用来控制应用程序的流程和处理视图所发出的请求。当控制器接收到用户的请求后,会将用户的数据和模型的更新相映射,也就是调用模型来实现用户请求的功能;然后控制器会选择用于响应的视图,把模型更新后的数据展示给用户。

前端控制器

Front Controller模式要求在WEB应用系统的前端(Front)设置一个入口控制器(Controller),所有的request请求都被发往该控制器统一处理。
Front Controller一般可以用来做一个共通处理比如认证,页面导航,Session管理,国际化或本地化处理等。
优点:

  1. 集中控制
  2. 提高可管理性和安全控制能力
  3. 提高可重用性可扩展性

前端控制器

前端控制器(front controller):J2EE中的设计模式,主要提供一种可以集中式管理请求的控制器,一个前端控制器可以接受所有的客户请求,完成大部分通用的功能,并将每个请求递交给相应的请求处理对象(Action/Controler),并适当地响应用户。前端控制器完成大部分通用功能,把把具体的操作交给各个Action去完成。

MVC框架都有前端控制器,使用MVC框架的步骤:
1):拷贝框架依赖的jar.
2):在web.xml中配置前端控制器.
ThreadLocal:又称为“线程局部变量”,它为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突(好比每一个线程都使用的是一个新的变量)。

miniMVC

MVC框架的功能作用(WEB开发常见功能):

MVC令程序开发有章可循,撇开框架,但是表现层的困惑也就出来了.
设置编码、接受请求参数、请求数据验证、处理响应、参数类型转换、
把参数封装成对象、文件上传下载、国际化、令牌机制、自定义标签等。

最简单的前端控制器

项目目录层次结构:

最简单的前端控制器

DepartmentAction:

package ee.coding.oa.web.action;

//处理Department相关的请求
public class DepartmentAction {
    public void execute() {
        System.out.println("部门列表");
    }
}

EmployeeAction:

package ee.coding.oa.web.action;

//处理Employee相关的请求
public class EmployeeAction {
    public void execute() {
        System.out.println("员工列表");
    }
}

ActionFilter:

package ee.coding.core.web.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import ee.coding.oa.web.action.DepartmentAction;
import ee.coding.oa.web.action.EmployeeAction;

//前端控制器
public class ActionFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        // 获取请求资源的名称
        String requestUri = req.getRequestURI().substring(1);
        // 根据不同的请求资源做不同的分发操作
        if ("employee".equals(requestUri)) {
            // 创建Action 对象,再调用Action方法
            EmployeeAction action = new EmployeeAction();
            action.execute();
        } else if ("department".equals(requestUri)) {
            DepartmentAction action = new DepartmentAction();
            action.execute();
        }
    }

    public void destroy() {

    }

}

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">

    <!-- 配置前端控制器 -->
    <filter>
        <filter-name>ActionFilter</filter-name>
        <filter-class>ee.coding.core.web.filter.ActionFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>ActionFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

使用Action.xml文件解耦

使用Action.xml文件解耦

新增了3个文件:

actions.xml:

<?xml version="1.0" encoding="UTF-8"?>
<actions>
    <!-- 配置DepartmentAction -->
    <action name="department" class="ee.coding.oa.web.action.DepartmentAction" method="execute"/>
    <!-- 配置EmployeeAction -->
    <action name="employee" class="ee.coding.oa.web.action.EmployeeAction" method="execute"/>
</actions>

ActionConfig:

package ee.coding.core.web.config;

//封装<action name="" class="" method=""/>元素的信息
public class ActionConfig {
    private String name;// 封装<ation/>元素的name属性
    private String className;// 封装<ation/>元素的class属性
    private String method;// 封装<ation/>元素的method属性

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    @Override
    public String toString() {
        return "ActionConfig [name=" + name + ", className=" + className + ", method=" + method + "]";
    }

    public ActionConfig(String name, String className, String method) {
        super();
        this.name = name;
        this.className = className;
        this.method = method;
    }

    public ActionConfig() {
        super();
    }

}

ActionFilter:

package ee.coding.core.web.filter;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import ee.coding.core.web.config.ActionConfig;
import ee.coding.oa.web.action.DepartmentAction;
import ee.coding.oa.web.action.EmployeeAction;

@SuppressWarnings("all")
// 前端控制器
public class ActionFilter implements Filter {
    private Map<String, ActionConfig> actionConfigMap = new HashMap<>();

    public void init(FilterConfig filterConfig) throws ServletException {
        Document doc = getDocument();
        NodeList nodeList = doc.getElementsByTagName("action");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element actionEle = (Element) nodeList.item(i);
            String actionName = actionEle.getAttribute("name");
            String actionClass = actionEle.getAttribute("class");
            String actionMethod = actionEle.getAttribute("method");

            ActionConfig actionConfig = new ActionConfig(actionName, actionClass, actionMethod);
            actionConfigMap.put(actionName, actionConfig);
        }
    }

    private Document getDocument() {
        try {
            InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("actions.xml");
            return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in);
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException("从classpath路径加载actions.xml文件失败");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        // 获取请求资源的名称
        String requestUri = req.getRequestURI().substring(1);
        if (!actionConfigMap.containsKey(requestUri)) {
            chain.doFilter(req, resp);
        }
        ActionConfig actionConfig = actionConfigMap.get(requestUri);
        try {
            Class actionClass = Class.forName(actionConfig.getClassName());
            // 创建Action对象
            Object actionObject = actionClass.newInstance();
            // 调用配置的Action方法
            Method actionMethod = actionClass.getMethod(actionConfig.getMethod());
            actionMethod.invoke(actionObject);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void destroy() {

    }
}

以后再开发Action类,只需要在actions.xml文件中配置即可

处理参数(ActionContext和ThreadLocal)

ActionFilter:

package ee.coding.core.web.filter;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import ee.coding.core.web.ActionContext;
import ee.coding.core.web.config.ActionConfig;
import ee.coding.oa.web.action.DepartmentAction;
import ee.coding.oa.web.action.EmployeeAction;

@SuppressWarnings("all")
// 前端控制器
public class ActionFilter implements Filter {
    private Map<String, ActionConfig> actionConfigMap = new HashMap<>();

    public void init(FilterConfig filterConfig) throws ServletException {
        Document doc = getDocument();
        NodeList nodeList = doc.getElementsByTagName("action");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element actionEle = (Element) nodeList.item(i);
            String actionName = actionEle.getAttribute("name");
            String actionClass = actionEle.getAttribute("class");
            String actionMethod = actionEle.getAttribute("method");

            ActionConfig actionConfig = new ActionConfig(actionName, actionClass, actionMethod);
            actionConfigMap.put(actionName, actionConfig);
        }
    }

    private Document getDocument() {
        try {
            InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("actions.xml");
            return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in);
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException("从classpath路径加载actions.xml文件失败");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        // 获取请求资源的名称
        ActionContext actionContext = new ActionContext(req, resp);
        ActionContext.setContext(actionContext);
        String requestUri = req.getRequestURI().substring(1);
        if (!actionConfigMap.containsKey(requestUri)) {
            chain.doFilter(req, resp);
        }
        ActionConfig actionConfig = actionConfigMap.get(requestUri);
        try {
            Class actionClass = Class.forName(actionConfig.getClassName());
            // 创建Action对象
            Object actionObject = actionClass.newInstance();
            // 调用配置的Action方法
            Method actionMethod = actionClass.getMethod(actionConfig.getMethod());
            actionMethod.invoke(actionObject);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void destroy() {

    }
}

ActionContext:

package ee.coding.core.web;

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

public class ActionContext {
    private HttpServletRequest request;
    private HttpServletResponse response;

    //好比每一个线程都使用一个新的ActionContext对象
    private static ThreadLocal<ActionContext> threadLocal = new ThreadLocal<>();

    public ActionContext(HttpServletRequest req, HttpServletResponse resp) {
        super();
        this.request = req;
        this.response = resp;
    }

    public static void setContext(ActionContext actionContext) {
        threadLocal.set(actionContext);
    }

    public HttpServletRequest getRequest() {
        return request;
    }

    public void setRequest(HttpServletRequest request) {
        this.request = request;
    }

    public HttpServletResponse getResponse() {
        return response;
    }

    public void setResponse(HttpServletResponse response) {
        this.response = response;
    }

    public static ActionContext getmActionContext() {
        return threadLocal.get();
    }

}

ThreadLocal的使用

响应视图(ResultConfig):

在ResultConfig中封装结果视图,在前端控制器中控制页面的跳转

miniMVC代码:miniMVC.zip

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注