`
mengyang
  • 浏览: 266141 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

让struts1来模拟struts2

    博客分类:
  • SSH
阅读更多
  公司里的项目用的框架是常见的SSH,只是使用的是struts1和spring1,都略显得有点老旧了。之前看了阵struts2,感觉比struts1先进了很多,但是我想公司是不可能随便升级框架的,正好这两天闲着没什么事做,琢磨着该做些什么了。于是我就想让struts1模拟一些struts2的特性。
  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发布运行。注意项目的包结构和文章里的有点不同。
分享到:
评论
6 楼 jakoes 2009-07-27  
写得不错,赞一个!
楼主最好把源码打个包贴一下啊
5 楼 kjj 2009-07-23  
呵呵,param 拦截没必要用struts2 ,用xwork系列就可以了
4 楼 mengyang 2009-07-23  
xuyao 写道
标签怎么弄?ognl表达式呢?struts2里还有很多功能呢?还是用struts2和spring2把公司框架做一遍吧

也许是我起的文章主题给你产生了误解 我从来没有想过要让struts1全盘模仿struts2 这里仅仅是想模仿struts2的param拦截器。。。
3 楼 xuyao 2009-07-23  
标签怎么弄?ognl表达式呢?struts2里还有很多功能呢?还是用struts2和spring2把公司框架做一遍吧
2 楼 mengyang 2009-07-23  
quaff 写道
这样是不行的,struts1的action是多线程复用的

是的 所以我一开始就说了 要把struts1的action由spring来管理 并且设置为singleton=“false”
1 楼 quaff 2009-07-23  
这样是不行的,struts1的action是多线程复用的

相关推荐

    struts2简单模拟

    通过实践简单的Struts2模拟代码,可以更好地理解和体验Struts2框架的工作原理和优势。在实际开发中,还需要不断学习和熟悉Struts2的高级特性,如自定义拦截器、类型转换、插件使用等,以提升开发能力。

    struts1和struts2的区别

    ### Struts1与Struts2的主要区别 #### 概述 Apache Struts 是一个用于构建企业级Java Web应用的开源框架。它分为两个版本:Struts1 和 Struts2。虽然两者都基于模型-视图-控制器(MVC)设计模式,但它们之间存在...

    servlet模拟struts1工作原理

    本文将通过使用Servlet来模拟Struts1的工作流程,深入理解其核心机制。 首先,让我们探讨Struts1的基本架构。Struts1框架的核心组件包括ActionServlet、ActionForm、Action、Tiles以及配置文件(struts-config.xml...

    Struts2与Struts1区别

    - Struts1 的配置文件较为繁琐,而 Struts2 提供了更简洁的 XML 配置方式,同时支持注解配置,让配置更加直观和易懂。 8. **拦截器(Interceptors)**: - Struts2 引入了拦截器机制,这是一个重要的增强,允许...

    struts2框架模拟-教学示范代码

    首先,让我们深入了解Struts2的核心概念: 1. **Action类**:在Struts2中,业务逻辑主要由Action类处理。Action类是用户请求的接收者,负责处理请求并返回相应的结果。 2. **配置文件**:Struts2使用XML配置文件...

    Servlet简单模拟Struts2

    在本主题"Servlet简单模拟Struts2"中,我们将探讨如何利用Servlet来实现类似Struts2框架的一些关键功能,包括请求拦截、XML配置解析、动态代理以及日志拦截。 首先,让我们了解一下Struts2框架的核心特性。Struts2...

    struts1&struts2

    Struts1和Struts2是两个著名的...总结来说,Struts2在很多方面改进了Struts1.x的设计,提供了更灵活的架构、更简单的配置、更好的测试支持以及更直接的数据绑定方式。这使得Struts2成为Java Web开发中更受欢迎的选择。

    Struts1和Struts2的区别和对比

    Struts1和Struts2是两个非常著名的Java Web框架,它们在设计模式、可测试性、输入处理和表现层等方面...总的来说,Struts2相对于Struts1在灵活性、可测试性、输入处理和表现层控制上有所改进,使得开发更为高效和便捷。

    转:struts1与struts2的区别

    为了解决这一问题,社区开发了第三方工具如Struts TestCase,它提供了一套用于模拟Struts1环境的组件。 - **Struts2**: 在Struts2中,可以通过初始化、设置属性和调用方法等方式轻松地测试Action。依赖注入的支持...

    Struts1与Struts2本质区别

    ### Struts1与Struts2本质区别 #### 1. 在Action实现类方面的对比 - **Struts 1**:要求Action类必须继承一个抽象基类`Action`,这种设计方式意味着开发者必须按照预设的结构来编写代码,灵活性较低。这种通过抽象...

    struts2-scan_struts2-scan_struts2scan_scan_struts2漏洞_

    1. 及时更新Struts2框架到最新版本,修补已知的安全漏洞。 2. 避免在视图层使用未经验证的用户输入,尤其是在OGNL表达式中。 3. 使用过滤器或拦截器来限制不安全的HTTP请求头。 4. 对敏感信息进行加密处理,防止数据...

    模拟struts

    在"模拟Struts"这个项目中,你可能已经尝试实现了Struts框架的核心功能,让我们深入探讨一下Struts的关键知识点。 1. **MVC设计模式**:MVC是一种将业务逻辑、数据和用户界面分离的设计模式。在Struts中,模型...

    K8 Struts2 Exp 20160516(Struts2综合漏洞利用工具)

    总的来说,Struts2漏洞是企业信息安全的重要威胁,需要开发者和运维人员时刻保持警惕,通过持续的安全更新和严格的安全策略来降低风险。而“K8 Struts2 Exp 20160516”这样的工具提醒我们,对安全漏洞的利用工具必须...

    struts1.x和struts2.x区别

    Struts1.x的测试通常依赖于对Servlet API的模拟,需要使用如StrutsTestCase等工具来搭建测试环境。相比之下,Struts2.x提供了更为灵活的测试机制,可以通过Mock对象轻松地进行单元测试,同时也支持集成测试,提高了...

    自己模拟实现struts的dispatcherAction

    2. **模拟实现DispatcherAction**: 要模拟实现DispatcherAction,首先我们需要创建一个类,这个类将负责接收请求、解析配置信息以及调用目标Action。这个类可能包含以下方法: - `init()`: 初始化方法,用于加载...

    struts2整合hibernate的网上银行模拟项目

    Struts2 是 Struts 的下一代,它继承了Struts1的优点并解决了其不足。该框架的核心是Action类,它处理用户的请求,并通过结果映射决定返回哪个视图。Struts2 使用拦截器(Interceptor)机制来扩展功能,如验证、日志...

    servelt模拟struts1框架

    总结来说,使用Servlet模拟Struts1框架主要涉及以下几个步骤: 1. 创建并解析XML配置文件,获取ActionMapping。 2. 实现请求解析,找到匹配的ActionMapping。 3. 创建ActionForm对象,封装请求参数。 4. 调用业务...

    struts1示例代码

    Struts1是一个经典的Java Web框架,它为开发者提供了一种结构化的MVC(Model-View-Controller)设计模式实现...同时,这也是对实际项目开发中常见登录功能的模拟,可以帮助我们理解如何在Struts1框架下实现用户认证。

    Struts2_Android(2.2)_模拟数据访问项目 JSON项目

    这个项目可能是为了展示如何在Android应用中利用Struts2提供的服务接口来获取或提交模拟的数据。 首先,Struts2的JSON插件允许开发者将Action结果直接转换为JSON格式,以便于Android或其他客户端进行解析和显示。这...

    全网最全Struts 2 全版本漏洞检测工具,最新struts漏洞更新

    1. **OGNL漏洞扫描**:检测Struts 2应用中是否存在可被恶意利用的OGNL表达式,如未过滤的用户输入或不安全的配置。 2. **插件漏洞扫描**:Struts 2有众多插件,这些插件可能存在单独的安全问题,工具会检查已安装...

Global site tag (gtag.js) - Google Analytics