by agate - Published: 2008-04-08 [2:12 下午] - Category: 程序编码
最近使用struts1.x进行项目开发,发现虽然struts2的设计让功能更为强大,但是struts1.x的开发更好定制!完全servlet体系的扩展,虽然有耦合的嫌疑吧,但是让很多从Servlet/JSP开发转移过来的朋友大概会有很大的亲切感。
以下是我得出的Struts1 + Spring + Hibernate整合开发的一些建议:
1.所有页面都用struts来转发!不能直接访问到资源页面。把所有资源页面放在WEB-INF\pages下
2.虽然很多文档中似乎可以不用在struts-config.xml中以plugin的方式启用spring托管【在web.xml中声明监听器】,但是加载spring框架方式使用struts-plugin的这种老式的方式保守。
3.对此!我们的action的type通通改成org.springframework.web.struts.DelegatingActionProxy这个代理类!交给spring动态获取这个action实例!
对此必须设定对应action的bean配置,此bean的并非配置id标示属性,改为配置name属性其值为struts的action的path值。
4.使用ActionForm的validate方法来验证表单数据的合法性。用代码来控制,不使用xml配置。
5.显示页面部分,我们统一使用el表达式+struts标签形式!不使用scriptlet的脚本形式,保证页面的高可读性!
其他:有可能我们要使用到spring提供的open-session-view的过滤器,来处理持久层的东西,那么我们不得不吧spring的托管放在web.xml中因为这个过滤器要用到那个监听器……注意,事实证明这样是更为优秀的整合手段,可以放心使用!
还有的当然就是我们的编码问题咯!这是一个十分冗长的问题,需要时间来研究【特别是tomcat】的具体运作问题!接下来我会写一个文章来参数这个问题的
by agate - Published: 2008-04-06 [4:38 下午] - Category: 程序编码
今天郁闷了2个小时,不停地试验,就是没成功……(试验什么呢?)就是这个,用HQL来查出一个List,条件是某个字段为空。
两个领域类:
public class User {
private String username;
private Group group;
// ------------- setter
// ------------- getter
}
public class Group {
private String groupName;
private List<user> users;
// ------------- setter
// ------------- getter
}
让你查数据库中所有group为空的(即加入组的)所有用户。
Read more...
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: 2008-01-31 [4:44 下午] - Category: 程序编码
所谓 OpenSessionInView 就是真对 Hibernate 这个数据持久层框架的一个Web下的使用技巧。我们在日常 MVC 程序开发的过程中,如果使用的是 Hibernate 的数据持久层框架,一定常常在业务逻辑层中出现 Session is closed 的 laze 异常,这是由于 Hibernate 的Session 在上一个操作中我们为了保证 Session 已经及时的回收时被我们关掉了。
我们常常被放在一个很尴尬的地带:我们的 DAO 成负责 CRUD 在执行完成后根据我上面说的那个“好习惯”,肯定要把本次操作的Session关掉,或者说在一个点统一被关掉。那么导致的结果是,当我拿到我们需要的对象时,该对象虽然说是数据完整的,但是他可能有一个 Link 对象,这个对象可是不完整的!顶多是一个包含 ID 的空壳。那很自然的,当我们要访问这个对象中的 Link 对象的某个非ID值的时候,必然抛出“Laze”异常。如果我们之前不关闭那个 Session 似乎一般的解决方案没法告诉在下一个使用层的代码何时关闭,虽然可以成功访问 Link 对象,但是长此以往 Session 就大大占用内存了。
如何设计才好呢?在很多 Hibernate 的使用者中就有人很早地提出了 Open Session In View 这个Web开发的重要理念,即让 Hibernate 的 Session 在整个请求周期中生存(包括建立和消亡)。当然这个是理念,实现手段有很多种了,包括什么参数传递、建立辅助包等等等等。这里我们专门提及 Spring 这个万能的整合型框架是如何处理的。
Spring 为我们提供了一个叫做 OpenSessionInViewFilter 的过滤器,他是标准的 Servlet Filter 所以我们把它按照规范配置到 web.xml 中方可使用。使用中我们必须配合使用 Spring 的 HibernateDaoSupport 来进行开发,也就是说,我们的dao层的类都要继承于 HibernateDaoSupport,从中由 Spring 来控制 Hibernate 的 Session 在请求来的时候开启,走的时候关闭,保证了我们访问数据对象时的稳定性。
下面我大概描述一下使用的方法:
1. 在 web.xml 中加入对应过滤器配置文件
<!-- 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>
2. 在我们访问持久层数据是使用 Spring 为我们的 HibernateDaoSupport 的支持,并使用其中的对应方法操作我们的持久层数据
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class XxxDAO extends HibernateDaoSupport {
public void save(Xxx transientInstance) {
try {
getHibernateTemplate().save(transientInstance);
} catch (RuntimeException re) {
throw re;
}
}
public void delete(Xxx persistentInstance) {
try {
getHibernateTemplate().delete(persistentInstance);
} catch (RuntimeException re) {
throw re;
}
}
public Xxx findById(Integer id) {
try {
Role instance = (Xxx) getHibernateTemplate().get("Xxx", id);
return instance;
} catch (RuntimeException re) {
throw re;
}
}
public List findByProperty(String propertyName, Object value) {
try {
String queryString = "from Xxx as model where model."
+ propertyName + "= ?";
return getHibernateTemplate().find(queryString, value);
} catch (RuntimeException re) {
throw re;
}
}
}
其实啊,就是调用 HibernateDaoSupport 中的 getHibernateTemplate() 这个模板方法,再使用里头的对应对持久层操作的方法。
这样,Spring 这个优秀的整合型框架就是轻松地帮我们把烦恼已久的问题解决了。
by agate - Published: 2008-01-26 [12:09 下午] - Category: 程序编码
连续写了两次AOP的文章,总是觉得没有吧AOP说得很清楚。而且网络上每一篇AOP的教程都是充斥着大量专业术语,令人甚是费解。打算把各个术语根据spring这个aop的实现来串讲一边。
还是从“日志记录说起吧”,几乎我们所有的程序中都需要记录日志的,这是一个共通的需求。
如果我们要把日志记录提取出来,作为一个抽象的独立的服务我们可以叫它为 Advice (“通知”,说白了,advice就是一个功能逻辑)。
然后我们将这个“功能逻辑 ”-advice 加入到我们需要的地方,而这个地方就是我们所谓的 PointCut ,(“切入点”,我们使用中最常看到的就无非是:方法前、方法后、出异常时等等)。
重复上一篇文章的话:我们的 Aspect = Advice + PointCut 这样我们就形成了我们AOP最为基础“切面”
那么我们要把这个 advice 加在哪里呢?也就是说这个PointCut在哪里呢?这个就是我们所谓的 Target (“目标对象”)。我们的目标说白了也就是目标对象中的某些需要我们这个日志服务的方法罢了。
OK,我们有了我们的“通知”、“切入点”,也有了我们的“目标对象”,那么将“通知”加入到“目标对象”的“切入点”的动作就叫 weaving (“织入”)。呵呵,这个当然是有spring容器在运行时执行的咯,但是多我们来说就是在applicationContext.xml中加入对应的配置段咯。
这里,还有一个名词 Proxy (“代理对象”),如何理解这个词呢?它在实际使用中又是什么呢?我为此特别画了一张图:

所谓 Proxy 这个代理,说白了,在 Spring 的 AOP 应用中,就是将你那些需要插入服务的目标对象在一个新的、模拟好你这些目标对象的现有方法接口的一个新的对象。正如我画的这个图所示,其实在程序运行过程中我们使用的比不是我们自己设计出来的那个“DiyBean”,而是由 Spring 容器自动生成的一个对象“DiyBeanProxy”。我们调用的那些被声明配置过的方法时,也不是用我们对象默认的方法,而是根据配置的不同,重新生成的代理对象中的对应方法。就比如我们这图中的“methodTwo()”方法,代理对象中的(1)、(2)方法段其实就是对应着的就是切入点的功能啦,(1)就是等于“beforeMethod”,(2)就是相应的“afterMethod”了(当然还有在抛出异常的时候的服务我没有画出)。
综合来说,AOP不是什么特新的思想,就是我们一切业内的术语过于专业、抽象。不就是一个拦截,一个重新打包的过程嘛!不难!
by agate - Published: 2008-01-19 [10:44 下午] - Category: 程序编码
近日一直沉醉在 O'REILLY 的群书中,记了不少学习笔记,受益匪浅。在看《Spring程序高手秘籍》中看到了译者注释的一段话,很好的(至少比我自己解释的好)。
他是这么说地:
AOP:即为aspect面向程序设计,是 OOP 的一种的补充或改进。
将 OOP 应用到现实世界时,时常发现系统中的各类会有许多共同使用的逻辑或责任(例如日志管理、权限控制、调度处理等) ,这些 “服务” 不断重复地 “切入” 各类中。
如果功能流程是纵向的,这些切入点就是所谓的 point-cut,而在此提供服务的额外程序代码就是所谓的 advice。简而言之,point-cut 与 advice 的组合就成为的 aspect,就是这些共同使用的逻辑或责任的封装。
通过 AOP 可将程序的责任理清,对象与 aspect 互不干扰。另外,aspect 的模组并非显示地为对象所用,而是通过依赖注入或拦截方式获得被封装在对象内部的方法与信息。
by agate - Published: 2007-12-07 [9:41 下午] - Category: 程序编码
其实我的个人表达能力不是很好……估计小时候语文没学好~也可能是aop这个概念本身意会的程度很大吧!呵呵寒暄一下!
正式进入主题:AOP 即 Aspect Oriented Programming 的缩写,中文译为"面向切面编程"。本篇没那么学术化,只是为了快速入门了解真实的使用方式!我们不用那么早去想这个名词的意义,fellow me,用真实的代码透析AOP的含义。
不知各位有没有项目开发的经验,如果有的话你应该可以清楚地了解到我们的代码中常常充斥着大量的日志记录代码,我们用log4j等日志记录工具一段一段地记录程序运行的信息。也许有个别是十分特殊的,但是似乎大部分都是例行公事吧!或者在丢出exception的时候捕获其message然后记入日志对吗?这样的代码难道不觉得碍眼?修改起来是不是也很麻烦?要是我们换了一个日志记录工具怎么办?呵呵,别吓到了,没这么严重啦,这些都是极端情况,平时我们还是很开心地写着这些东西。但是今天要说到的这个aop可以将一些特别的操作提取出来,作为我们的"通知-advice",在运行时加载到对象中。就是说,我们可以在代码中不写日志记录段,但是在运行时加载一些"通知"到需要记录日志的"切入点-pointcut"动态地加入日志记录功能。
Read more...
by agate - Published: 2007-12-06 [9:45 下午] - Category: 程序编码
一直以来我们总是使用new的方式来创建对象或对象层级之间的关系
ClassOne one = new ClassOneImpl();
用 UML 图示很明确的告诉我们一个引用只是一个箭头,那我们平时那种new的方式建立关系的方法让我们在以后的代码修改还是添加功能的时候会造成大量的代码修改,比如我们把上面这个 ClassOneImpl 实现更换成新的一个实现 ClassOneImpl2 那么我们就要修改所有代码中 ClassOneImpl 的部分……如果这个工程很大的话,这个可是件可怕的修改工程啊!
IOC,即 Inversion of control 中文意思为"控制反转",个人理解即为把过去在代码中 new 的方式在编译时绑定关系进而在运行时使用的对象关系方式,转化成在运行时加载的方式,即所谓的"反转"。
现在我用一个十分简单的例子实现IOC的HelloWorld
Read more...
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.xml的dataSource问题。
将
<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所以会出现如题的问题。