javaWeb-编写迷你Structs2框架并使用

miniMVC流程和项目结构:
这里写图片描述

1:创建oa.web.action包,并在其中编写两个对象的action,如:EmployeeAction和DepartmentAction:

package com._520it.oa.web.action;

public class EmployeeAction {
    public void excute(){
        System.out.println("员工的列表");
    }
}


package com._520it.oa.web.action;

public class DepartmentAction {
    public void excute(){
        System.out.println("部门列表");
    }
}

2.1:在源文件夹resources中编写actions.xml文件,为action节点配置name,class,method属性,代表每次请求时需要执行的对象方法:

<!-- actions.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<actions>
    <action name="Employee" class="com._520it.oa.web.action.EmployeeAction" method="excute" />
    <action name="Department" class="com._520it.oa.web.action.DepartmentAction" method="excute" />
</actions>

2.2:在包web.core.config中编写ActionConfig类,用来封装每一个actions.xml中action节点的信息(以后只需在actions.xml中添加action节点即可):

package com._520it.core.web.config;

//使用lombok完成get/set/toString
@Data
public class ActionConfig {
    private String name;
    private String className;
    private String method;

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

2.3:在包filter中编写处理请求的过滤器:

//1.解析actions.xml文件,将actions.xml中的action节点封装成对象,在过滤器中的init中封装成ActionConfigMap
//2.在doFilter中根据请求中的信息,取出ActionConfigMap中的ActionConfig对象,利用反射创建对象并执行函数
package com._520it.core.web.filter;

import java.io.IOException;
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.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

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

import com._520it.core.web.config.ActionConfig;

/**
 * @author lq
 *
 */
@SuppressWarnings("all")
public class ActionFilter implements Filter {
    //将action对象的配置信息封装成对象,并装在Map中,获取时方便用请求名索引
    private Map<String, Object> actionConfigMap = new HashMap<>();

    public void init(FilterConfig filterConfig) throws ServletException {
        //1.获取文档对象
        Document doc = getDocument();
        //2.获取文档中所有action节点的节点集合
        NodeList nodeList = doc.getElementsByTagName("action");
        for (int i = 0; i < nodeList.getLength(); i++) {
            //3.获取每个节点中的属性信息,并封装成配置对象
            Element actionEl = (Element) nodeList.item(i);
            String actionName = actionEl.getAttribute("name");
            String actionClassName = actionEl.getAttribute("class");
            String actionMethod = actionEl.getAttribute("method");
            //4.创建配置文件对象,并放入配置文件对象映射中
            ActionConfig actionConfig = new ActionConfig(actionName, actionClassName, actionMethod);
            actionConfigMap.put(actionName, actionConfig);
        }
    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        try {
            //1.在过滤器中对请求和相应对象强强转成Http类型,方便处理Http请求相关操作
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse resp = (HttpServletResponse) response;
            //2.得到请求中的最后一个/请求的名称
            String requestUri = req.getRequestURI();
            String actionName = requestUri.substring(requestUri
                    .lastIndexOf("/") + 1);
            //3.判断配置对象映射中是否含有当前请求的信息
            if (!actionConfigMap.containsKey(actionName)) {
                chain.doFilter(req, resp);
            }
            //4.获取当前请求操作的配置对象
            ActionConfig actionConfig = (ActionConfig) actionConfigMap.get(actionName);
            //5.通过反射执行请求对象中的函数
            Class clz = Class.forName(actionConfig.getClassName());
            Object actionObj = clz.newInstance();
            Method method = clz.getMethod(actionConfig.getMethod());
            method.invoke(actionObj);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void destroy() {

    }

    //返回文档对象
    public Document getDocument() {
        DocumentBuilderFactory db = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder builder = db.newDocumentBuilder();
            return builder.parse(Thread.currentThread().getContextClassLoader()
                    .getResourceAsStream("actions.xml"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException("解析action.xml文件失败!");
    }
}

3.1:如何在对象Action中得到请求中的参数信息,写一个函数ActionContext,用来封装HttpServletRequest和HttpServletResponse:


//1.含HttpServletRequest和HttpServletResponse参数的构造器,方便构造函数。
//2.既然有了这个构造器,已上两个参数就不需要setter方法了。
//3.设置一个静态私有的ActionContext对象:
    静态目的就是方便该类的静态方法getContext()能调用它,
    私有目的:其他类不能直接调用该变量
package com._520it.core.web;

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

public class ActionContext {
    private HttpServletRequest req;
    private HttpServletResponse resp;

    //虽然此变量是静态的,但是是私有的,和getContext的static不重复
    private static ActionContext mContext;


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

    public static void setContext(ActionContext context){
        mContext = context;
    }

    public static ActionContext getContext(){
        return mContext;
    }


    //因为构造器中已经有传req和resp得ActionContext对象,此处创建get即可
    public HttpServletRequest getReq() {
        return req;
    }
    public HttpServletResponse getResp() {
        return resp;
    }

}

3.2:在过滤器中,将当前的环境变量存储到ActionContext对象中,方便在其他对象中操作请求的信息:


    ActionContext context = new ActionContext(req, resp);
    context.setContext(context);

3.3:在对象Action中调用ActionContext对象,以得到请求中的参数信息:

public class EmployeeAction {
    public void excute(){
        System.out.println("员工的列表");
        ActionContext context = ActionContext.getContext();
        String username = context.getReq().getParameter("username");
        System.out.println("username=" + username);
    }
}

4.但此时在对象ActionContext中有线程不安全情况:

public class EmployeeAction {
    public void excute(){
        System.out.println("员工的列表");
        try {
            Thread.sleep(8000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        ActionContext context = ActionContext.getContext();
        String username = context.getReq().getParameter("username");
        System.out.println("username=" + username);
    }
}
4.1:问题:最后一次请求中的参数值会覆盖前面请求中的参数,主要原因是ActionContext中每次请求使用的是同一个该类中的ActionContext对象成员变量

4.2:解决:
    为容易发生线程不安全的变量使用ThreadLocal,ThreadLocal又称为“线程局部变量”,它为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突(好比每一个线程都使用的是一个新的变量),相应ActionContext的变化:
private static ThreadLocal<ActionContext> threadLocal = new ThreadLocal<>();

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

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

5.在执行完action对象中的函数后跳转到一个页面中:

5.1:在actions.xml中的action节点展开并配置result节点,包含name,type,path,页面名字,请求类型,路径:

<actions>
    <action name="Employee" class="com._520it.oa.web.action.EmployeeAction" method="excute" >
        <result name="list" type="dispatcher" path="/WEB-INF/views/employee/list.html" />
        <result name="edit" type="redirect" path="/WEB-INF/views/employee/edit.html" />
    </action>
</actions>

5.2:在core.web.config中创建ResultConfig对象,封装result节点的属性信息:

package com._520it.core.web.config;
@Data
public class ResultConfig {
    private String name;
    private String type;
    private String path;

    public ResultConfig(String name, String type, String path) {
        this.name = name;
        this.type = type;
        this.path = path;
    }

}

5.3:在ActionConfig类中添加成员变量:

private Map<String, ResultConfig> resultConfigMap = new HashMap<>();

并提供getter和setter方法,用于封装action节点中result节点属性信息。

5.4:在过滤器中,解析actions.xml时,在每个action节点迭代时,迭代得出result属性:

//将action对象的配置信息封装成对象,并装在Map中,获取时方便用请求名索引
    private Map<String, ActionConfig> actionConfigMap = new HashMap<>();
    private Map<String, ResultConfig> resultConfigMap = new HashMap<>();

    public void init(FilterConfig filterConfig) throws ServletException {
        //1.获取文档对象
        Document doc = getDocument();
        //2.获取文档中所有action节点的节点集合
        NodeList nodeList = doc.getElementsByTagName("action");
        for (int i = 0; i < nodeList.getLength(); i++) {
            //3.获取每个节点中的属性信息,并封装成配置对象
            Element actionEl = (Element) nodeList.item(i);
            String actionName = actionEl.getAttribute("name");
            String actionClassName = actionEl.getAttribute("class");
            String actionMethod = actionEl.getAttribute("method");
            //4.创建配置文件对象,并放入配置文件对象映射中
            ActionConfig actionConfig = new ActionConfig(actionName, actionClassName, actionMethod);
            actionConfigMap.put(actionName, actionConfig);

            //5.获取文档中所有result节点的节点集合
            resultConfigMap = new HashMap<>();
            NodeList resultNode = actionEl.getElementsByTagName("result");
            for (int j = 0; j < resultNode.getLength(); j++) {
                Element resultEl = (Element) resultNode.item(j);
                String resultName = resultEl.getAttribute("name");
                String resultType = resultEl.getAttribute("type");
                String resultPath = resultEl.getAttribute("path");

                ResultConfig resultConfig = new ResultConfig(resultName, resultType, resultPath);
                resultConfigMap.put(resultName, resultConfig);
                //6.将该action中所有result节点组成的map对象封装到actionConfig对象中
                actionConfig.setResultConfigMap(resultConfigMap);
            }
        }
    }

5.5:在doFilter中,执行完函数后,得到函数返回值(result的名称),由请求得到action的名称,得到对应ActionConfig对象,由函数返回值得到ActionConfig对象中对应的ResultConfig对象,然后根据请求类型和资源路径跳转到相应界面:

public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        try {
            //1.在过滤器中对请求和相应对象强强转成Http类型,方便处理Http请求相关操作
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse resp = (HttpServletResponse) response;

            //2.将当前的环境变量存储到ActionContext对象中,方便在其他对象中操作请求的信息
            ActionContext context = new ActionContext(req, resp);
            context.setContext(context);

            //3.得到请求中的最后一个/请求的名称
            String requestUri = req.getRequestURI();
            String actionName = requestUri.substring(requestUri
                    .lastIndexOf("/") + 1);
            //4.判断配置对象映射中是否含有当前请求的信息
            if (!actionConfigMap.containsKey(actionName)) {
                chain.doFilter(req, resp);
            }
            //5.获取当前请求操作的配置对象
            ActionConfig actionConfig = (ActionConfig) actionConfigMap.get(actionName);
            //6.通过反射执行请求对象中的函数
            Class clz = Class.forName(actionConfig.getClassName());
            Object actionObj = clz.newInstance();
            Method method = clz.getMethod(actionConfig.getMethod());
            String action = (String) method.invoke(actionObj);
            System.out.println("acton=" + action);

            //7.通过执行函数返回的result的name,得到该action中确切的ResultConfig对象
            ResultConfig resultConfig = actionConfigMap.get(actionName).getResultConfigMap().get(action);
            String type = resultConfig.getType();
            String path = resultConfig.getPath();

            if("dispatcher".equals(type)){
                req.getRequestDispatcher(path).forward(req, resp);
                System.out.println(path);
            }else if("redirect".equals(type)){
                resp.sendRedirect(path);
                System.out.println(path);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

6.导出miniMVC框架:

这里写图片描述

7.使用jar包

7.1:导入jar包并buildpath.

7.2:在web.xml中配置ActionFilter前端控制器:

<filter>
    <filter-name>ActionFilter</filter-name>
    <filter-class>com._520it.core.web.filter.ActionFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>ActionFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

7.3:书写自己的Action函数,如Product:

package A.B.C;

public class Product {
    public String execute(){
        System.out.println("Product.execute()");
        return "welcome";
    }
}

7.4:在resources源文件夹中新建actions.xml文件,并配置:

<?xml version="1.0" encoding="UTF-8"?>
<actions>
    <action name="Product" class="A.B.C.Product" method="execute">
        <result name="welcome" type="redirect" path="/abc.html" />
    </action>
</actions>

7.5:然后在webapp中随便写个html页面,如abc.html

7.6:访问格式:http://localhost:8080/miniMVC/Product

miniMVC:tomcat根/config/server.xml中context元素属性path的值

Product:应该和action中的name向对应;

Product中的execute返回值应与result元素中的name相对应
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值