开发者突击的配套Struts2SpringHibernate集成项目学习笔记1

学习开发者突击的配套Struts2SpringHibernate集成项目,写下自己的心得和值得注意的地方
一、首先我们查看login.jsp中的代码
(一)对于
<s:text name="login.page.title" />
我们自然按照步骤去找国际化文件,这里是找全局国际化文件,而我们查找struts.xml发现没有struts.custom.i18n.resources这个属性,而我们注意到还有一个struts.properties,为什么要有struts.properties,这个是以键值的形式存储数据,它相对于struts.xml更加直观,而加载这两个的顺序为先加载 struts.xml,再加载 struts.properties。也就是说 struts.properties 是可以覆盖 struts.xml里面的 配置的,而struts.properties中的属性详见
http://blog.163.com/anly_liu/blog/static/42832314200792672853251/
(二)
<s:fielderror><s:param>username</s:param></s:fielderror> 
fielderror标签输出action的fieldErrors属性保存的字段错误,fieldErrors是一个map类型的属性
(三)
<a href="register!init.do">
,这里注意感叹号!的用法是动态调用,不用我们在配置文件中配置,就可直接调用方法。
<action name="login" class="com.pj.action.LoginAction">
<result type="json"></result>
</action>  

当请求/login!query.action时,将调用LoginAction的query()方法,当请求/login!save.action时,将调用LoginAction的save()方法
。详见 http://hi.baidu.com/dd_taiyangxue/item/e4a385be6e09d8a2eaba935d
我们查看RegistrationAction,
public String init() throws Exception {
		setUsername(null);
		setPassword1(null);
		setPassword2(null);
		setEmail(null);
		return Action.INPUT;
	}
而Action.INPUT 为字符串"input"
二、进入注册页面注册
(一)进入注册页面注册,然后提交给RegistrationAction的register方法。注意getUserDAO().isExist(username),这个得到的UserDAO实例是继承自HibernateDaoSupport,并且调用getHibernateTemplate得到HibernateTemplate方法。在此我们需要理解HibernateTemplate的使用。这个HibernateTemplate就类似于Hibernate中的Session,如果我们不使用HibernateTemplate,那么打开Session,开始一个事务,处理异常,提交一个事务,最后关闭一个Session,而HibernateTemplate 对Hibernate操作进行封装,我们只要简单的调用用HibernateTemplate 对象,传入hql和参数,就获得查询接口,至于事务的开启,关闭,都交给HibernateTemplate  对象来处理,我们自己只专注于业务。HibernateTemplate的常用用法通过它自己的delete(Object entity),find(String queryString),save(Object entity)等常用用法完成大多数DAO对象的增,删,改,查等操作。复杂的用法通过
Object execute(HibernateCallback action)

List execute(HibernateCallback action);
这两个方法都需要一个
HibernateCallback实例,HibernateCallback是一个接口,只有一个方法doInHibernate(org.hibernate.Session.session),一般
程序采用HibernateCallback的匿名内部类来获取HibernateCallback实例。如
List list = (List) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException {
				List result = session.createCriteria(User.class).add(
						Restrictions.eq("username", username)).list();
				return result;
			}
		});

关于HibernateTemplate的用法详见 http://wenku.baidu.com/view/2834a82b2af90242a895e544.html
(二)注意createCriteria是QBC查询方式,使用Restrictions相关方法设置查询条件,使用工具类Order的相关方法设置排序方式。
(三)注意
super.getText("register.message.failed")
的用法,查找国际化文件中register.message.failed。
(四)我们查看数据库连接的代码
1.这里的数据库连接不是使用hibernate的配置,而是把数据库连接的配置放在了Spring的配置中。也就是说我们在Spring中配置
数据库的连接,如:
<bean id="dataSource"
		class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName">
			<value>org.gjt.mm.mysql.Driver</value>
		</property>
		<property name="url">
			<value>jdbc:mysql://localhost:3306/user?

useUnicode=true&amp;characterEncoding=utf-8</value>
		</property>
		<property name="username">
			<value>root</value>
		</property>
		<property name="password">
			<value></value>
		</property>
	</bean>

修改相应的驱动和数据库用户名和密码。点击注册,发现错误 org/hibernate/exception/DataException,查看相应的包,发现没有该类,引入包含该类的Hibernate3包。紧接着发生了错误Field 'id' doesn't have a default value。由于我的数据表中的id没有设置自增属性,所以设置indentity属性,但是MySQL5.0似乎不支持identity,使用auto_increment属性代替之。解决这个问题。如果我们要改变某个列的为identity,则不能简单alter,而要使用 set indentity_insert 表名 on 详见
http://www.cnblogs.com/zshsuming/archive/2010/05/10/1731907.html
2.配置SessionFactory
<!-- 配置Hibernate -->
	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource">
			<ref local="dataSource" />
		</property>
		<property name="mappingResources">
			<list>
				<value>com/demo/hibernate/beans/User.hbm.xml</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					org.hibernate.dialect.MySQLDialect
				</prop>
				<prop key="hibernate.show_sql">true</prop>
			</props>
		</property>
	</bean>

并且注意其中mappingResources属性配置其映射文件。
3.配置事物管理器
<bean id="transactionManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory">
			<ref local="sessionFactory" />
		</property>
	</bean>

(五)注册类所使用的验证方法。
这里我们注意几个正则表达式
1.邮箱
REG_email = "^[\\w\\d]+@[\\w\\d]+(\\.[\\w\\d]+)+$"
;^代表开头,\w表示字符,\d表示数字,[]表示其中的数字或是
字母,+表示前面数字或者字母出现一次或多次,@就是邮箱所需的@符,\.是匹配的 "." (因为"."在正则中匹配除换行符以外的任意字符),所以如果需要.在字符串中出现的话就加一个\ ,这个是转义符,$是字符串结尾
三、注册完毕,回到login.jsp页面
(一)发现刚刚注册的用户名显示在了用户名这一栏中,查找源代码,发现
<s:textfield name="username" />
并没有直接设置
value,这里就要注意name和value的关系:默认struts2的标签会将属性name对应变量的值设为value,也就是说在提交表单时,将name属性所在的标签的value属性值设置为对应javabean的相应属性值。关于name和value的关系见 http://struts.apache.org/2.3.4.1/docs/form-tags.html
(二)点击登录按钮,调用LoginAction的execute方法,我们查看源代码,发现了ActionContext这个类,这个ActionContext类时struts2为访问ServletAPI提供的工具类,同时也是struts2中的默认Action类,其静态方法getContext得到ActionContext实例,我们可以理解它类似于ServletContext的引用。进而实现对HttpServletRequest及HttpSession对象的访问。
四、该项目的web.xml中配置的过滤器和其他配置
(一)CharacterEncodingFilter
这个过滤器主要解决前台JSP页面和JAVA代码中使用了不同的字符集进行编码的时候就会出现表单提交的数据或者上传/下载中文名称文件出现乱码的问题。详见 http://blog.csdn.net/heidan2006/article/details/3075730
(二)com.opensymphony.module.sitemesh.filter.PageFilter
1.什么是sitemesh?它是由一个基于web页面布局、装饰以及与现存web应用整合的框架。它能帮助我们在由大量页面构成的项目中创建一致的页面布局和外观,使用sitemesh给我们带来的是不仅仅是页面结构问题,它的出现让我们有更多的时间去关注底层业务逻辑,而不是整个页面的风格和结构。它让我们摆脱了大量用include方式复用页面尴尬局面。
2.如何使用sitemesh,首先我们要引入相应的包,第二,在web.xml中配置这个过滤器
<filter>
		<filter-name>sitemesh</filter-name>
		<filter-class>
			com.opensymphony.module.sitemesh.filter.PageFilter
		</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>sitemesh</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

第三、在decorators.xml中进行配置选定装饰器所在目录名。装饰器名字及装饰器页面,该装饰器装饰pattern指定的页面。
<decorators defaultdir="/decorators">
<decorator name="frame" page="frame.jsp">
        <pattern>/*</pattern>
    </decorator>
</decorators>

第四,根据配置文件中指定的目录名,建立相应的目录,放置我们的装饰器。第五、在装饰器中编写代码
<%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %>
<%@ taglib uri="http://www.opensymphony.com/sitemesh/page" prefix="page"%>

注意几个用法
<decorator:title default="welcome to test sitemesh!" />
:读取被装饰页面的标题,并给出了默认标题。
<decorator:head />
:读取被装饰页面的<head>中的内容;
<page:applydecorator page="/menu.jsp" name="panel" />
:用panel指定的装饰器装饰menu.jsp页面,并把结果插入到这个page标签的位置;
<decorator:body />
:读取被装饰页面的<body>中的内容;
3.sitemesh运行原理,首先客户端发出一个请求,如果这个URL需要装饰器装饰,那么服务器就先解释被请求的资源,然后根据配置文件指定的目录名和装饰器名获得该装饰器,最后用装饰器装饰被请求的资源,将结果一同返回给客户端。可参考

http://hi.baidu.com/jlh_jianglihua/item/04ae63c19999af40a8ba94a1

http://wenku.baidu.com/view/a630e4c48bd63186bcebbcf2.html
(三)org.apache.struts2.dispatcher.ActionContextCleanUp
1.用途
引用
Special filter designed to work with the FilterDispatcher and allow
for easier integration with SiteMesh. Normally, ordering your filters to have
SiteMesh go first, and then FilterDispatcher go second is perfectly fine.
However, sometimes you may wish to access Struts features, including the
value stack, from within your SiteMesh decorators. Because FilterDispatcher
cleans up the ActionContext, your decorator won't have access to the
data you want.By adding this filter, the FilterDispatcher will know to not clean up and
instead defer cleanup to this filter. The ordering of the filters should then be:
1.this filter
2.SiteMesh filter
3.FilterDispatcher

就是说,一般情况下,如果你要用SiteMesh或者其他过滤器,一般是放在FilterDispatcher或者是现在的StrutsPrepareAndExecuteFilter之前。在调用完所有过滤器的doFilter方法后,核心过滤器FilterDispatcher或者StrutsPrepareAndExecuteFilter会清空ActionContext,如果其他过滤器要一直使用value stack等struts的特性时,如果不用ActionContextCleanUp的话,便得不到想要的值。ActionContextCleanUp的作用,它会在doFilter方法里设置一个计数器counter的初始值为1,有了这个值,后续的核心过滤器就不会清空ActionContext,而是由之前的过滤器也就是ActionContextCleanUp来清空ActionContext。参考 http://changli269.iteye.com/blog/928190http://wenku.baidu.com/view/a630e4c48bd63186bcebbcf2.html
(四)org.apache.struts2.dispatcher.FilterDispatcher
1.这个过滤器是struts2的过滤器,其作用有执行Actions,清除ActionContext,维护静态内容,清除request生命周期内的xwork的interceptors,详见 http://blog.csdn.net/dy_paradise/article/details/5996748
(五)jsp-config
1
<jsp-config>
		<taglib>
			<taglib-uri>sitemesh-page</taglib-uri>
			<taglib-location>
				/WEB-INF/sitemesh-page.tld
			</taglib-location>
		</taglib>

		<taglib>
			<taglib-uri>sitemesh-decorator</taglib-uri>
			<taglib-location>
				/WEB-INF/sitemesh-decorator.tld
			</taglib-location>
		</taglib>
	</jsp-config>

taglib用于在jsp页面引用标签库描述符.<taglib-uri>指映射路径,<taglib-location>指tld文件(标签描述文件)实际路径,而<taglib-uri>指定的内容我们可以在<%@taglib uri=""%>中引用。
比如
<taglib>
<taglib-uri>/tags/struts-nested </taglib-uri>
<taglib-location>/WEB-INF/struts-nested.tld </taglib-location>
</taglib> 

然后
<%@ taglib uri="/tags/struts-nested" prefix="nested"%>

2./struts-tags 是struts2标签库描述符默认的URI,建议使用这个名称,而我们为什么没有在web.xml中写下对应的<taglib>呢?原因为struts2的taglib已经写在jar里面了。只要在页头加上
<%@taglib uri= "/struts-tags " preix= "s " %>
就可以使用它的taglib了。
3.为什么要使用这种方式引用标签库描述符,直接在页面中通过<%@taglib uri ="标签库描述符的绝对路径"%>引用标签库不就可以了嘛? 这主要是因为耦合度,如果标签库需要更改,那么我们就要更改所有引入该标签库的页面,如果我们使用web.xml配置方式,那么只需要更改配置文件这一处即可,详见 http://zhidao.baidu.com/question/308209512.html
4.为什么需要标签库描述符? JSP标签库需要一个标签库描述符(TLD)文件来自定义标签的命名,它们的属性,以及操作该标签的Java类,详见 http://blog.sina.com.cn/s/blog_4f847d0801008lcb.html
5.标签库描述符和标签库的区别是什么,我的理解是这样的,标签库是一个宽泛的概念,或者我们理解它为一种通过JavaBean生成基于XML的脚本的方法。在这个方法中,我们要定义一个javabean,创建标签处理器,创建标签描述符,用于描述自定义标签以让系统知道如何处理。更新web xml文件。从而使用新的标签。
(六)
<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/applicationContext.xml</param-value>
	</context-param>
	<listener>
		<listener-class>
			org.springframework.web.context.ContextLoaderListener
		</listener-class>
	</listener>

1.ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。
而context-param表示加载配置,如果在web.xml中配置了这个属性,则会到相应的param-value所给出的值中加载配置文件,如果仅给出listener而没有给出默认会去/WEB-INF/下加载applicationContext.xml。注意这个配置项是融合spring和struts必不可少的一项。
2.由此可见applicationContext.xml的文件位置就可以有两种实现:
方法一我们可以直接将配置文件放到/WEB-INF下,之在web.xml中声明一个listener、
方法二将之放到classpath下,但是此时要在web.xml中加入<context-param>,用来给出配置文件的具体位置。
详见 http://blog.csdn.net/seng3018/article/details/6758860
五、该项目中application.xml中的配置解析
(一)数据库所需配置的dataSource数据源和sessionFactory上面已经配置过了。这里不再赘述
(二)Struts的配置
<!-- 定义Struts配置 -->
	<bean name="loginAction" class="com.demo.struts2.actions.LoginAction">
		<property name="userDAO">
			<ref local="userDAO" />
		</property>
	</bean>
	<bean name="logoutAction" class="com.demo.struts2.actions.LogoutAction">
		<property name="userDAO">
			<ref local="userDAO" />
		</property>
	</bean>
	<bean name="registerAction" class="com.demo.struts2.actions.RegisterAction">
		<property name="userDAO">
			<ref local="userDAO" />
		</property>
	</bean>

这里我们要理解一下单独使用struts时其action的实例化过程。我们发现,其是使用com.opensymphony.xwork2.ObjectFactory中的方法buildAction(),而如果spring和struts相结合,就使用com.opensymphony.xwork2.spring.SpringObjectFactory。具体
可参考 http://blog.csdn.net/wmj2003/article/details/5018927
在application.xml中配置完成后就在struts.xml中配置,如
<action name="login" class="loginAction">
			<result name="success">welcome.jsp</result>
			<result name="input">login.jsp</result>
		</action>
注意这里的class所引用的并不是完整的类名,而是在application.xml中配置的bean的name。
(三)配置自己的拦截器
<bean name="logger" class="com.demo.spring.aop.LoggingInterceptor" />

1.我们知道spring提供了几种很实用的通知,前置通知,后置通知,环绕通知,异常通知,都是方法级别的,对于这些advice的实现,spring提供了基于xml,基于annotation的实现,此外,我们还应该知道有基于接口的实现,这里就是使用基于接口的实现。
public class LoggingInterceptor implements MethodBeforeAdvice {

	public void before(Method method, Object[] objects, Object o)
			throws Throwable {
		System.out.println("logging before: " + o.getClass());
	}
}

(四)拦截器代理org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator的使用,这个是spring为我们提供的根据beanName匹配后进行自动代理的解决方法,
<bean name="loggingAutoProxy"
		class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="beanNames">
			<list>
				<value>/login</value>
				<value>/register</value>
			</list>
		</property>
		<property name="interceptorNames">
			<list>
				<value>logger</value>
			</list>
		</property>
	</bean>

这个配置意味着/login和/register的beanName,都被spring自动代理,并执行logger的切面通知。这个logger就是我们自己的拦截器。

你可能感兴趣的:(spring,Hibernate,struts)