by agate - Published: 2009-11-06 [12:14 下午] - Category: 程序编码

这个是搜来的一个 PPT, 介绍的不错.
从个人角度看来就是: 建立 OO 机制选用 Protytpe, 操作 DOM 用 jQuery.
(P.S. 说到 javascript OO. 我宁可自己写 OO 框架, 多轻量!)

Tags: [ , ] - Comments: View Comments
by agate - Published: 2008-05-01 [9:42 下午] - Category: 程序编码

不知道各位使用 Struts1.x 的朋友们晓得不晓得,Struts1.x 的 org.apache.struts.action.Action 中有两个 execute 方法,在我们使用 eclipse 的自动完成 override 功能的时候要是不小心给弄错了你就等着迎接一个不报错的空白页面吧!让我们看看代码:

// one execute
public ActionForward execute(ActionMapping mapping, ActionForm form,
	HttpServletRequest request, HttpServletResponse response) {
	//code...
}
// another execute
public ActionForward execute(ActionMapping mapping, ActionForm form,
	ServletRequest request, ServletResponse response) {
	//code...
}

好,公布结果!只有 override 上面这个 execute 才能起作用。如果你 override 的是下面这个 execute 的话,很不幸,您调用这个 action 的时候响应给你的是一个空白的页面,你也别想得到任何 exception 的提示!

在 eclipse 中我导入了 struts1.x 的 src 路径,并通过 Open Call Hierarchy 查找调用上面第二个 execute 的类时发现竟然没有调用者!换句话说当我们实现第二个 execute 的时候(没有实现第一个 execute ),是根本没用的!程序根本不会调用到我们 override 的那个 execute 只会傻傻地调用第一个 execute 的默认实现,返回一个 null

那第二个 execute 有什么用呢?我觉得他不是用来给我们重写的,看看它的内容:

public ActionForward execute(ActionMapping mapping, ActionForm form,
	ServletRequest request, ServletResponse response)
	throws Exception {
	try {
		return execute(mapping, form,
			(HttpServletRequest) request,
			(HttpServletResponse) response);
	} catch (ClassCastException e) {
		return null;
	}
}

在我看来,其实它是为了当一个请求是一个非 http 请求的时候,作为一个前端转换器,重新包装请求和响应,然后才交给真正的,也就是我们的第一个 execute 方法来实现。这个从 src 的注释中我们也可以比较清晰的了解:

Process the specified non-HTTP request, and create the corresponding non-HTTP response (or forward to another web component that will create it), with provision for handling exceptions thrown by the business logic. Return an ActionForward instance describing where and how control should be forwarded, or null if the response has already been completed.

所以……当你重写这个 action 的 execute 方法时,注意咯!是重写那个参数是 http-request/response 的 execute哦!!!

这里严重鄙视一下 struts1.x 的编码态度!

Tags: [ , , , , , ] - Comments: View Comments
by agate - Published: 2008-04-03 [11:01 上午] - Category: 程序编码

原来还是自己的问题……首先自我检讨一下,常常总是自以为是用自己的思维方式想问题。呵呵,这个真的是打开眼界啊!原来 OGNL 这个玩意有如此大的魔力,难怪 Struts2 使用他哈!厉害……

说道上回写的那个中文问题,本来是以为是转换成什么 Unicode 编码方式了,其实不然,只不过 Struts2 的 OGNL 有点低智商,只认 '' 符号里的西欧字符为字符串,其他的字符出现就会抛个 FormatException 异常……比较罕见,但是这个也使我研究了一小下 OGNL 的“真才实学”。

发现 Struts2 中
<s:if test="#user.sex == 'male' "> ... </s:if>
是可以的

<s:if test="#user.sex == '男' "> ... </s:if>
是不行的,必须改成
<s:if test="#user.sex == '男'.toString() "> ... </s:if>

发现牛逼在何处了吧…… OGNL 竟然除了属性的调用,还可以调用方法。像上面这样使用的话,中文的 OGNL 比较就基本没有问题了。

by agate - Published: 2008-04-02 [9:48 下午] - Category: 历程, 程序编码

最近一直使用struts2进行项目开发,因为之前就是用的webwork进行学习的,可是一直没有使用到页面逻辑的 if 标签。谁知今天用来比较一个String是否等于一个String的时候出现了问题。

这里先对自己不扎实的OGNL知识表示惭愧,我现学现卖地使用了包括EL格式的${},以及参见的${}和#{}以及直接输入属性的方式都无法识别,可是在我使用输出型标签<s:property/>时竟是完好显示的……

百思不得其解。忽然想到Struts2的Ajax是使用Dojo的,这个框架对于除西欧字符以外的所有字符都是使用Unicode的方式进行编码的(所有的字符都要编程类似 'ऩ' 的格式)。莫非……我马上在action端把属性的值进行了url转换,并写了一个测试用的jsp模板页面,里头也对应的把对比的字符串使用url格式的字符串代替了,结果真的通过了……我的妈呀!不会这么弱吧,这个框架的标签功能岂能这样啊!

没法,只好正规点使用其他标签来替代这种页面标签逻辑的方法。打算努力学几天OGNL和“白痴”的Struts2的标签,知道了结果陆续文章跟进……

补充后续探讨:
《续:Struts2的OGNL的中文识别》

Tags: [ , , , , , ] - Comments: View Comments
by agate - Published: 2008-04-02 [5:45 下午] - Category: 程序编码

虽然如今的ws标准都已经很智能了,大多的数据类型都可以识别。但是在GlassFish中,发布的ws方法中参数和返回值不可以为hashmap的,但是如果非要要使用键值对该如何使用呢?
最简单的方法就是再次包装:

public class MapBean {
	private HashMap<String,String> hm;
	public HashMap<String, String> getHm() {
		return hm;
	}
	public void setHm(HashMap<String, String> hm) {
		this.hm = hm;
	}
}
@WebService()
public class HelloWorld {
	public HelloWorld() {
	}
	@WebMethod
	public MapBean getMapBean(HashMap<String,String> hm) {
		MapBean mb = new MapBean();
		hm.put("server", "serverHashMap");
		mb.setHm(hm);
		System.out.println("Hello HashMap...");
		return mb;
	}
}

这样,加一层包装之后,就可以间接地使用Map了。

by agate - Published: 2008-04-01 [7:44 下午] - Category: 历程, 程序编码

IOC(DI)即依赖注入,参见的就是Spring的IOC容器,实现就是实现符合java bean的规范的带有无参构造函数的带有对应set和get方法的一个java类(pojo)。前面都是废话,我们都知道注入一般就是使用set方法,对需要的成员变量进行动态的赋值指向。一般get没有什么用,所以我们用spring管理的时候一般只需写入需要注入对象的set方法即可。

但是今天开发展现层的时候……我用struts的表单自动注入的特性写了一个CRUD模块,当然我还是使用原先的逻辑:只写set方法。结果值没有正确被赋值(有的时候只有部分值被获取)。而且发现如果是简单的数值类型的参数在只设置set方法的情况下是可以被赋值的,但是要是参数是一个ValueObject这样的一个实体对象:

public class User {
	private String userName;
	private String userPwd;
	// --------other private property
	// --------setter
	// --------getter
}

那么,如果我们在action中只对user属性写了set方法

public class UserAction {
	private User user;
	// --------setter
	public String execute() {
		// --------code...
	}
}

那么经过我不下10次的不同试验,保证是无法正确赋值的!因为……还缺了个get方法。也不知道具体原因是什么(猜想在struts2的IOC中,对于复杂对象的注入可能同时用到了set和get方法),但是经验告诉的是set和get方法最好都写!

by agate - Published: 2008-03-27 [7:20 下午] - Category: 未分类

使用hibernate开发的朋友都知道,想在使用hibernate进行持久层操作的时候显示对应的sql语句,可以设置hibernate配置文件中的show_sql属性为true来实现这个需求。可是这个功能比较让人失望……他的语句是没错,但是参数值全是?这个带传入的参数符号,每个调试带来什么方便。

在这里,我介绍两个工具包来解决这个问题:[p6spy]和[sql_profiler],具体主页地址在下列给出
p6spy: http://www.p6spy.com
SQL Profile: http://www.jahia.net/jahia/page597.html

我们先来说说p6spy这个工具。在我理解上,他其实就是一个中间驱动,在数据库调用端和实际数据库驱动间做一个代理人的角色,进而加入了一些特有的辅助功能。在这里我们使用的调用端就是hibernate这个数据持久层框架,而数据库驱动我们用的是mysql的默认驱动(一般就是com.mysql.jdbc.Driver)。好,解释完理论开始实际试验:
* 将p6syp.zip(可能是tar文件包)下载好
* 将包中的p6syp.jar包放入你工程的classpath下(web项目就丢到WEB-INF/lib中)
* 将包中的spy.properties放入src目录中
* 修改hibernate配置文件中的数据库驱动为com.p6spy.engine.spy.P6SpyDriver这个中间驱动
* 修改刚才拷贝进src中的spy.properties文件,将realdriver的值设置成原来hibernate中的真正驱动(这里我是com.mysql.jdbc.Driver)

OK搞定!重新编译项目,以保证p6syp.jar和spy.properties编译进入目标地址。

接着就运行你的程序吧,看看控制台……(不要打我啊,我又没说控制台会有变化=.=)。但是看看你的工程根目录下是不是出现了spy.log,这里面就是你要的数据库操作历史,包括了真正传入的参数的具体值。

如果需要改变spy.log日志的生成地址,可以对应设置spy.properties文件的logfile属性给出绝对地址。(logfile默认是spy.log可以加上路径比如logfile = c:\xxx\xxx.log)

好了,似乎hibernate的?参数问题解决了,但是我的好多朋友都抱怨:“怎么这个log格式这么难懂啊!看着好不爽啊!”的确!p6syp的日志格式让人确实分外头大,于是乎介绍个SQL Profile来帮帮痛苦的大众。

照惯例,介绍介绍理论知识。SQL Profiler是一个基于p6syp的一个辅助包,可以实现很多功能,比如根据你的query来帮你生成合适的索引功能。在这里我们使用它的GUI控制介面,说白了就是一个监听器,在你通过p6spy进行数据库操作时,他就会很清晰地跟踪并列出你的操作,比单单查看那个恶心的spy.log来得舒服!既然基于p6syp当然也要用到p6syp.jar这个包咯,只是必须使用我们下载到的sqlprofiler.zip中的spy.properties来替换原来的项目中的那个文件,因为这个文件是定制过的,所谓的监听器也是靠这个文件才起作用的。好,废话说多了毕竟会烦,来说说具体步骤:
* 将下载好的sqlprofiler.zip包中的 sqlprofiler.jar 和spy.properties两个文件解压出来
* 使用新的spy.properties文件替换工程中原来的那个文件
* 根据刚才添加p6syp的过程,修改 spy.properties 文件的参数
* 使用 java -jar sqlprofiler.jar 命令启动sqlprofiler的GUI介面
* 再次启动你的应用吧,看看是不是在进行数据持久层操作的时候sqlprofiler的GUI介面中便出现了对应的操作记录,很清晰的列了出来:

Tags: [ , , , , ] - Comments: View Comments
by agate - Published: 2008-01-31 [5:00 下午] - Category: 程序编码

其实这个问题一句话就可以讲清楚:过滤器气垫的顺序要有规则的!

在 Struts2 (或者 WebWork )这个展现层框架的配置中,我们使用的前端控制器也是使用 Filter 这种方式管理的。一样是在 web.xml 中配置 Filter 的属性,但是由于我们需要由 Spring 托管整个 web 项目的东西,就拿我们这个 OpenSessionInView 的主角 Session 来说吧,如果这个 Session 是由 Spring 在整个程序运行过程中全权负责的话,它啥时候建立,啥时候消亡是 Spring 说的算的,固然必须由 Spring 先建立好在负责发配个对应的适用对象。啥时候建立呢?当时是当请求来的时候咯,当然得让 Spring 先知道吧!所以啊,这个 Spring 的 OpenSessionInViewFilter 的过滤器必须在 Struts2 的 FilterDispatcher 过滤器前配置。

部分代码大致如下:

<!-- Spring的OpenSessionInView实现 -->
<filter>
	<filter-name>openSessionInViewFilter</filter-name>
	<filter-class>
		org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
	</filter-class>
</filter>
<filter-mapping>
	<filter-name>openSessionInViewFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 添加 Struts2 过滤器 -->
<filter>
	<filter-name>struts2</filter-name>
	<filter-class>
		org.apache.struts2.dispatcher.FilterDispatcher
	</filter-class>
</filter>
<filter-mapping>
	<filter-name>struts2</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

这样,Spring 的过滤器就可以先于 Struts2 的过滤器得知请求的到来,提前做好开启 Session 转载 bean 的各项准备任务。

by agate - Published: 2007-09-13 [8:05 下午] - Category: 程序编码

这几天学习几大框架的整合运用,挺简单的,但是有点搞人耐性……出了一些包啊,tag啊之类的小问题。其中就有这个奇怪的问题,在网上找了些资料贴到这里!

一下引用自"吴星"的MSN空间:

很多人在做webwork+spring+hibernate整合时遇到"严重:Error listenerStart"问题
tomcat启动时报如下错误:

2007-5-31 14:27:13 org.apache.catalina.core.StandardContext start
严重: Error listenerStart
2007-5-31 14:27:13 org.apache.catalina.core.StandardContext start
严重: Context [/testWSH] startup failed due to previous errors

有一种解决方案是把web.xml文件中的
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
<listener-class>
<listener>
改为
<servlet>
<servlet-name>SpringContextServlet<servlet-name>
<servlet-class>.....<servlet-class>
<load-on-startup>1<load-on-startup>
<servlet>

但这种方法可能会出现其他问题(网上又说会导致其他文件无法打开)。

最终解决方案如下:
我用的是tomcat5.5,配置了日志之后打印出下列信息:

ERROR main org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
Caused by:
java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2328)
at java.lang.Class.getConstructor0(Class.java:2640)
at java.lang.Class.getDeclaredConstructor(Class.java:1953)
……

从日志信息看问题已经很明显了,是applicationContext.xmldataSource问题。

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
改为
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

出现"Error listenerStart"一般是applicationContext.xml中的bean加载有问题。在用eclipse做webwork + spring + hibernate 的项目时一般都是用Myeclipse自动生成那些配置文件,而有些相关的jar<!--[if !supportEmptyParas]-->或者文件并没有加载在至项目中,以至引出奇怪的问题,又因为是自动生成的东西所以往往会忽略一些文件,而问题确恰恰是这些生成的文件所致,所以自动化的东西也未必一定是正确的,呵呵……

org.springframework.jdbc.datasource.DriverManagerDataSource 不可以使用连接池。org.apache.commons.dbcp.BasicDataSource作为注入的DataSource源,为了使用 DBCP的功能,必须要将commons-dbcp.jar加入CLASSPATH中,另外还需要commons-pool.jar和commons- collections.jar,这些都可以在Spring的lib目录下找到。
org.springframework.jdbc.datasource.DriverManagerDataSource并没有提供连接池的功能,只能作作简单的单机连接测试。
使用org.apache.commons.dbcp.BasicDataSource时缺少commons-pool.jar所以会出现如题的问题。