SSH
SSH 整合应用
重构云笔记项目, 将Spring MVC 替换为 Struts2, 将MyBatis替换为Hibernate.
1. 创建项目 搭建 SSH 架构
创建项目, 导入SSH包:
<dependency><groupId>org.apache.struts</groupId><artifactId>struts2-core</artifactId><version>2.3.8</version> </dependency><dependency><groupId>org.apache.struts</groupId><artifactId>struts2-spring-plugin</artifactId><version>2.3.8</version> </dependency><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version> </dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>3.6.9.Final</version> </dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version> </dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version> </dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>3.0.5.RELEASE</version> </dependency><dependency><groupId>commons-dbcp</groupId><artifactId>commons-dbcp</artifactId><version>1.4</version> </dependency> <dependency><groupId>org.apache.struts</groupId><artifactId>struts2-json-plugin</artifactId><version>2.3.8</version> </dependency><dependency><groupId>aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.5.3</version> </dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.10</version> </dependency>
还需要导入目标运行环境为: Tomcat Runtime
添加配置 Struts2 和 Spring 的配置 web.xml
<filter><display-name>StrutsPrepareAndExecuteFilter</display-name><filter-name>StrutsPrepareAndExecuteFilter</filter-name><filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class></filter><filter-mapping><filter-name>StrutsPrepareAndExecuteFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:conf/spring-*.xml</param-value></context-param>
添加Struts2 配置文件 struts.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN""http://struts.apache.org/dtds/struts-2.3.dtd"> <struts></struts>
添加Spring配置文件:
spring-aop.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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:util="http://www.springframework.org/schema/util"xmlns:jpa="http://www.springframework.org/schema/data/jpa"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsdhttp://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"><!-- 配置组件扫描 --><context:component-scan base-package="cn.tedu.note.aop"/><!-- 使 @Aspect 注解生效 --><aop:aspectj-autoproxy/> </beans>
spring-service.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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:util="http://www.springframework.org/schema/util"xmlns:jpa="http://www.springframework.org/schema/data/jpa"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsdhttp://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"><context:component-scan base-package="cn.tedu.note.service"/></beans>
添加Spring Hibernate 配置文件:
数据库连接参数文件: conf/jdbc.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/cloud_note user=root password=root maxActive=20 salt=\u4ECA\u5929\u4F60\u5403\u4E86\u5417? pageSize=4
spring-hbm.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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:util="http://www.springframework.org/schema/util"xmlns:jpa="http://www.springframework.org/schema/data/jpa"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsdhttp://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"><!-- 配置 spring-mybatis.xml --><!-- 读取配置文件 --><util:properties id="jdbc"location="classpath:conf/jdbc.properties"/> <!-- 配置数据库连接池 --><bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close"> <property name="driverClassName"value="#{jdbc.driver}"/><property name="url"value="#{jdbc.url}"/><property name="username"value="#{jdbc.user}"/><property name="password"value="#{jdbc.password}"/><property name="maxActive"value="#{jdbc.maxActive}"></property></bean><bean id="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><property name="dataSource"ref="dataSource"/> <property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop><prop key="hibernate.show_sql">true</prop><prop key="hibernate.format_sql">true</prop></props></property><property name="mappingLocations"><list><!-- <value>classpath:hbm/User.hbm.xml</value>--></list></property></bean><bean id="hibernateTemplate"class="org.springframework.orm.hibernate3.HibernateTemplate"><property name="sessionFactory"ref="sessionFactory"/> </bean><!-- spring-mybatis.xml --><bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"><property name="sessionFactory"ref="sessionFactory"/></bean><!-- 设置 注解驱动的事务管理 --><tx:annotation-driven transaction-manager="txManager"/></beans>
部署测试
...
2. 移植云笔记界面组件
3. 移植云笔业务层, 持久层和测试组件
测试组件的作用: 如果能够通过测试组件的回归测试, 则说明持久层和业务层重构成功!
重构 NoteDao, NotebookDao, 删除MyBatis注解 @Param
public interface NoteDao {List<Map<String,Object>> findNotesByNotebookId(String notebookId);Note findNoteById(String noteId);int updateNote(Note note);int addNote(Note note);List<Map<String, Object>> findDeleteNotesByUserId(String userId);int deleteNoteById(String noteId);int deleteNotes(String... ids);List<Map<String, Object>> findNotes(String userId, String notebookId, String statusId);
}public interface NotebookDao {List<Map<String, Object>>findNotebooksByUserId(String userId);int countNotebookById(String notebookId);List<Map<String, Object>>findNotebooksByPage(String userId,int start,int pageSize,String table);}
4. 实现User的映射
添加映射文件 hbm/User.hbm.xml:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping><class name="cn.tedu.note.entity.User"table="cn_user"><id name="id" column="cn_user_id"></id><property name="name" column="cn_user_name"type="string"/><property name="password" column="cn_user_password" /><property name="token" column="cn_user_token" /><property name="nick" column="cn_user_nick" /></class> </hibernate-mapping>
将实体User映射到表 cn_user
实现 UserDao
@Repository("userDao") public class UserDaoImpl implements UserDao {@Resourceprivate HibernateTemplate hibernateTemplate;public User findUserByName(String name) {//HQL Hibernate Query Language// sql:// select * from cn_note // where cn_user_name = #{name} // HQL:// from User // where name = ?String hql = "from User where name = ?";List<User> list=hibernateTemplate.find(hql, name);return list.isEmpty()?null:list.get(0); }public int addUser(User user) {Serializable id=hibernateTemplate.save(user);return id==null ? 0 : 1;}public User findUserById(String userId) {return hibernateTemplate.get(User.class, userId); }}
重构 spring-hbm.xml
配置hbm/User.hbm.xml
<property name="mappingLocations"><list><value>classpath:hbm/User.hbm.xml</value></list> </property>
打开组件扫描
<context:component-scan base-package="cn.tedu.note.dao"/>
重构 测试案例 基类 BaseTest
@Before public void initCtx() {ctx = new ClassPathXmlApplicationContext("conf/spring-hbm.xml","conf/spring-service.xml"); }
执行测试案例 UserDaoTest, 进行回归性测试:
...
5. 测试 Login 功能
由于Spring容器初始化期间会扫描创建全部的Bean, 并且注入全部属性, 如果不将dao接口全部实现就会造成Spring容器初始化异常, 故将dao接口全部空实现, 在具体方法的功能实现在日后逐一添加.
实现NoteDao
@Repository("noteDao") public class NoteDaoImpl implements NoteDao {public List<Map<String, Object>> findNotesByNotebookId(String notebookId) {// TODO Auto-generated method stubreturn null;}public Note findNoteById(String noteId) {// TODO Auto-generated method stubreturn null;}public int updateNote(Note note) {// TODO Auto-generated method stubreturn 0;}public int addNote(Note note) {// TODO Auto-generated method stubreturn 0;}public List<Map<String, Object>> findDeleteNotesByUserId(String userId) {// TODO Auto-generated method stubreturn null;}public int deleteNoteById(String noteId) {// TODO Auto-generated method stubreturn 0;}public int deleteNotes(String... ids) {// TODO Auto-generated method stubreturn 0;}public List<Map<String, Object>> findNotes(String userId, String notebookId, String statusId) {// TODO Auto-generated method stubreturn null;}}
实现NotebookDao
@Repository("notebookDao") public class NotebookDaoImpl implements NotebookDao {public List<Map<String, Object>> findNotebooksByUserId(String userId) {// TODO Auto-generated method stubreturn null;}public int countNotebookById(String notebookId) {// TODO Auto-generated method stubreturn 0;}public List<Map<String, Object>> findNotebooksByPage(String userId, int start, int pageSize, String table) {// TODO Auto-generated method stubreturn null;}}
实现StarsDao
@Repository("starsDao") public class StarsDaoImpl implements StarsDao {public Stars findStarsByUserId(String userId) {// TODO Auto-generated method stubreturn null;}public int insertStars(Stars stars) {// TODO Auto-generated method stubreturn 0;}public int updateStars(Stars stars) {// TODO Auto-generated method stubreturn 0;}}
测试 UserService 的 Login 方法
...
6. 实现Login功能的控制器
创建控制器的抽象父类, 封装控制器的公共属性和方法:
public abstract class AbstractAction extends ActionSupportimplements SessionAware,RequestAware, ApplicationAware{protected static final String JSON="json";protected Map<String, Object> request;protected Map<String, Object> session;protected Map<String, Object> application;//Json返回值protected JsonResult jsonResult;public JsonResult getJsonResult() {return jsonResult;}public void setJsonResult(JsonResult jsonResult) {this.jsonResult = jsonResult;}public void setSession(Map<String, Object> session) {this.session=session;}public void setRequest(Map<String, Object> request) {this.request=request;}public void setApplication(Map<String, Object> application) {this.application=application;}}
声明常量 JSON 用于配合JSON返回结果
创建UserAction
@Controller @Scope("prototype") public class UserAction extends AbstractAction{@Resourceprivate UserService userService; private String name;private String password;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String login(){User user = userService.login(name, password);session.put("loginUser", user);jsonResult=new JsonResult(user);return JSON;} }
其中 name, password 来时用户界面提交的参数, jsonResult 是JSON返回值.
添加配置文件 spring-struts.xml 初始化 Struts 控制器Bean
<?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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:util="http://www.springframework.org/schema/util"xmlns:jpa="http://www.springframework.org/schema/data/jpa"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsdhttp://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"><context:component-scan base-package="cn.tedu.note.action"/></beans>
在 struts.xml 中配置控制器
<!-- 修改请求扩展名 action改为do --> <constant name="struts.action.extension" value="do"></constant> <package name="user" namespace="/user"extends="json-default"><global-results><result name="json" type="json"><param name="root">jsonResult</param></result></global-results><action name="login" class="userAction"method="login"/></package>
测试:
http://localhost:8080/ssh4/user/login.do?name=demo&password=123456
在用户界面中整合测试登录功能.
7. 实现注册功能
重构控制器 UserAction, 添加控制器方法:
private String nick; private String confirm;public String getNick() {return nick; }public void setNick(String nick) {this.nick = nick; }public String getConfirm() {return confirm; }public void setConfirm(String confirm) {this.confirm = confirm; }public String regist(){User user = userService.regist(name, nick, password, confirm);jsonResult = new JsonResult(user);return JSON; }
其中扩展了两个表单参数 nick 和 conform
配置 struts.xml:
<action name="regist" class="userAction"method="regist"/>
测试
...
Struts 拦截器
Struts 提供了默认的异常拦截功能, 但是不适合将异常转换为JSON, 可以利用拦截器处理Struts2 异常, 转换为JSON消息.
Struts2 拦截器工作原理:
编写简单的拦截器
编写拦截器类:
@Component public class MyInterceptor implements Interceptor {public void destroy() {}public void init() {}public String intercept(ActionInvocation invocation) throws Exception {System.out.println("控制器之前");String val = invocation.invoke();System.out.println("控制器之后"); return val;}}
设置组件扫描 spring-struts.xml
<context:component-scan base-package="cn.tedu.note.web"/>
配置拦截器:
<interceptors><interceptor name="demo" class="myInterceptor"/><interceptor-stack name="demoStack"><interceptor-ref name="defaultStack"/><interceptor-ref name="demo"></interceptor-ref></interceptor-stack> </interceptors><action name="login" class="userAction"method="login"><interceptor-ref name="demoStack"></interceptor-ref> </action>
必须将用户拦截器demo和系统拦截器栈defaultStack组合为一个新的拦截器栈 demoStack, 这样才能保留系统提供的功能!!
- 测试: 在登录时候输出拦截器的内容
利用拦截器处理 异常
异常原因分析:
原理:
重构 AbstractAction 添加异常处理方法
//AbstractAction 中约定控制器处理异的方法 public String handleException(Exception e) {e.printStackTrace();jsonResult = new JsonResult(e);return JSON; }
这样全部的控制器都有异常处理方法了.
添加拦截器拦截异常:
@Component public class ExceptionInterceptor implements Interceptor {public void destroy() {}public void init() {}public String intercept(ActionInvocation invocation) throws Exception {//得到目标控制器 AbstractAction action = (AbstractAction)invocation.getAction();//调用目标控制器的方法String val = null;try {val = invocation.invoke();} catch (Exception e) {val = action.handleException(e);}if(val == null){throw new NullPointerException();}return val;}}
配置异常拦截器 struts.xml
<interceptors><interceptor name="execInte" class="exceptionInterceptor"/><interceptor-stack name="noteStack"><interceptor-ref name="defaultStack"/><interceptor-ref name="execInte"></interceptor-ref></interceptor-stack></interceptors><default-interceptor-ref name="noteStack"/>
配置 default-interceptor-ref 元素以后, 所有控制器就都被异常处理拦截器拦截了!
测试…