struts2 paramsPrepareParamsStack拦截器简化代码(源码分析)
一、在讲 paramsPrepareParamsStack 之前,先看一个增删改查的例子。
其中,文件结构如下
|-- org.example.preparable
-- Dao.java
-- Employee.java
-- EmployeeAction.java
struts.xml
|-- webapp
emp-edit.jsp
emp-list.jsp
WEB-INF
-- web.xml
1. Dao.java准备数据和提供增删改查
/**
* 准备数据,提供增删改查方法
*
* @author baojulin
*
*/
public class Dao {
private static Map<Integer, Employee> emps = new LinkedHashMap<Integer, Employee>();
static {
emps.put(1001, new Employee(1001, "AA", "aa", "aa@test.com"));
emps.put(1002, new Employee(1002, "BB", "bb", "bb@test.com"));
emps.put(1003, new Employee(1003, "CC", "cc", "cc@test.com"));
emps.put(1004, new Employee(1004, "DD", "dd", "dd@test.com"));
emps.put(1005, new Employee(1005, "EE", "ee", "ee@test.com"));
}
public List<Employee> getEmployees() {
return new ArrayList<>(emps.values());
}
public void delete(Integer empId) {
emps.remove(empId);
}
public void save(Employee emp) {
long time = System.currentTimeMillis();
emp.setEmployeeId((int) time);
emps.put(emp.getEmployeeId(), emp);
}
public Employee get(Integer empId) {
return emps.get(empId);
}
public void update(Employee emp) {
emps.put(emp.getEmployeeId(), emp);
}
}
2. Employee.java 为model
/**
* 模型
* @author baojulin
*
*/
public class Employee {
private Integer employeeId;
private String firstName;
private String lastName;
private String email;
// 省去get set
3. EmployeeAction 控制器
public class EmployeeAction implements RequestAware, ModelDriven<Employee> {
@Override
public Employee getModel() {
employee = new Employee();
return employee;
}
private Dao dao = new Dao();
private Employee employee;
public String update() {
dao.update(employee);
return "success";
}
public String edit() {
// 编辑,先从数据库获取employee,到页面回显
Employee emp = dao.get(employee.getEmployeeId());
// 因为实现了 ModelDriven 接口, 所以,在到达 edit 之前,会通过 getModel 方法,将 employee 放到值栈中的栈顶
// 所以页面 emp-edit.jsp 可以直接通过栈顶获取 employee
employee.setFirstName(emp.getFirstName());
employee.setLastName(emp.getLastName());
employee.setEmail(emp.getEmail());
return "edit";
}
public String save() {
dao.save(employee);
return "success";
}
public String delete() {
dao.delete(employee.getEmployeeId());
return "success";
}
public String list() {
request.put("emps", dao.getEmployees());
return "list";
}
private Map<String, Object> request;
@Override
public void setRequest(Map<String, Object> req) {
this.request = req;
}
}
在这里,我们可以先看看 struts 对 ModelDrivenInterceptor 拦截器的实现
@Override
public String intercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
// 判断是否实现了 ModelDriven 接口
if (action instanceof ModelDriven) {
ModelDriven modelDriven = (ModelDriven) action;
ValueStack stack = invocation.getStack();
// 通过 getModel 获取 model ,这里是我们的 Employee
Object model = modelDriven.getModel();
if (model != null) {
// 放到栈顶
stack.push(model);
}
if (refreshModelBeforeResult) {
invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
}
}
return invocation.invoke();
}
通过拦截器的源码,可以看到在edit方法中,employee是在栈顶的。
4. 页面的代码 emp-list.jsp
<body>
<s:form action="emp-save">
<s:textfield name="firstName" label="FirstName"></s:textfield>
<s:textfield name="lastName" label="LastName"></s:textfield>
<s:textfield name="email" label="Email"></s:textfield>
<s:submit></s:submit>
</s:form>
<hr>
<br>
<table cellpadding="10" cellspacing="0" border="1">
<thead>
<tr>
<td>ID</td>
<td>FirstName</td>
<td>LastName</td>
<td>Email</td>
<td>Edit</td>
<td>Delete</td>
</tr>
</thead>
<tbody>
<s:iterator value="#request.emps">
<tr>
<td>${employeeId }</td>
<td>${firstName }</td>
<td>${lastName }</td>
<td>${email }</td>
<td><a href="emp-edit?employeeId=${employeeId }">Edit</a></td>
<td><a href="emp-delete?employeeId=${employeeId }">Delete</a></td>
</tr>
</s:iterator>
</tbody>
</table>
</body>
5. emp-edit.jsp页面
<body>
<s:debug></s:debug>
<br>
<br>
<s:form action="emp-update">
<s:hidden name="employeeId"></s:hidden>
<s:textfield name="firstName" label="FirstName"></s:textfield>
<s:textfield name="lastName" label="LastName"></s:textfield>
<s:textfield name="email" label="Email"></s:textfield>
<s:submit></s:submit>
</s:form>
</body>
6 简化上面代码
上面的代码中,不够简洁,我们可以使用struts2的 paramsPrepareParamsStack 拦截器来简化代码
二、struts2的 paramsPrepareParamsStack 拦截器
paramsPrepareParamsStack 拦截器跟 defaultStack 拦截器一样,都是struts拦截器。
关于 struts2 的拦截器栈,可以在struts2-core 的jar包中的 struts-default.xml 配置文件中查看。
1. defaultStack拦截器栈
其中,defaultStack 拦截器栈如下:
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
</interceptor-stack>
这里可以看到,prepare 拦截器,modelDriven 拦截器,和 params 拦截器的执行顺序如下:
prepare --> modelDriven --> params
2. paramsPrepareParamsStack 拦截器栈
<interceptor-stack name="paramsPrepareParamsStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
从拦截器栈的执行顺序中,可以看出如下执行顺序
params --> prepare --> modelDriven --> params
3. 使用 paramsPrepareParamsStack 拦截器栈 的步骤
3.1 实现 Preparable
里面只有一个方法
// 此方法不一定会被调用
// 通过 prepare.alwaysInvokePrepare 参数指定(后面讲)
@Override
public void prepare() throws Exception {
System.out.println("prepare()...");
}
3.2 为 save update edit 方法都增加一个以 prepare 开头的方法(固定写法)
如 : prepareSave,prepareUpdate,prepareEdit
当我们访问save的时候,会先默认访问 prepareSave 方法
同样,访问 update 会先访问 prepareUpdate 方法。
这样,我们就可以在调用save之前创建 employee 对象了,而不用在 getModel 中创建,在getModel 中,每次请求这个action,都会创建一个 employee,浪费资源
public void prepareSave(){
employee = new Employee();
}
在访问edit 的时候,可以先从数据库获取employee
public void prepareEdit(){
System.out.println("prepareEdit()");
employee = dao.get(employeeId);
}
update 也一样
public void prepareUpdate(){
System.out.println("prepareUpdate()");
employee = new Employee();
}
通过这样的方式,为特殊需要获取employee对象定制一个私有的方法,减少资源浪费
3.3 struts.xml 配置 paramsPrepareParamsStack 拦截器栈
<!-- 配置使用 paramsPrepareParamsStack 作为默认的拦截器栈 -->
<!-- 修改 PrepareInterceptor 拦截器的 alwaysInvokePrepare 属性值为 false ,
为 false 时,action中的 prepare 方法不掉用 -->
<interceptors>
<interceptor-stack name="atguigustack">
<interceptor-ref name="paramsPrepareParamsStack">
<param name="prepare.alwaysInvokePrepare">false</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
3.4 修改后的action
修改后的action中可以看出,代码简洁了不少
public class EmployeeAction implements RequestAware, ModelDriven<Employee>, Preparable{
private Dao dao = new Dao();
private Employee employee;
public String update(){
dao.update(employee);
return "success";
}
public void prepareUpdate(){
System.out.println("prepareUpdate()");
employee = new Employee();
}
public String edit(){
return "edit";
}
public void prepareEdit(){
System.out.println("prepareEdit()");
employee = dao.get(employeeId);
}
public String save(){
dao.save(employee);
return "success";
}
public void prepareSave(){
employee = new Employee();
}
public String delete(){
dao.delete(employeeId);
return "success";
}
public String list(){
request.put("emps", dao.getEmployees());
return "list";
}
private Map<String, Object> request;
@Override
public void setRequest(Map<String, Object> arg0) {
this.request = arg0;
}
private Integer employeeId;
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
@Override
public Employee getModel() {
return employee;
}
@Override
public void prepare() throws Exception {
System.out.println("prepare...");
}
}
4. 源码分析 paramsPrepareParamsStack
在 PrepareInterceptor 拦截器中,代码如下。
@Override
public String doIntercept(ActionInvocation invocation) throws Exception {
//获取action,这里是EmployeeAction
Object action = invocation.getAction();
// 判断是否实现 Preparable 借口
if (action instanceof Preparable) {
try {
String[] prefixes;
// firstCallPrepareDo 默认是 false
if (firstCallPrepareDo) {
prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX};
} else {
// prefixes 内容是:[prepare, prepareDo]
prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX};
}
PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);
}
catch (InvocationTargetException e) {
省略...
}
// 这里是调用 Preparable 接口中的 prepare 方法 , 需要 alwaysInvokePrepare 为 true
// 我们在配置文件中,配置了 false ,所以不会执行
if (alwaysInvokePrepare) {
((Preparable) action).prepare();
}
}
return invocation.invoke();
}
PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes); 如下,主要是去获取prepareEdit 类似的方法,如果存在,则执行
public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) throws InvocationTargetException, IllegalAccessException {
Object action = actionInvocation.getAction();
String methodName = actionInvocation.getProxy().getMethod();
if (methodName == null) {
// if null returns (possible according to the docs), use the default execute
methodName = DEFAULT_INVOCATION_METHODNAME;
}
// 获取 prepareEdit 类似的方法
Method method = getPrefixedMethod(prefixes, methodName, action);
if (method != null) {
method.invoke(action, new Object[0]);
}
}
getPrefixedMethod(prefixes, methodName, action); 如下,通过for循环获取刚刚数组 [prepare,prepareDo] ,然后拼接上刚刚的方法名,然后反射调用
public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) {
assert(prefixes != null);
String capitalizedMethodName = capitalizeMethodName(methodName);
for (String prefixe : prefixes) {
// 这里获取 prepareEdit 或 prepareDoEdit ,然后返回
String prefixedMethodName = prefixe + capitalizedMethodName;
try {
return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY);
}
catch (NoSuchMethodException e) {
...
}
}
return null;
}
看到这里,已经知道了为什么会执行prepareEdit 方法了。
其他的prepareUpate 一样。
这样就实现了 paramsPrepareParamsStack 拦截器栈来简化代码
struts2 paramsPrepareParamsStack拦截器简化代码(源码分析)的更多相关文章
- 设计模式(一)——Java单例模式(代码+源码分析)
1)单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能 2)当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不 ...
- Cocos2d-X3.0 刨根问底(六)----- 调度器Scheduler类源码分析
上一章,我们分析Node类的源码,在Node类里面耦合了一个 Scheduler 类的对象,这章我们就来剖析Cocos2d-x的调度器 Scheduler 类的源码,从源码中去了解它的实现与应用方法. ...
- IK分词器原理与源码分析
原文:http://3dobe.com/archives/44/ 引言 做搜索技术的不可能不接触分词器.个人认为为什么搜索引擎无法被数据库所替代的原因主要有两点,一个是在数据量比较大的时候,搜索引擎的 ...
- 【Cocos2d-x 3.x】 调度器Scheduler类源码分析
非个人的全部理解,部分摘自cocos官网教程,感谢cocos官网. 在<CCScheduler.h>头文件中,定义了关于调度器的五个类:Timer,TimerTargetSelector, ...
- *CI框架装载器Loader.php源码分析
http://www.bitscn.com/pdb/php/201411/404680.html 顾名思义,装载器就是加载元素的,使用CI时,经常加载的有: $this->load->li ...
- java框架之Struts2(4)-拦截器&标签库
拦截器 概述 Interceptor (拦截器):起到拦截客户端对 Action 请求的作用. Filter:过滤器,过滤客户端向服务器发送的请求. Interceptor:拦截器,拦截的是客户端对 ...
- struts2 18拦截器详解(十)
ModelDrivenInterceptor 该拦截器处于defaultStack中的第九的位置,在ScopedModelDrivenInterceptor拦截器之后,要使该拦截器有效的话,Actio ...
- 读书笔记-Ribbon源码分析
@LoadBalanced注解用来给RestTemplate做标记,以使用负载均衡的客户端来配置. 通过搜索LoadBalancerClient可以发现,LoadBalancerClient是Spri ...
- STL空间分配器源码分析(一)
一.摘要 STL的空间分配器(allocator)定义于命名空间std内,主要为STL容器提供内存的分配和释放.对象的构造和析构的统一管理.空间分配器的实现细节,对于容器来说完全透明,容器不需关注内存 ...
随机推荐
- composer 国内加速,修改镜像源
为什么慢 由于默认情况下执行 composer 各种命令是去国外的 composer 官方镜像源获取需要安装的具体软件信息,所以在不使用代理.不翻墙的情况下,从国内访问国外服务器的速度相对比较慢 如何 ...
- 记录MNIST采用卷积方式实现与理解
从时间上来说,这篇文章写的完了,因为这个实验早就做完了:但从能力上来说,这篇文章出现的早了,因为很多地方我都还没有理解.如果不现在写,不知道什么时候会有时间是其一,另外一个原因是怕自己过段时间忘记. ...
- Mac版-python环境配置(二):编译器pycharm下载安装
简介 PyCharm是一种Python IDE,带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具,比如调试.语法高亮.Project管理.代码跳转.智能提示.自动完成.单元测试.版本 ...
- event代表事件的状态,专门负责对事件的处理,它的属性和方法能帮助我们完成很多和用户交互的操作;
IE的event和其他的标准DOM的Event是不一样的,不同的浏览器事件的冒泡机制也是有区别 IE:window.event.cancelBubble = true;//停止冒泡window.eve ...
- 字符串格式的Url的截取
一,我们先在看在页面上获取的URL的处理,如下方法: //获取全部URL string Url = Request.Url.ToString(); Url += "</br>&q ...
- C#设计模式:状态者模式(State Pattern)
一,什么是状态设计模式? 1,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新. 2,当一个对象的内部状态改变时允许改变其行为,这个对象看起来像是 ...
- 2019-1-28-WPF-高性能笔
title author date CreateTime categories WPF 高性能笔 lindexi 2019-1-28 14:21:5 +0800 2018-2-13 17:23:3 + ...
- 2018-2-13-win10-UWP-九幽登录
title author date CreateTime categories win10 UWP 九幽登录 lindexi 2018-2-13 17:23:3 +0800 2018-2-13 17: ...
- linuxprobe培训第3节课笔记2019年7月7日
linux常用命令: echo:在终端输出字符串或变量提取后的值 date:显示及设置系统的时间或日期 reboot:重启 poweroff:关机 wget:下载 ps:查看系统中的进程状态(常用参数 ...
- mysql基于Altas读写分离并实现高可用
实验环境准备: master:192.168.200.111 slave1:192.168.200.112 slave2:192.168.200.113 Altas:192.168.200.114 c ...