首先我要感谢下【北京-痕迹】同学在这个过程中的帮助,让我能及时完成这部分工作。也让产品有了一个新的版本,新的编码体验。接下来就说下基本情况和过程吧!
本分分为如下几个部分:
A, 原始版本介绍
B, 注解版本介绍
C, 注解过程疑难问题总结
开源地址:http://code.google.com/p/ostocy-jshop/ 完全开源
A, 原始版本是基于struts2+spring2.5+hibernate3+freemarker构件的。完全没有使用注解,所以在整个编码从dao到action的过程都需要在applicationContext.xml和多个struts.xml中配置(我为了区分业务,定义了多个struts2的配置文件)。
对于团队合作开发来说太多配置文件每次加入新方法都需要修改是很不好的,很容易造成svn的冲突。
B, 注解版本的构件也是基于原始版本的,但是使用了注解。具体使用了spring的注解给类进行依赖注入,用struts2的注解完成页面控制和拦截器设定。本来有想过想用spring mvc代替action层,但是这样会破坏了现有较为完好的逻辑和页面控制。所以放弃了。再者个人对spring mvc 不怎么熟悉。
这里来一个applicationContext.xml文件的内容吧然后我做一个小解释,大牛们可以略过了。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd" default-autowire="byName"> <aop:aspectj-autoproxy> </aop:aspectj-autoproxy> <context:component-scan base-package="com.jshop" /> <context:annotation-config /> <context:property-placeholder location="classpath*:*.properties" /> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${jdbc.driver}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="initialPoolSize" value="${pool.initialPoolSize}" /> <property name="minPoolSize" value="${pool.minPoolSize}" /> <property name="maxPoolSize" value="${pool.maxPoolSize}" /> <property name="maxIdleTime" value="${pool.maxIdleTime}" /> <property name="acquireIncrement" value="${pool.acquireIncrement}" /> <property name="checkoutTimeout" value="${pool.checkoutTimeout}" /> <property name="maxIdleTimeExcessConnections" value="${pool.maxIdleTimeExcessConnections}" /> </bean> <!-- 邮箱服务器配置 --> <bean id="javamailsenderimpl" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host" value="${email.host}"></property> <property name="defaultEncoding" value="${email.defaultEncoding}"></property> <property name="port" value="${email.port}"></property> <property name="username" value="${email.username}"></property> <property name="password" value="${email.password}"></property> <property name="javaMailProperties"> <props> <prop key="mail.smtp.auth">${email.auth}</prop> <prop key="mail.smtp.timeout">${email.timeout}</prop> </props> </property> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" destroy-method="close"> <property name="dataSource"> <ref bean="dataSource" /> </property> <property name="mappingDirectoryLocations"> <list> <value>classpath:com/jshop/entity</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> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref local="sessionFactory" /> </property> </bean> <aop:config> <aop:advisor pointcut="execution(* com.jshop.action.service.*Service.*(..))" advice-ref="txAdvice"/> </aop:config> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="get*" read-only="true"/> <tx:method name="query*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="load*" read-only="true"/> <tx:method name="*" rollback-for="Exception"/> </tx:attributes> </tx:advice> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> </bean> <!-- 配置异步线程执行器 --> <!-- 为异步线程执行器 进注入 --> <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <!-- 设置核心池子的大小 --> <property name="corePoolSize" value="10"></property> <!-- 设置最大池子的大小 --> <property name="maxPoolSize" value="30"></property> </bean> </beans>
从上到下,依次的内容解释为
<!--[if !supportLists]-->1, <!--[endif]-->default-autowire="byName" 要求spring按照name来依赖注入而不是类型。
<!--[if !supportLists]-->2, <!--[endif]-->context:component-scan base-package="com.jshop" 要求spring去扫描com.jshop下得所有类去解释注解。
<!--[if !supportLists]-->3, <!--[endif]-->context:property-placeholder location="classpath*:*.properties" 要求spring检索web-info下得文件,这里主要是给下面的数据库和邮箱信息提供了properties形式的保存而已。
<!--[if !supportLists]-->4, <!--[endif]--><value>classpath:com/jshop/entity</value> 要求spring去搜索这个目录下得所有数据库映射文件,以前都是一个一个写的,现在让他自己搜索去,省事点。
<!--[if !supportLists]-->5, <!--[endif]-->com.jshop.action.service.*Service.*(..))" advice-ref="txAdvice"/>这个就是事务管理了,在service进行事务的回滚
<!--[if !supportLists]-->6, <!--[endif]--> <bean id="taskExecutor" 是邮件发送,是用的线程发送。
接下来是摘录部分类的注解了
Dao层
@Repository("articleCategoryTDaoImpl") public class ArticleCategoryTDaoImpl extends HibernateDaoSupport implements ArticleCategoryTDao {
继承HibernateDaoSupport 就不用注入hibernateTemplate了
Service 层
@Service("articleCategoryTServiceImpl") @Scope("prototype") public class ArticleCategoryTServiceImpl implements ArticleCategoryTService { @Resource(name="articleCategoryTDaoImpl") private ArticleCategoryTDaoImpl articleCategoryTDaoImpl; public ArticleCategoryTDaoImpl getArticleCategoryTDaoImpl() { return articleCategoryTDaoImpl; }
Action层@ParentPackage("jshop")
@Controller("articleCategoryTAction") public class ArticleCategoryTAction extends ActionSupport { @Resource(name = "articleCategoryTServiceImpl") private ArticleCategoryTServiceImpl articleCategoryTServiceImpl;
接下来是struts.xml了
<!--开发状态 --> <constant name="struts.devMode" value="true" /> <!-- 配置文件重新加载 --> <constant name="struts.configuration.xml.reload" value="true" /> <!-- convention类从新加载 --> <constant name="struts.convention.classes.reload" value="true" /> <!-- 主题 --> <constant name="struts.ui.theme" value="simple" /> <constant name="struts.custom.i18n.resources" value="globalMessages" /> <constant name="struts.i18n.encodeing" value="UTF-8" /> <constant name="struts.convention.result.path" value="/" /> <package name="jshop" extends="json-default" > <interceptors> <interceptor name="json" class="org.apache.struts2.json.JSONInterceptor" /> <!-- 新增登录验证拦截器 --> <interceptor name="authoritylogin" class="com.jshop.action.interceptor.impl.AuthorityLogininterceptor"></interceptor> <!-- 定义全部变量拦截器!是否开启用户注册 --> <interceptor name="isusercanregister" class="com.jshop.action.interceptor.impl.MyIsusercanregisterInterceptor" /> <!-- 定义全局变量拦截器 是否允许用户注册多个商城信息 --> <interceptor name="canuserregistermoreshopinfo" class="com.jshop.action.interceptor.impl.CanuserregistermoreshopinfoInterceptor"></interceptor> <!-- 定义全局变量拦截器是否允许发送激活邮件 --> <interceptor name="issendactivatemail" class="com.jshop.action.interceptor.impl.Issendactivatemail" /> <interceptor-stack name="jshopdefaultStack"> <!-- 将登录验证拦截器加入默认的拦截器栈中 --> <interceptor-ref name="authoritylogin"> <param name="excludeMethods">adminlogin,uploadFiles,findAllCoupon,loginforAndroid,registerforAndroid</param> </interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="json"></interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="jshopdefaultStack" /> <global-results> <result name="login" type="redirect">/jshop/admin/jump.jsp</result> <result name="isusercanregister">/usercenter/warning/warningmsg.jsp</result> <result name="canuserregistermoreshopinfo">/jshop/admin/error/adminerror.jsp </result> </global-results> <!-- 验证码 --> <action name="randomchecknum" class="com.jshop.tools.RandomCheckNumAction"> <result type="stream"> <param name="contentType">image/jpeg</param> <param name="inputName">inputStream</param> </result> <interceptor-ref name="defaultStack"></interceptor-ref> </action> </package>
解释如下
1,<constant name="struts.convention.result.path" value="/" /> 这个很重要,告诉struts2我所有可以跳转的页面在项目根目录下。
2,<package name="jshop" extends="json-default" >这个package和action中的package要一致,因为这个影响到拦截器的作用域。
C,注解过程疑难问题总结
1, 注解默认的依赖注入对象是首字母小写的类名@Repository("articleCategoryTDaoImpl")对应的dao层的类就是ArticleCategoryTDaoImpl
2, Struts2配置文件中的拦截器作用域是和package有关的,所有action类都要统一package和struts.xml一致。
3, Struts2的注解中有namespace注解,这个注解是会影响页面跳转的,应该是url了的显示问题,我这里没有用。需要在研究下。
4, Struts2我继承了json-default,所以绝大部分action层的struts2的@Action没有显示的跳转页面,我都让前台的js做了页面控制。