前言
在来写这篇笔记之前,去看了一篇别人的美文。自从入行以来,已经记不得有多久没有去看真正的文学作品了。甚至曾经一度以为那些能引起我们心灵共鸣的文章对我们的生活并没有什么作用。毕业以来,我也从未放弃看书,但看的书却更具功利性,比如《XXX从入门到精通》、《XXX指南》一类的书。貌似这类书更具价值,因为它能变现。不管出于什么样的目的,现在真正能静下心来欣赏文学作品的人越来越少。功名利禄,灯红酒绿更能吸引人心。但偶尔静下心来,与心灵对话,也会让我们这种无产阶级短暂的放下压力,产生出幸福感。也许风雨后的彩虹来得太晚,但明天的路还长,我不会在今天就倒在路上。
继续我们的成长之旅,这个标题已经是老生常谈,这些技能也几乎是所有java程序员的基本技能。关于这三个框架的整合的教程已经很多了。但是比较系统的、或者说比较详细的并且能在容易入坑的地方着重强调的文章却不多。对于像我这种初学者来说,是越详细越好,最好能在坑多的地方多做提醒。
我在写这篇笔记的时候会尽可能详细的介绍这三个框架的整合过程,并在我掉坑的地方着重强调,一方面让我记忆更深,另一方面希望对后来者有所帮助。我也是初学者,如有不妥,也希望能留言指点,先行谢过。
使用Struts框架搭建Web项目
1、首先下载好Struts库(点击下载),在官方下载页下载struts-2.5.14.1-all.zip。
2、创建Web Project:打开Eclipse => File => New => Web Project。
3、在 ... => struts-2.5.14.1-all.zip => struts-2.5.14.1 => lib目录下将如下图的这些库(也可下载struts-2.5.14.1-min.zip,其中的库是搭建Struts项目必须要导入的库)拷贝到项目的WebRoot => WEB-INF => lib目录下,并添加到build path。
4、在WEB-INF下创建web.xml文件。
5、在web.xml文件中配置struts拦截器(注意拦截器不要配错了,新版本的拦截器和旧版本拦截器不一样,具体的可以看struts-core.jar下面是否有响应的java类):
MyWeb
login.jsp
struts2
org.apache.struts2.dispatcher.FilterDispatcher
struts2
*.action
6、在src目录下创建struts配置文件struts.xml (记住是src目录,不是WEB-INF目录,如果要放在WEB-INF目录下,请在WEB-INF目录下创建classes目录,并将struts.xml放在classes目录下。这样做的原因:在编译的时候会将src下的文件拷贝到classes目录下,WEB-INF/classes目录下的文件也都全部会被拷到编译路径的classes目录下,而WEB-INF下的文件不会被拷贝到classes目录下,在程序运行的时候默认是在classes目录下查找web.xml和struts.xml)。
7、在com.xxx.action下创建LoginAction类:
package com.xxx.action;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport{
private String userName;
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public String execute() throws Exception {
if ("test".equals(getUserName()) && "123456".equals(getPassword())) {
return SUCCESS;
}
return ERROR;
}
}
8、在WEB-INF/content目录下创建登录成功和失败的显示页success.jsp、error.jsp:
success.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
My JSP 'success.jsp' starting page
登录成功!
error.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
My JSP 'error.jsp' starting page
账户名或密码错误
9、打开刚才创建好的struts.xml文件,开始配置:
/WEB-INF/content/success.jsp
/WEB-INF/content/error.jsp
说明:package的name属性是自己取的,用于管理action,必须要继承extends="struts-default"。
特别注意:如果你使用的是旧版本的拦截器,那么在配置的时候action的name属性login.action要写全(因为后面请求的时候用的就是这个),如果你使用的是新版本的拦截器,那么只需要将action的name属性配置为login就可以了(请求时的action name还是login.action)
10、创建登录页,在WebRoot目录下创建login.jsp(因为在web.xml文件在设置的welcome-file是login.jsp因此输入网址会直接导航到这个界面),login.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
My JSP 'login.jsp' starting page
说明:form的action属性要和struts.xml文件中配置是action的name属性一致(旧版本,如果是新版本,在struts配置的时候如果是以.cation结尾,那么去掉.action),input的name属性对应LoginAction中的userName属性和password属性,且名字必须保持一致。
到此为之,struts项目就搭建完成了,可以先运行下项目,测试运行过程是否正常,如果有问题,在下一步之前,请确保已经解决。
Spring整合Struts
首先,下载spring,下载地址http://repo.springsource.org/libs-release-local/org/springframework/spring/。根据需要下载相应的版本,我下载的是5.0.1版。
下载下来后,解压并找到libs文件夹,将libs下面的所有jar文件拷贝到项目的WebRoot/WEB-INF/libs目录下,并且add to build path。
修改web.xml文件如下:
MyWeb
login.jsp
org.springframework.web.context.ContextLoaderListener
struts2
org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
struts2
*.action
ContextLoaderListener这个监听器负责查找spring配置文件并启动spring。context-param用于配置spring配置文件的路径,如果不配置,则默认在WEB-INF下找applicationContext.xml文件。
然后在WEB-INF/目录下(请根据自己在context-param中配置的参数进行修改,路径不对,将找不到spring配置文件),创建applicationContext.xml文件。
创建LoginService接口:
package com.xxx.service;
public interface LoginService {
String doLogin(String userName,String password);
}
创建LoginServiceImpl类:
package com.xxx.service;
import com.opensymphony.xwork2.ActionSupport;
public class LoginServiceImpl implements LoginService{
private String userName = "xxx";
private String password = "123456";
@Override
public String doLogin(String userName, String password) {
if (this.userName.equals(userName) && this.password.equals(password)) {
return ActionSupport.SUCCESS;
}
return ActionSupport.ERROR;
}
}
修改LoginAction类:
package com.xxx.action;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.opensymphony.xwork2.ActionSupport;
import com.xxx.service.LoginService;
public class LoginAction extends ActionSupport{
private String userName;
private String password;
private LoginService loginService;
public LoginService getLoginService() {
return loginService;
}
public void setLoginService(LoginService loginService) {
this.loginService = loginService;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public String execute() throws Exception {
return loginService.doLogin(userName, password);
}
}
最终的项目结构:
现在开始配置spring:在applicationContext中配置bean
修改struts配置文件:
/WEB-INF/content/success.jsp
/WEB-INF/content/error.jsp
主要的变化是:login这个action的class属性改成了在applicationContext中的id。这样action的实例的创建将交由spring来管理。
到这里就结束了吗?当然不是,我们忘了一件重要的事。在整合的过程中还要加入一个重要的库struts2-spring-plugin-2.5.14.1.jar,加入方法还是拷贝到WEB-INF/lib下,并加入到build path(注意:这个库的版本一定要和struts的版本一致,否则可能出错)。好了,到这里就可以运行程序了。如果在登录页面输入用户名xxx密码123456能成功实现“登录”功能,那么整合就基本实现了。
在这里再补充一点,如果按照刚才的配置,应该会出现一个ERRO,意思是说没有导入log4j。log4j这个包负责打印日志,对调试程序很有帮助。导入这个包也比较简单,首先导入log4j的jar包:
log4j的包自己网上找一下,这里不多说。
然后在src下创建log4j2.xml文件,用于配置log4j:
重启应用,如果那个ERRO消失了,那么log4j就配置成功了,如果还是有那个错误,就要根据错误提示,检查错误可能是什么原因引起的。
整合Hibernate
到这里真不容易,已经过半了,再坚持一下。该项目已经整合了spring+struts,现在将hibernate也整合进来就大功告成!
首先还是下载hibernate,下载地址(http://hibernate.org/orm/releases/5.2/),点击最右侧的Download zip archive下载。
解压后将...\hibernate-release-5.2.12.Final\hibernate-release-5.2.12.Final\lib\required目录下的全部jar包拷贝到WEB-INF/lib目录下,并且都添加到build path中。
将...\hibernate-release-5.2.12.Final\hibernate-release-5.2.12.Final\lib\optional\c3p0目录下的所有jar文件拷贝到WEB-INF/lib下,并添加都build path。
在这里,不要忘了还有两个需要导入的jar包。一个是AspectJ包,下载地址:https://www.eclipse.org/aspectj/downloads.php。Spring的AOP需要依赖这个包。另外一个是MySql的驱动JDBC Driver for MySQL ,下载地址:https://www.mysql.com/products/connector/。
安装MySql,此处不做相信说明,自行百度。
在MySql的test数据库下创建tb_employee表,通过下面的sql创建
CREATE TABLE `tb_employee` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`firstName` varchar(32) NOT NULL DEFAULT '',
`lastName` varchar(32) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=105 DEFAULT CHARSET=utf8
创建com.xxx.dao包,创建BaseDao接口:
package com.xxx.dao;
import java.io.Serializable;
import java.util.List;
public interface BaseDao {
/**
* 根据实体加载数据
* @param entityClazz
* @param id
* @return
*/
T get(Class entityClazz,Serializable id);
/**
* 保存实体
* @param entity
* @return
*/
Serializable save(T entity);
/**
* 更新实体
* @param entity
*/
void update(T entity);
/**
* 删除实体
* @param entity
*/
void delete(T entity);
/**
* 根据ID删除实体
* @param entityClazz
* @param id
*/
void delete(Class entityClazz,Serializable id);
/**
* 获取所有实体
* @param entityClazz
* @return
*/
List findAll(Class entityClazz);
/**
* 获取实体总数
* @param entityClazz
* @return
*/
long findCount(Class entityClazz);
}
创建BaseDaoHibernate3
package com.xxx.dao;
import java.io.Serializable;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
public class BaseDaoHibernate3 implements BaseDao {
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public T get(Class entityClass, Serializable id) {
return getSessionFactory().getCurrentSession().get(entityClass, id);
}
@Override
public Serializable save(T entity) {
SessionFactory sf = getSessionFactory();
@SuppressWarnings("unused")
Session s = sf.getCurrentSession();
return getSessionFactory().getCurrentSession().save(entity);
}
@Override
public void update(T entity) {
getSessionFactory().getCurrentSession().update(entity);
}
@Override
public void delete(T entity) {
getSessionFactory().getCurrentSession().delete(entity);
}
@Override
public void delete(Class entityClass, Serializable id) {
getSessionFactory().getCurrentSession()
.createQuery("delet " + entityClass.getSimpleName() + " en where en.id=?0").setParameter(0, id)
.executeUpdate();
}
@Override
public List findAll(Class entityClass) {
return (List) find("select en from " + entityClass.getSimpleName() + " en");
}
@SuppressWarnings("unchecked")
@Override
public long findCount(Class entityClass) {
List list = (List) find("select count(*) from " + entityClass.getSimpleName() + " en");
if (list != null && list.size() == 1) {
return (long) list.get(0);
}
return 0;
}
/**
* 分页查询
*
* @param hql
* @param pageNo
* @param pageSize
* @return
*/
@SuppressWarnings("unchecked")
protected List findBypage(String hql, final int pageNo, final int pageSize) {
List list = getSessionFactory().getCurrentSession().createQuery(hql).setFirstResult((pageNo - 1) * pageSize)
.setMaxResults(pageSize).list();
return list;
}
/**
* 带占位符的分页查询
*
* @param hql
* @param pageNo
* @param pageSize
* @param params
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected List findBypage(String hql, int pageNo, int pageSize, Object... params) {
Query query = getSessionFactory().getCurrentSession().createQuery(hql);
for (int i = 0; i < params.length; i++) {
query.setParameter(i, params[i]);
}
return query.setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).list();
}
@SuppressWarnings("unchecked")
protected List find(String hql) {
return getSessionFactory().getCurrentSession().createQuery(hql).list();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected List find(String hql, Object... paramas) {
Query query = getSessionFactory().getCurrentSession().createQuery(hql);
for (int i = 0; i < paramas.length; i++) {
query.setParameter(i, paramas[i]);
}
return query.list();
}
}
创建BaseDaoHibernate4
package com.xxx.dao;
import java.io.Serializable;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.query.Query;
import org.springframework.orm.hibernate5.HibernateCallback;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
public class BaseDaoHibernate4 extends HibernateDaoSupport implements BaseDao{
@Override
public T get(Class entityClass, Serializable id) {
return getHibernateTemplate().get(entityClass, id);
}
@Override
public Serializable save(T entity) {
return getHibernateTemplate().save(entity);
}
@Override
public void update(T entity) {
getHibernateTemplate().update(entity);
}
@Override
public void delete(T entity) {
getHibernateTemplate().delete(entity);
}
@Override
public void delete(Class entityClass, Serializable id) {
getHibernateTemplate().delete(get(entityClass, id));
}
@SuppressWarnings("unchecked")
@Override
public List findAll(Class entityClass) {
return (List) getHibernateTemplate().find("select en from " + entityClass.getSimpleName() + " en");
}
@SuppressWarnings("unchecked")
@Override
public long findCount(Class entityClass) {
List list = (List) getHibernateTemplate().find("select count(*) from " + entityClass.getSimpleName()+" en");
if (list != null && list.size() == 1) {
return (long) list.get(0);
}
return 0;
}
/**
* 分页查询
*
* @param hql
* @param pageNo
* @param pageSize
* @return
*/
@SuppressWarnings("unchecked")
protected List findBypage(final String hql,final int pageNo,final int pageSize) {
List list = getHibernateTemplate().execute(new HibernateCallback>() {
@Override
public List doInHibernate(Session session) throws HibernateException {
List result = session.createQuery(hql).setFirstResult((pageNo - 1) * pageSize)
.setMaxResults(pageSize).list();
return result;
}
});
return list;
}
/**
* 带占位符的分页查询
*
* @param hql
* @param pageNo
* @param pageSize
* @param params
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected List findBypage(final String hql, final int pageNo, final int pageSize, final Object... params) {
List list = getHibernateTemplate().execute(new HibernateCallback>() {
@Override
public List doInHibernate(Session session) throws HibernateException {
Query query = session.createQuery(hql).setFirstResult((pageNo - 1)*pageSize)
.setMaxResults(pageSize);
for (int i = 0; i < params.length; i++) {
query.setParameter(i, params[i]);
}
List result = query.list();
return result;
}
});
return list;
}
}
创建实体类Employee
package com.xxx.dao;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "tb_employee")
public class Employee {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Column(name = "firstName")
private String firstName;
@Column(name = "lastName")
private String lastName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
emsp;创建EmployeeDao
package com.xxx.dao;
public interface EmployeeDao extends BaseDao{
}
这个接口看似没用,其实暗藏玄机。如果EmployeeDao 有它特定的业务时,可以写在这个接口中,一方面满足面向接口编程的原则,一方面又不污染BaseDao接口。
创建EmployeeDaoHibernate
package com.xxx.dao;
public class EmployeeDaoHibernate extends BaseDaoHibernate3 implements EmployeeDao{
}
项目结构截图:
BaseDaoHibernate3和BaseDaoHibernate4是两种不同是实现,更推荐前者,因为前者的代码污染更小。
现在来添加一个Action:EmployeeAction。用于响应添加Employee请求。
package com.xxx.action;
import org.springframework.beans.factory.annotation.Autowired;
import com.opensymphony.xwork2.ActionSupport;
import com.xxx.dao.Employee;
import com.xxx.service.EmployeeService;
public class EmployeeAction extends ActionSupport {
@Autowired
private EmployeeService employeeService;
private String firstName, lastName;
private Employee employee;
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public EmployeeService getEmployeeService() {
return employeeService;
}
public void setEmployeeService(EmployeeService employeeService) {
this.employeeService = employeeService;
}
/**
*
*/
private static final long serialVersionUID = -8442995807620521709L;
@Override
public String execute() throws Exception {
Employee employee = new Employee();
// System.out.println(firstName+lastName);
employee.setFirstName(firstName);
employee.setLastName(lastName);
employeeService.add(employee);
// System.out.println(id);
return super.execute();
}
}
这个action中真正进行数据存储的是EmployeeService,于是接着创建EmployeeService接口和实现类,实现添加和获取两个方法:
EmployeeService
package com.xxx.service;
import java.io.Serializable;
import com.xxx.dao.Employee;
public interface EmployeeService {
Serializable add(Employee employee);
Employee get(Serializable id);
}
EmployeeServiceImpl
package com.xxx.service;
import java.io.Serializable;
import com.xxx.dao.Employee;
import com.xxx.dao.EmployeeDao;
public class EmployeeServiceImpl implements EmployeeService {
private EmployeeDao employeeDao;
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
public Serializable add(Employee employee) {
return employeeDao.save(employee);
}
public Employee get(Serializable id) {
return employeeDao.get(Employee.class, id);
}
}
好了,代码已写完,下面就来配置。
首先配置applicationContext:
com.xxx.dao.Employee
org.hibernate.dialect.MySQL5InnoDBDialect
update
true
true
配置struts.xml文件:
/WEB-INF/content/success.jsp
/WEB-INF/content/error.jsp
/WEB-INF/content/success.jsp
/WEB-INF/content/error.jsp
在login.jsp中添加一个表单用于测试hibernate整合是否成功:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
My JSP 'login.jsp' starting page
到这里就算整合成功了,最后的项目结构如图:
启动服务器,输入firstName、lastName,点击add会跳转到成功页面,打开MySQL Workbench查看tb_employee表的记录可以看到已添加的数据:
最后附上项目下载地址: https://github.com/xudefei/WebTest