浏览 3492 次
锁定老帖子 主题:让struts1来模拟struts2
精华帖 (0) :: 良好帖 (1) :: 新手帖 (1) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-07-23
最后修改:2009-07-29
struts2取消了actionform,并且使action成为了多实例的模式,这样在action里就可以使用成员变量了,而在使用了param拦截器后,表单中的值还会自动填充action的成员变量。 今天的目标就是让struts1也来实现这个特性。 首先我们要使struts1的action成为多实例模式--这样我们才能在action中使用成员变量,只要让action由spring来管理就行了。这个很简单,具体做法略。 接着我们要实现表单的值自动填充action的成员变量。在做这个之前我有两重想法: 一种是使用类继承的方式,通过编写一个抽象的action父类,在这个父类的execute方法里,在调用正式的业务处理代码之前,把表单中传来的值赋给各个action子类的成员变量,然后在一个抽象的方法doExecute中执行具体的业务处理,而这个doExecute就由每个子类去实现。 public abstract class AbstractBaseAction extends Action { @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response){ //1.为action的成员便来那个赋值 WebParamUtils.perpareParam(this, request); //2.业务处理 return doExecute(mapping, form, request, response); } //真正处理业务的方法,由子类实现 public abstract ActionForward doExecute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response); } 我们先不管具体的赋值操作时如何实现的,这种想法比较简单,实现起来也很容易。但是我们都知道struts2里的核心是拦截器,在处理这个问题的时候实际上也是使用了param拦截器,这种做法说不上什么不好,只是既然我们想模拟它,那就模拟的更像一点吧。于是又有了第二种做法。 第二种就是通过使用spring aop来实现,很明显这个赋值操作应该在action的execute方法执行前进行,我们这里就使用spring里的前置通知(也有叫前置增强的)来模拟struts2的param拦截器了。 public class ParamAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("在action调用execute方法前,为成员变量赋值"); Action action = null; HttpServletRequest request = null; //取出action if(target instanceof Action){ action = (Action)target; } //取出request for(Object arg : args){ if(arg instanceof HttpServletRequest){ request = (HttpServletRequest)arg; break; } } //为成员变量赋值 if(action != null && request != null){ WebParamUtils.perpareParam(action, request); } } } 接着我们就要为每个action来配置这个前置通知了,这里我们使用自动创建代理的方式来配置。 <!-- action bean --> <bean name="/test" class="personal.smy.showcase.web.TestAction" singleton="false"/> <!-- 切面bean --> <bean id="paramAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedNames"> <list> <value>execute</value> </list> </property> <property name="advice"> <bean class="personal.smy.modules.web.advice.ParamAdvice" /> </property> </bean> <!-- 自动创建代理bean --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> 之后,所有的action的成员变量在调用execute方法之前就会被表单里的值所填充。 这里使用DefaultAdvisorAutoProxyCreator为所有的action bean来创建代理,其实我原先是想用BeanNameAutoProxyCreator来为指定的action bean来创建代理的,但是不知道为什么如果我把action bean配置成singleton="false",那么就无法被代理,而我有试过对于一般singleton="false"的bean,代理是会配置上去的。这个问题谁知道的话,希望指导一下。 最后我们再研究下如何为action里的成员变量赋值,如果成员变量都是简单类型,那么很简单,但是若成员变量是复杂类型,比如有个自定义的User类型,User类型里又有一个自定义的Address类型,而表单中提交的键值对是user.address.name=110,这样我们就要递归的创建外层对象,为最内层变量赋值,最后把对象设置到action中去。这里我们用反射来实现。 public class WebParamUtils { public static void perpareParam(Action action, HttpServletRequest request) { @SuppressWarnings("unchecked") Enumeration e = request.getParameterNames(); while (e.hasMoreElements()) { String fieldName = (String) e.nextElement(); //名 String fieldValue = request.getParameter(fieldName); //值 try { ReflectionUtils .setFieldForObject(action, fieldName, fieldValue); } catch (SecurityException e1) { e1.printStackTrace(); } catch (IllegalArgumentException e1) { e1.printStackTrace(); } catch (InstantiationException e1) { e1.printStackTrace(); } catch (IllegalAccessException e1) { e1.printStackTrace(); } catch (NoSuchMethodException e1) { e1.printStackTrace(); } catch (InvocationTargetException e1) { e1.printStackTrace(); } } } } public class ReflectionUtils { //向上获得类的声明字段 @SuppressWarnings("unchecked") public static Field getDeclaredField(final Class clazz, final String fieldName) { Assert.notNull(clazz, "clazz不能为空"); Assert.hasText(fieldName, "fieldName"); for (Class superClass = clazz; superClass != Object.class; superClass = superClass .getSuperclass()) { try { return superClass.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { // Field不在当前类定义,继续向上转型 } } return null; } //调用对象的set方法 public static void invokeSetMethod(Object instance, Field field, Object fieldValue) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { String fieldName = field.getName(); String setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); Method setMethod = instance.getClass().getMethod(setMethodName, field.getType()); setMethod.invoke(instance, fieldValue); } //调用对象的get方法 public static Object invokeGetMethod(Object instance, Field field) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { String fieldName = field.getName(); String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); Method getMethod = instance.getClass().getMethod(getMethodName, null); return getMethod.invoke(instance, null); } // 获得指定变量的值 public static Object getFieldValue(Object instance, String fieldName) throws IllegalArgumentException, IllegalAccessException { Field field = getDeclaredField(instance.getClass(), fieldName); return getFieldValue(instance, field); } // 获得指定变量的值 public static Object getFieldValue(Object instance, Field field) throws IllegalArgumentException, IllegalAccessException { // 参数值为true,禁用访问控制检查 field.setAccessible(true); return field.get(instance); } //为对象设置属性值 public static void setFieldForObject(Object instance, String fieldName, Object fieldValue) throws InstantiationException, IllegalAccessException, SecurityException, IllegalArgumentException, NoSuchMethodException, InvocationTargetException { int index = fieldName.indexOf("."); if (index != -1) { // 要设置的是一个实体对象 String entityName = fieldName.substring(0, index); String subString = fieldName.substring(index + 1); Field field = getDeclaredField(instance.getClass(), entityName); if (field != null) { Object entity = getFieldValue(instance, field); if (entity == null) { // 不存在的话就创建实体对象 entity = field.getType().newInstance(); } // 递归 setFieldForObject(entity, subString, fieldValue); // 把实体对象设置到父对象中 invokeSetMethod(instance, field, entity); } } else { // 要设置的是一个简单类型对象 Field field = ReflectionUtils.getDeclaredField(instance.getClass(), fieldName); if (field != null) { Object value = ConvertUtils .convert(fieldValue, field.getType()); // 把值转换为相应的类型 invokeSetMethod(instance, field, value); } } } } 再做一个测试action public class TestAction extends Action{ private int code; private String msg; private User user; @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { System.out.println("========================"); System.out.println("code="+code); System.out.println("msg="+msg); System.out.println("user.id="+user.getId()); System.out.println("user.name="+user.getName()); System.out.println("user.address.id="+user.getAddress().getId()); System.out.println("user.address.addr="+user.getAddress().getAddr()); return null; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } } 一切都配置好之后,可以再浏览器中输入http://localhost:8080/case1/test.do?code=1&msg=2&user.id=3&user.name=4&user.address.id=5&user.address.addr=6,你就会在控制台上看到 在action调用execute方法前,为成员变量赋值 ======================== code=1 msg=2 user.id=3 user.name=4 user.address.id=5 user.address.addr=6 这样我们就大功告成了,以上的两种方案都有经过测试,虽然第二种做法比较新颖一点,但是我们知道spring aop使用了代理,而且这里还使用了CGLIB代理,而CGLIB并不适于创建多实例模式bean的代理(和jdk动态代理相比),所以性能上估计会有点损失,显然第一种方案会好点,其实具体的性能怎样我也没有测过,有谁感兴趣的话可以去试试。 源码附件已上传,里面包含了完整的类库,直接解压后即可导入MyEclipse发布运行。注意项目的包结构和文章里的有点不同。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-07-23
这样是不行的,struts1的action是多线程复用的
|
|
返回顶楼 | |
发表时间:2009-07-23
quaff 写道 这样是不行的,struts1的action是多线程复用的 是的 所以我一开始就说了 要把struts1的action由spring来管理 并且设置为singleton=“false” |
|
返回顶楼 | |
发表时间:2009-07-23
标签怎么弄?ognl表达式呢?struts2里还有很多功能呢?还是用struts2和spring2把公司框架做一遍吧
|
|
返回顶楼 | |
发表时间:2009-07-23
xuyao 写道 标签怎么弄?ognl表达式呢?struts2里还有很多功能呢?还是用struts2和spring2把公司框架做一遍吧 也许是我起的文章主题给你产生了误解 我从来没有想过要让struts1全盘模仿struts2 这里仅仅是想模仿struts2的param拦截器。。。 |
|
返回顶楼 | |
发表时间:2009-07-23
呵呵,param 拦截没必要用struts2 ,用xwork系列就可以了
|
|
返回顶楼 | |
发表时间:2009-07-27
写得不错,赞一个!
楼主最好把源码打个包贴一下啊 |
|
返回顶楼 | |