如何保证Struts Action的线程安全?

使用Spring代理Struts Action请求的话这个问题可以通过配置bean的生成方式简单的解决线程安全问题( org.springframework.web.struts.DelegatingActionProxy)

如果自己手工做怎么做?这是我们系统里一个很普遍的架构


public XXBusinessLogicAction extends DispatchAction {

private BusinessLogicServiceLayer  service;

....

}
public BusinessSerivceLayer {

private BusinessLogicDAO dao;

public boolean create(obj) {

dao.create(obj);

}
public BusinessLogicDAO {

private EntityManger em;

public boolean create(obj) {

em.persist(obj);

}

我暂时想的是用ThreadLocal控制BusinessLogicServerLayer对象或者是EntityManager对象,应该可以解决线程问题。

不知道还有啥解决方案?

To be continued…

Struts1 RESTFul Style URL

作为一个程序员我还是更喜欢研究技术问题,喜欢动手实干,吹牛/空谈/诡辩实在不是我的强项啊,这两天开会太多,空闲时间太少,抽空研究了下Struts1 RESTFUL 风格URL的设计和实现。

使用到UrlRewriteFilter,文档 http://urlrewritefilter.googlecode.com/svn/trunk/src/doc/manual/3.1/index.html

用起来还算简单,在web.xml中配置一个filter,大家知道filter和Servlet的工作顺序 filter->Servlet->filter

所以这个其实和Struts1的入口org.apache.struts.action.ActionServlet没啥冲突的。


<!-- UrlRewriteFilter filter -->

<!-- UrlRewriteFilter filter -->
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>
org.tuckey.web.filters.urlrewrite.UrlRewriteFilter
</filter-class>
<init-param>
<param-name>logLevel</param-name>
<param-value>WARN</param-value>
</init-param>
</filter>

如何用这个?要配置一个urlrewrite.xml


<rule>

<note></note>

<from>/user/(.*)</from>

<to type="forward">/UserAction.do?method=$1</to>

</rule>

<outbound-rule>

<note></note>

<from>/UserAction.do\?method=(.*)</from>

<to>/user/$1</to>

</outbound-rule>

rule

from 定义的是用户看得到的,用户发起的,浏览器地址栏的url

to 定义的是程序内部的URL,这里配置的其实是Struts的某个dispatchAction

outbound-rule

from定义的是程序内部的URL,from里的特殊字符需要转义,to里的不需要转义

to 定义的是程序生成的HTML里的link

比如下面的HTML代码生成的两个link是这样的

http://localhost:8080/url_rewrite_demo/user/get

http://localhost:8080/url_rewrite_demo/user/delete


<a href="<c:url value='/UserAction.do?method=get' />">get</a>

<%request.setAttribute("request_method","delete");%>

<html:link action="/UserAction" paramId="method" paramName="request_method">${request_method}</html:link>

可以看到,struts的html:link也如我们期望的那样生成了期望看到的clean的URL。

基于这个我们可以在struts1的基础上支持RESTFul风格URL的API了,

如果在请求的Header里加上不同的值,根据rewrite配置其实可以render HTML/XML/JSON,这样简单的API就能工作了,暂时还没考虑用户认证+权限什么的。

一个风格清晰的URL如下
/users
/user/1 (GET for read)
/user/1/edit (GET for form, POST for update)
/user/1/delete (GET for form, POST for delete)
/user/create (GET for form, POST for create)

为什么EL表达式里的属性名不是javabean的字段呢?

这个问题的场景,相信很多人都写过Struts的helloworld程序,下面是一个常见的hello.jsp页面,请求经由org.apache.struts.action.ActionServlet或者org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter几经处理之后,最后返回ActionForward或者action的result相应的jsp或者各种模板页面。

这个结果页面中本质一直没有变,取得它所需要的值,通常是一个javabean的属性,假设这个javabean是下面的Bean20091228,那么为什么这里我们写的是属性${message}而不是字段${msg}呢?

<%@ page contentType="text/html; charset=UTF-8" %>

<html>
<head>
    <title> JSP Hello World </title>
</head>
<body>
 ${message}
</body>
</html>

要搞清楚问题首先要区分java中类的字段和属性的区别,对,这是显然有区别的

字段:通常是在类中定义的类成员变量 例如:下面的value和msg

属性:类中读写方法的声明,属性的名字不一定要和实际访问的字段相同 例如:下面的类我们可以说Bean20091228有支持读写的属性message, 属性名称默认小写开头。

public class Bean20091228 {

	public Bean20091228(int value, String msg) {
		this.value = value;
		this.msg = msg;
	}
	private int value;
	private String msg;
	public int getVal() {
		return value;
	}
	public void setVal(int value) {
		this.value = value;
	}
	public String getMessage() {
		return msg;
	}
	public void setMessage(String msg) {
		this.msg = msg;
	}
}

搞清楚这个问题之后,下面这段代码用反射机制取得这个类的PropertyDescriptor,并且使用反射设置message属性的值,第5行代码检查是否当前迭代属性是message,第7行代码调用该属性的写方法,设置新的值为”hello,reflection”
该程序执行的打印结果为hello,reflection

	public static void main(String[] args) throws Exception {
		Bean20091228 bean = new Bean20091228(1,"hello,world");

		BeanInfo bi = Introspector.getBeanInfo(bean.getClass());
		for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
			if(pd.getName().equals("message"))
			{
				pd.getWriteMethod().invoke(bean, new Object[]{"hello,reflection"});
			}
		}
		System.out.println(bean.getMessage());
	}

所以EL表达式这玩意,在JSP页面解析的时候,技术上是使用reflection机制取得javabean的attribute的值,这就是为什么我们使用EL表达式的时候,使用的是属性值的原因。

这个问题搞清楚之后,不管是Struts1的BeanUtils参数类型转换,或者是Strut2使用的Interceptor参数类型转换都能触类旁通,想想也知道框架干了哪些事情,是怎么干的了。

最后想说的是Struts的各种书、视频教程很多,大家也都是依葫芦画瓢照做,但是很少有人思考这么基础的问题,学一门东西不是死命的学就能学的好的,应当常常思考。学习学的是方法,每种技术都有它的生命周期,方法永远都是活的,要想在IT的世界活下去,一定要勤于思考,与菜鸟共勉。