千家信息网

深入理解struts的运行机制

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,title: 深入理解struts的运行机制date: 2016-10-26 20:02tags: javacategories: 编程permalink: zxh个人剖析,不喜勿喷扫码关注公众号,不
千家信息网最后更新 2026年11月03日深入理解struts的运行机制

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

permalink: zxh

个人剖析,不喜勿喷

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

这里写图片描述

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

jar包准备

  • 这里写图片描述

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

新建项目

  • 这里写图片描述

流程梳理

  • struts配置文件


    <struts>
    <package>
    <action name="login" method="login" class="org.zxh.action.LoginAction">
    <result name="success">/index.jspresult>
    <result name="login">/WEB-INF/login.jspresult>
    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中我们仔细观察一下配置文件

    这里写图片描述

  • 其实我们放在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 resultMap = new HashMap();

public ActionConfig(){

}

public ActionConfig(String name , String clazzName , String method , Map 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 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 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实体下的属性

Classextends 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 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);

上诉原理的×××

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

这里写图片描述


文件 方法 配置 属性 参数 就是 过滤器 信息 数据 名字 对象 类型 封装 前台 图片 反射 地址 只是 目标 程序 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 期待互联网科技 紫鲸互联网科技 银优互联网科技有限公司 互联网金融监管科技 创业天下互联网科技有限公司 互联网金融科技公司干啥的 互联网大会黑科技排名 岳阳华臻互联网科技有限公司 互联网科技公司实习生 新兴科技互联网创新大赛颁奖典礼 山东有哪些互联网科技公司 互联网公司高科技产品 电子商务与互联网科技的关系 富国互联网金融科技混合基金 福州德业互联网科技 艺电能源互联网科技有限公司招聘 长沙渺峰互联网科技有限公司 北京互联网科技类企业分布 科技发展第三阶段互联网时代 互联网 科技节活动 微视频大赛 互联网科技公司电话 北京三清互联网科技 中化能源互联网科技招聘 坤平互联网科技 互联网新科技动漫ppt素材 城市宝互联网科技有限公司 浙南科技城互联网大厦招聘 福建互联网展示科技馆设计 互联网的科技体现在哪里 科技互联网专业工作会议发言稿
0