个人剖析,不喜勿喷

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

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

jar包准备

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

新建项目

流程梳理

  • struts配置文件
```
<?xml version="1.0" encoding="UTF-8"?>

/index.jsp
/WEB-INF/login.jsp

```

  • 熟悉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<String, String> resultMap = new HashMap<String, String>(); public ActionConfig(){ } public ActionConfig(String name , String clazzName , String method , Map<String, String> 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<String, String> 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<String, String> 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<a href="http://www.oschina.net/code/snippet_216465_36771”>详解

  • 有了这个方法我们在回到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看看会不会去执行指定的方法
<form method="post" action="login.action" name="loginForm">
<table width="422" border="1" bgcolor="#0080c0" height="184">
<caption>
<h1>用户登陆</h1>
</caption>
<tbody>
<tr>
<td>&nbsp;姓名:</td>
<td>&nbsp; <input type="text" name="username">
</td>
</tr>
<tr>
<td>&nbsp;密码:</td>
<td>&nbsp; <input type="password" name="password">
</td>
</tr>
<tr align="center">
<td colspan="2">&nbsp; <input type="submit" value="登陆"
name="submit"> <input type="reset" value="重置" name="reset">
</td>
</tr>
</tbody>
</table>
</form>
  • 效果读者自行展示吧,到这里struts的运行机制就讲完了,注意知识运行机制里面还有很多值得我们学习的东西,就好比说这里有很多过滤器,不同过滤器过滤数据程度不同执行效果不同。希望有机会再和大家分享一些其他关于struts的知识!

上诉原理的源码下载

手写Struts,带你深入源码中心解析的更多相关文章

  1. 实现简单的手写涂鸦板(demo源码)

    在一些软件系统中,需要用到手写涂鸦的功能,然后可以将涂鸦的结果保存为图片,并可以将"真迹"通过网络发送给对方.这种手写涂鸦功能是如何实现的了?最直接的,我们可以使用Windows提 ...

  2. 手把手教你使用LabVIEW OpenCV DNN实现手写数字识别(含源码)

    @ 目录 前言 一.OpenCV DNN模块 1.OpenCV DNN简介 2.LabVIEW中DNN模块函数 二.TensorFlow pb文件的生成和调用 1.TensorFlow2 Keras模 ...

  3. [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...

  4. Android AsyncTask完全解析,带你从源码的角度彻底理解

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11711405 我们都知道,Android UI是线程不安全的,如果想要在子线程里进 ...

  5. Android -- 带你从源码角度领悟Dagger2入门到放弃

    1,以前的博客也写了两篇关于Dagger2,但是感觉自己使用的时候还是云里雾里的,更不谈各位来看博客的同学了,所以今天打算和大家再一次的入坑试试,最后一次了,保证最后一次了. 2,接入项目 在项目的G ...

  6. Android -- 带你从源码角度领悟Dagger2入门到放弃(二)

    1,接着我们上一篇继续介绍,在上一篇我们介绍了简单的@Inject和@Component的结合使用,现在我们继续以老师和学生的例子,我们知道学生上课的时候都会有书籍来辅助听课,先来看看我们之前的Stu ...

  7. Android -- 带你从源码角度领悟Dagger2入门到放弃(一)

    1,以前的博客也写了两篇关于Dagger2,但是感觉自己使用的时候还是云里雾里的,更不谈各位来看博客的同学了,所以今天打算和大家再一次的入坑试试,最后一次了,保证最后一次了. 2,接入项目 在项目的G ...

  8. Google Protocol Buffers 快速入门(带生成C#源码的方法)

    Google Protocol Buffers是google出品的一个协议生成工具,特点就是跨平台,效率高,速度快,对我们自己的程序定义和使用私有协议很有帮助. Protocol Buffers入门: ...

  9. [学习总结]7、Android AsyncTask完全解析,带你从源码的角度彻底理解

    我们都知道,Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制.之前我也写过了一篇文章从源码层面分析了Android的异步消息处理机制,感兴 ...

随机推荐

  1. kuangbin专题 专题二 搜索进阶 Escape HDU - 3533

    题目链接:https://vjudge.net/problem/HDU-3533 题目分析: 1.人不能经过碉堡; 2.敌军碉堡可能建到我军基地 3.子弹碰到碉堡就没了,说明子弹会被别的城堡给拦截下来 ...

  2. 自己实现定制自己的专属java锁,来高效规避不稳定的第三方

    java juc 包下面已经提供了很多并发锁工具供我们使用,但在日常开发中,为了各种原因我们总是会用多线程来并发处理一些问题,然而并不是所有的场景都可以使用juc 或者java本身提供的锁来方便的帮助 ...

  3. 嵊州D2T2 八月惊魂 全排列 next_permutation()

    嵊州D2T2 八月惊魂 这是一个远古时期的秘密,至今已无人关心. 这个世界的每个时代可以和一个 1 ∼ n 的排列一一对应. 时代越早,所对应的排列字典序就越小. 我们知道,公爵已经是 m 个时代前的 ...

  4. Linux 终端连接工具 XShell v6.0.01 企业便携版

    NetSarang Xshell – 知名终端连接工具,非常强大的SSH远程终端客户端 ,非常好用的SSH终端管理器.Xshell功能超级强大,性能非常优秀,其特色功能支持多标签会话管理主机,支持远程 ...

  5. c#零碎知识随笔

    1. 字符串转换日期: DateTime.ParseExact(item.Attribute("event-timestamp").Value,"dd.MM.yyyy H ...

  6. Java实现LRU算法

    一.LRU算法简介 LRU(Least Recently Used)最近最久未使用算法 常见应用场景:内存管理中的页面置换算法.缓存淘汰中的淘汰策略等 二.实现理论 底层结构:双向链表 + HashM ...

  7. zimg服务器图片数据迁移后,图片404异常的问题解决

    由于zimg特殊的图片存储结构及图片命名规则,其迁移数据应该当相当简单的,仅把对应的存储图片数据的文件夹复制即可.往往简单的东西总会有一些成本在里面,下面是我简单的迁移测试过程中遇到的一些问题,仅供参 ...

  8. java中几个常见的问题

    1.正确使用equals方法 Object的equals方法容易抛出空指针异常,应使用常量或确定有值的对象来调用equals方法 例如: //不能使用一个值为null的引用类型变量来调用非静态方法,否 ...

  9. 使用反射机制将对象序列化Json

    一 思路 获取对象的Class对象. 获取对象的属性数组, 迭代属性数据拼接属性名与属性值, 存入List. 将List转换为流库, 再将流库使用逗号分隔符转换为字符串, 去掉首尾的逗号 二 代码 p ...

  10. 详解svm和smo的出现

    SupportVector Machines are learning models used forclassification: which individuals in a population ...