温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

深入理解struts的运行机制

发布时间:2020-06-30 01:50:11 阅读:2476 作者:烟花散尽1 栏目:软件技术
开发者测试专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

title: 深入理解struts的运行机制
date: 2016-10-26 20:02
tags: java
categories: 编程

permalink: zxh

个人剖析,不喜勿喷

扫码关注公众号,不定期更新干活

深入理解struts的运行机制这里写图片描述

在此申明本博文并非原创,原文:http://blog.csdn.net/lenotang/article/details/3336623,本文章是在此文章基础上进行优化。也谈不上优化,只是加上了点自己的想法

jar包准备

  • 深入理解struts的运行机制这里写图片描述

  • 为什么会用到这两个jar包呢,因为我需要通过这个jar来解析xml配置文件。

新建项目

  • 深入理解struts的运行机制这里写图片描述

流程梳理

  • struts配置文件

    <?xml version="1.0" encoding="UTF-8"?><struts><package>    <action name="login" method="login" class="org.zxh.action.LoginAction">        <result name="success">/index.jsp</result>        <result name="login">/WEB-INF/login.jsp</result>    </action></package></struts>
  • 熟悉struts的朋友都清楚struts.xml配置文件的重要性,这个配置文件名字是可以更改的,这里简单解释下这个配置文件的作用,首先我们找到action这个节点这个action的name是login,就是说前台中请求这个login经过这个配置文件解析就会把这个请求交给action中的class属性,也就是上面的

    org.zxh.action.LoginAction

    具体的是交由这个类的login(method)这个方法。这个方法会方法一个string类型的字符串,如果返回的是success就将页面重定向到index.jsp如果是login就重定向到login.jsp。这个配置文件就是这样的作用。因为是自己写的,所以这里并不会想struts框架那样封装了很多东西,这里只是为了让读者更加深入的理解struts的运行机制。


如何将我们写的struts.xml文件在程序中启动呢?

  • 刚入门的同志可能会疑问,写一个配置文件就能处理前后台交互了?答案当然是不能。这里给大家普及一下web基础接触filter的,每次交互需要filter(jsp就是特殊的servlet),所以想实现交互我们就得新建一个servlet,在这个servlet里我们去读我们写的struts.xml文件,通过读到的信息决定下一步的操作。那么如何启动一个filter呢?这个不多说,直接在web项目中的web.xml配置拦截器就会执行filter。

新建filter(FilterDispatcher)

  • 这个servlet就是struts的核心过滤器,需要先继承过滤器。

    ```

    public class FilterDispatcher implements Filter{

    @Override
    public void destroy() {
       // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1,
           FilterChain arg2) throws IOException, ServletException {
       // TODO Auto-generated method stub

    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
       // TODO Auto-generated method stub

    }

}

```
  • Filter中我们要在初始化函数(init)中对一些参数进行初始化,对那些数据初始化呢,对!当然是拿配置文件的信息啦。配置文件是.xml这里我用dom4j读取.xml配置文件。 把struts.xml配置文件放在src下,(可以放在其他地方,这里的地址填的对应就行了)

// 获得xml配置文件        String webRootPath = getClass().getClassLoader()                .getResource("struts.xml").getPath();
  • 拿到配置文件路径之后开始读取,这里我讲读到的数据封装到一个map里面。在封装在Map中我们仔细观察一下配置文件

    深入理解struts的运行机制这里写图片描述

  • 其实我们放在Map里面就是这四个属性的值,有了这四个值我们就可以完成一次前后台交互的映射了。所以为了方便这里封装成javabean。

package org.zxh.util;import java.util.HashMap;import java.util.Map;/** * 将action属性封装成类 * @author 87077 * */public class ActionConfig {    //action 给别人调用的名字    private String name;    //action对应程序中的action类    private String clazzName;    //action中的方法    private String method;    //返回结果不知一条 所以用Map    private Map<StringString> resultMap = new HashMap<StringString>();    public ActionConfig(){    }    public ActionConfig(String name , String clazzName , String method , Map<StringString> resultMap){        this.name=name;        this.clazzName=clazzName;        this.method=method;        this.resultMap=resultMap;    }    public String getName() {        return name;    }    public String getClazzName() {        return clazzName;    }    public String getMethod() {        return method;    }    public Map<StringString> getResultMap() {        return resultMap;    }    public void setName(String name) {        this.name = name;    }    public void setClazzName(String clazzName) {        this.clazzName = clazzName;    }    public void setMethod(String method) {        this.method = method;    }    public void setResultMap(Map<StringString> resultMap) {        this.resultMap = resultMap;    }}
  • 有了javabean 我们开始解析xml文件

package org.zxh.util;import java.io.File;import java.util.List;import java.util.Map;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;/** * 采用dom4j解析xml配置文件 *  * @author 87077 *  */public class ConfigUtil {    /**     * @param fileName     *            待解析的文件     * @param map     *            存放解析的数据     */    public static void parseConfigFile(String fileName,            Map<String, ActionConfig> map) {        SAXReader reader = new SAXReader();        try {            Document doc = reader.read(new File("D:\\zxh\\soft\\apache-tomcat-7.0.70\\apache-tomcat-7.0.70\\webapps\\MyStruts\\WEB-INF\\classes\\struts.xml"));            Element root = doc.getRootElement();            List<Element> list = root.selectNodes("package/action");            for (Element element : list) {                // 封装成ActionConfig对象,保存在map中                ActionConfig config = new ActionConfig();                // 获取action中的值                String name = element.attributeValue("name");                String clazzName = element.attributeValue("class");                String method = element.attributeValue("method");                // 将值传入javabean中                config.setName(name);                config.setClazzName(clazzName);                // 如果没有设置执行method 执行默认的                if (method == null || "".equals(method)) {                    method = "execute";                }                config.setMethod(method);                // 继续向下获取action中的返回方法                List<Element> resultList = element.selectNodes("result");                for (Element resultElement : resultList) {                    String resultName = resultElement.attributeValue("name");                    String urlPath = resultElement.getTextTrim();                    if (resultName == null || "".equals(resultName)) {                        resultName = "success";                    }                    config.getResultMap().put(resultName, urlPath);                }                map.put(name, config);            }        } catch (DocumentException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}
  • 现在我们在回到过滤器上,上面两个类就是为了解析xml的。所以在Filter中的init方法里我们就可以将解析的数据放到我们的全局Map中

@Override    public void init(FilterConfig arg0) throws ServletException {        // TODO Auto-generated method stub 过滤器的初始化过程        // 获得xml配置文件        String webRootPath = getClass().getClassLoader()                .getResource("struts.xml").getPath();        // 将xml配置文件解析装在到map中        ConfigUtil.parseConfigFile(webRootPath, map);    }

过滤器的执行

  • 过滤器真正执行是在doFilter方法开始时。

public void doFilter(ServletRequest arg0, ServletResponse arg1,            FilterChain arg2)

doFilter()方法类似于Servlet接口的service()方法。当客户端请求目标资源的时候,容器就会调用与这个目标资源相关联的过滤器的 doFilter()方法。其中参数 request, response 为 web 容器或 Filter 链的上一个 Filter 传递过来的请求和相应对象;参数 chain 为代表当前 Filter 链的对象,在特定的操作完成后,可以调用 FilterChain 对象的 chain.doFilter(request,response)方法将请求交付给 Filter 链中的下一个 Filter 或者目标 Servlet 程序去处理,也可以直接向客户端返回响应信息,或者利用RequestDispatcher的forward()和include()方法,以及 HttpServletResponse的sendRedirect()方法将请求转向到其他资源。这个方法的请求和响应参数的类型是 ServletRequest和ServletResponse,也就是说,过滤器的使用并不依赖于具体的协议。

  • 获取请求域和响应域还有Filter链,并设置编码防止乱码

//针对http请求,将请求和响应的类型还原为HTTP类型        HttpServletRequest request = (HttpServletRequest) arg0;        HttpServletResponse response = (HttpServletResponse) arg1;        //设置请求和响应的编码问题        request.setCharacterEncoding("UTF-8");        response.setCharacterEncoding("UTF-8");
  • 获取请求地址

//获取请求路径String url = request.getServletPath();
  • 通过请求去判断知否拦截过滤这个地址的请求,本文默认过滤所有以.action结尾的请求

//请求地址过滤,如果不是以.action结尾的        if(!url.endsWith(".action")){            //不是.action的放行            arg2.doFilter(request, response);            return ;        }
  • 看我之前将xml文件中数据放入到Map的格式可以看出我是讲整个javabean放入Map中名字是action的name。所以下面我就要去那个name(就是请求中的login)

//解析request路径        int  start = url.indexOf("/");        int end = url.lastIndexOf(".");        String path=url.substring(start+1,end);        //通过path去匹配到对应的ActionConfig类。在这里已经解析到了所有的action的信息        ActionConfig config = map.get(path);        //匹配不成功就返回找不到页面错误信息        if(config==null){            response.setStatus(response.SC_NOT_FOUND);            return ;        }
  • 获取了ActionConfig类了,action的所有信息都存储在这个javabean类中了,下面的事情就好办了。下面的只是会用到反射的知识。我们拿到真正action类的名称后就需要根据名字获取到这个action的实体类。

//通过ActionConfig获取完成的类名字        String clazzName=config.getClazzName();        //实例化Action对象,不存在的话就提示错误信息         Object action = getAction(clazzName);        if(action==null){            //说明这个action是错误的,在配置文件中没有占到对应的action类            response.setStatus(response.SC_NOT_FOUND);            return ;        }

request参数获取并赋值给action

  • 执行action的方法前很定需要先将request中的参数获取到,进行赋值,这部才是真正的意义上的交互。

public static void requestToAction(HttpServletRequest request , Object action )
  • 将传进来的action对象进行class话并获取action实体下的属性

Class<? extends Object> clazzAction = action.getClass();        //获取aciton中所有属性,从前台获取的值很多,只有action属性中有的才会进行反射赋值        Field[] fields = action.getClass().getDeclaredFields();
  • 拿到request传过来的值并进行遍历

//获取请求中的名字属性值        Enumeration<String> names=request.getParameterNames();
String name=names.nextElement();            boolean flag=false;            //需要判断action属性中没有的而请求中有的我们不需要进行反射处理            for (Field field : fields) {                if(name.equals(field.getName())){                    flag=true;                }            }            if(!flag){                return;            }            String[] value=request.getParameterValues(name);
  • 通过request中的name并且在action中有这个属性之后我们就需要获取action这个字段的属性。

Class<Object> fieldType=(Class<Object>) clazzAction.getDeclaredField(name).getType();
  • 获取action的改name字段属性的set方法

//通过反射调用该属性的set方法                    String setName="set"+name.substring(0,1).toUpperCase()+name.substring(1);                    Method method=clazzAction.getMethod(setName, new Class[]{fieldType});
  • 下面我们就需要将获取的value按类型

private static Object[] transfer(Class<Object> fieldType , String[] value){        Object[] os = null;        //fieldType 是[]这种类型的,需要将[]去掉        String type=fieldType.getSimpleName().replace("[]""");        if("String".equals(type)){            os=value;        }else if("int".equals(type)||"Integer".equals(type)){            os = new Integer[value.length];            for (int i = 0; i < os.length; i++) {                os[i] = Integer.parseInt(value[i]);            }        }else if("float".equals(type)||"Float".equals(type)){            os=new Float[value.length];            for (int i = 0; i < os.length; i++) {                os[i]=Float.parseFloat(value[i]);            }        }else if("double".equals(type)||"Double".equals(type)){            os=new Double[value.length];            for (int i = 0; i < os.length; i++) {                os[i]=Double.parseDouble(value[i]);            }        }        return os;    }
  • 获取object数据之后就是讲这个object数据通过反射付给action对应的属性

//判断是否是数组属性                    if(fieldType.isArray()){                        method.invoke(action, new Object[]{object});                    }else {                        method.invoke(action, new Object[]{object[0]});                    }

这说一下 method.invoke是将action类中method方法这个方法需要的参数就是object详解

  • 有了这个方法我们在回到Filter就可以了

//前置拦截,获取request里面的参数,调用action的set方法给属性设置值
        BeanUtil.requestToAction(request, action);
  • 属性赋值完成就开始执行action中的method了

private String executeAction(ActionConfig config, Object action) {
        String method = config.getMethod();
        String result = null;
        try {
            Method callMethod = action.getClass().getMethod(method,String.class);
            result = (String) callMethod.invoke(action, new Object[] {});
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return config.getResultMap().get(result);
    }
  • 到这里你已经获取了配置文件中前台映射后应该的result了,那么就简单了,直接重定向就可以了,到这里就实现了struts的前后台交互。

request.getRequestDispatcher(result).forward(request, response);

  • 下面就在前台jsp中form表单将数据传递给我们的login  action看看会不会去执行指定的方法

详解
  • 有了这个方法我们在回到Filter就可以了

//前置拦截,获取request里面的参数,调用action的set方法给属性设置值
        BeanUtil.requestToAction(request, action);
  • 属性赋值完成就开始执行action中的method了

private String executeAction(ActionConfig config, Object action) {
        String method = config.getMethod();
        String result = null;
        try {
            Method callMethod = action.getClass().getMethod(method,String.class);
            result = (String) callMethod.invoke(action, new Object[] {});
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return config.getResultMap().get(result);
    }
  • 到这里你已经获取了配置文件中前台映射后应该的result了,那么就简单了,直接重定向就可以了,到这里就实现了struts的前后台交互。

request.getRequestDispatcher(result).forward(request, response);

上诉原理的×××

扫码关注公众号,不定期更新干活

深入理解struts的运行机制这里写图片描述

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI

开发者交流群×