一、建立web项目
之前想用myeclipse一次搞定,发现,那样做反而不利于理解,对于我这样的新手还是一步一步来的好。
计划是这样的,搭建web,添加struts,添加spring,数据库里面建立个user表,添加hibernate,首先把最基本的框架搭起来。
之后,又一点一点把框架丰富起来,添加role表,添加验证,ajax啥的。
搭建web不多说了,直接上图。
Context root URL里面,默认是和项目名一样,会发布到tomcat的:webapp\项目名\ 这个地方,如果是:/,如上图,会发布到tomcat的:webapp\ROOT\ 目录下。
myeclipse会默认添加一个index.jsp页面,只要servers运行起来能看到下面这个图,就说明基本的web就搭建好了。
二、加入基本的Struts
用myeclipse来添加struts,只是省了自己新建配置文件,导入包这步。其实自己做也不麻烦。
1、导入struts包。
struts最新版是2.3.7,下载地址http://struts.apache.org/download.cgi
下载下来的的压缩包解开,里面有个apps目录,里面是些.war文件。
其实war就是zip文件,重新命名下,就可以解压缩。把struts2-blank解压开来,能得到一个示例项目。既然人家都说这是blank了,就直接用这里面的包做最基础的包好了。
我不喜欢直接把包拷贝到lib目录下,宁可多建很多个Libraries包。
2、包导入好了,开始,建一个action。
先建立一个Package:demo.myssh.action,在下面建一个UserAction的类。
类的代码如下
- package demo.myssh.action;
- import com.opensymphony.xwork2.ActionSupport;
- @SuppressWarnings("serial")
- public class UserAction extends ActionSupport {
- @Override
- public String execute() throws Exception {
- this.addActionMessage("UserAction working");
- this.addActionMessage("hello world.");
- return ActionSupport.SUCCESS;
- }
- }
3、顺便改造下默认的index.jsp,用actionmessage来接收UserAction的信息,这样好测试Action是否起作用了。
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%@ taglib prefix="s" uri="/struts-tags" %>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <base href="<%=basePath%>">
- <title>My JSP 'index.jsp' starting page</title>
- </head>
- <body>
- This is my JSP page. <br>
- <s:actionmessage/>
- </body>
- </html>
4、接下来,修改web.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app
- version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
- <display-name>myssh</display-name>
- <filter>
- <filter-name>struts2</filter-name>
- <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>struts2</filter-name>
- <url-pattern>*.action</url-pattern>
- </filter-mapping>
- <welcome-file-list>
- <welcome-file>index.jsp</welcome-file>
- </welcome-file-list>
- </web-app>
5、在src目录下,添加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>
- <package name="mysshdemo" extends="struts-default">
- <action name="user" class="demo.myssh.action.UserAction">
- <result>/index.jsp</result>
- </action>
- </package>
- </struts>
这样,一个基本的struts就建立好了。访问http://localhost:8080/user.action,就能看到以下
三、在struts基础上加入spring
spring现在的版本是3.1.3,下载地址http://www.springsource.org/spring-framework#download
spring的包里面没有提供例子,额,这点不如struts。
1、导入包。
首先需要添加struts的spring插件:struts2-spring-plugin-2.3.7.jar
其次,spring还需要一个外部的jar:commons-logging-1.1.1.jar,下载地址http://commons.apache.org/logging/download_logging.cgi
在这里,spring只需要以下几个jar
2、新建packa:demo.myssh.business,添加UserService类。
- package demo.myssh.business;
- public class UserService {
- public String doing()
- {
- return "UserService working";
- }
- }
3、为了让UserService能够注入到UserAction,还需要为UserAction添加注入用的属性和方法
- package demo.myssh.action;
- import com.opensymphony.xwork2.ActionSupport;
- import demo.myssh.business.UserService;
- @SuppressWarnings("serial")
- public class UserAction extends ActionSupport {
- @Override
- public String execute() throws Exception {
- this.addActionMessage("UserAction working");
- // this.addActionMessage("hello world.");
- this.addActionMessage(userService.doing());// 修改下,确认注入成功。
- return ActionSupport.SUCCESS;
- }
- // 注入用属性
- private UserService userService;
- // 注入用的方法
- public void setUserService(UserService userService) {
- this.userService = userService;
- }
- }
4、在web.xml文件中加入spring的监听器
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
- <display-name>myssh</display-name>
- <filter>
- <filter-name>struts2</filter-name>
- <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>struts2</filter-name>
- <url-pattern>*.action</url-pattern>
- </filter-mapping>
- <!-- 加入spring的监听器 -->
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <welcome-file-list>
- <welcome-file>index.jsp</welcome-file>
- </welcome-file-list>
- </web-app>
5、修改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>
- <package name="mysshdemo" extends="struts-default">
- <action name="user" class="userAction">
- <result>/index.jsp</result>
- </action>
- </package>
- </struts>
6、在WEB-INF目录下新建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"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
- <bean id="userAction" class="demo.myssh.action.UserAction">
- <property name="userService" ref="userService" />
- </bean>
- <bean id="userService" class="demo.myssh.business.UserService"></bean>
- </beans>
这时候访问:http://localhost:8080/user.action
到此,基本的struts2+spring3搭建完成。
要点:这里有3个对应关系一定要注意
1、struts配置文件中,action的class属性要对应到spring配置文件中的bean的id,这样,struts就只负责传递,而所有servlet都有spring来管理。
我试过,struts.xml中,
不用<action name="user" class="userAction">
用<action name="user" class="demo.myssh.action.UserAction">也能正常运行。但是奇怪的是我之前如果这么用就无法注入。不确定是struts版本的问题还是哪里的问题。
个人理解,如果class里面配置的是个id,那么,action将有spring来直接管理。如果class里面配置的是类名,则action将有struts来处理。
推荐还是将action交给spring来管理的好,逻辑上更清晰,也不容易出现奇怪的问题。
2、spring配置文件中,注入类不是直接使用其类名,而是通过bean的id来获取。要注入哪个类,就在ref中填入那个的bean的id。
3、spring配置中注入属性的名称必须和java类里的属性名一致,并且,该属性还要提供set方法。
四、struts+spring搭建,spring注解注入
这里,还可以用spring的注解注入的方式进行注入。
导入的包和之前一样不变,web.xml不变
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:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd">
- <context:component-scan base-package="demo.myssh.business" />
- </beans>
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>
- <package name="mysshdemo" extends="struts-default">
- <action name="user" class="demo.myssh.action.UserAction">
- <result>/index.jsp</result>
- </action>
- </package>
- </struts>
UserAction.java
- package demo.myssh.action;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import com.opensymphony.xwork2.ActionSupport;
- import demo.myssh.business.UserService;
- public class UserAction extends ActionSupport {
- @Override
- public String execute() throws Exception {
- this.addActionMessage("UserAction working");
- // this.addActionMessage("hello world.");
- this.addActionMessage(userService.doing());// 修改下,确认注入成功。
- return ActionSupport.SUCCESS;
- }
- // 注入属性
- @Autowired
- private UserService userService;
- }
UserService.java
- package demo.myssh.business;
- import org.springframework.stereotype.Service;
- @Service
- public class UserService {
- public String doing()
- {
- return "UserService working.";
- }
- }
运行结果和之前的一样。
这样的注入方式可以省去一些代码,不需要大量的配置文件。
没有大量的配置文件,好处是省了不少代码,坏处是没有统一地方查看。
不过个人认为,如果采取这样的注入方式的话,不如直接把struts踢掉直接用spring3的mvc好了。
五、在struts和spring基础上加入hibernate
Hibernate有很机械的pojo类和hbm文件要写,这部分用myeclipse来做,能省不少事情,终于又感觉到myeclipse的好处了。
1、先在mysql里面建个表
- CREATE TABLE `t_user` (
- `pk_user_id` bigint(20) NOT NULL AUTO_INCREMENT,
- `login_name` varchar(45) DEFAULT NULL,
- `email` varchar(45) DEFAULT NULL,
- `password` varchar(45) DEFAULT NULL,
- PRIMARY KEY (`pk_user_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、让myeclipse连接到数据库,还会帮我们自动生成hibernate的配置文件,和一个sessionFactory类。
3、用myeclipse生成实体类和映射配置文件,在myeclipse的db browser里面,打开数据库,选中表。
最后会生成这样一堆文件
整理一下,我是这么放的
4、把myeclipse添加的lib引用去掉,换成hibernate4。
就是这两个hibernate 3.3的,去掉。
hibernate最新版是4.1.8,下载地址http://sourceforge.net/projects/hibernate/files/hibernate4/
解压开后,在lib目录下有个叫required的目录,下面放的是最基本的包,这个我喜欢,比struts和spring做的好。
引入这些包就好了
此外还需要mysql的连接包,最新版本5.1.22,下载地址:http://www.mysql.com/downloads/connector/j/
5、现在开始来修改,从web页面开始
在网站根目录下,增加一个user目录,下面添加一个add.jsp文件。
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%@ taglib prefix="s" uri="/struts-tags" %>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <title>My JSP 'add.jsp' starting page</title>
- <meta http-equiv="pragma" content="no-cache">
- <meta http-equiv="cache-control" content="no-cache">
- </head>
- <body>
- <form name="form1" method="post" action="user!add.action">
- <p>
- <label for="loginname">loginname</label>
- <input type="text" name="loginname">
- </p>
- <p>
- <label for="email">email</label>
- <input type="text" name="email">
- </p>
- <p>
- <label for="password">password</label>
- <input type="text" name="password">
- </p>
- <p>
- <input type="submit" name="submit" value="提交">
- </p>
- </form>
- This is my JSP page. <br>
- </body>
- </html>
这个页面,将内容提交到了UserAction类的add方法,现在来修改UserAction类。
UserAction类里面要添加add方法,和接收页面信息的属性及方法。
- package demo.myssh.action;
- import com.opensymphony.xwork2.ActionSupport;
- import demo.myssh.business.UserService;
- import demo.myssh.model.User;
- @SuppressWarnings("serial")
- public class UserAction extends ActionSupport {
- @Override
- public String execute() throws Exception {
- this.addActionMessage("UserAction working");
- // this.addActionMessage("hello world.");
- this.addActionMessage(userService.doing());// 修改下,确认注入成功。
- return ActionSupport.SUCCESS;
- }
- // 注入用属性
- private UserService userService;
- // 注入用的方法
- public void setUserService(UserService userService) {
- this.userService = userService;
- }
- public String add() throws Exception {
- userService.save(new User(loginname, email, password));
- return ActionSupport.SUCCESS;
- }
- private String loginname;
- private String email;
- private String password;
- public void setLoginname(String loginname) {
- this.loginname = loginname;
- }
- public void setEmail(String email) {
- this.email = email;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- }
这里面,用到了UserService类里面的save方法,继续修改UserService。
- package demo.myssh.business;
- import demo.myssh.dao.UserDAO;
- import demo.myssh.model.User;
- public class UserService {
- public String doing() {
- return "UserService working";
- }
- private UserDAO userDAO;
- public void setUserDAO(UserDAO userDAO) {
- this.userDAO = userDAO;
- }
- public void save(User user) {
- userDAO.save(user);
- }
- }
UserDAO这个类不需要多少修改,只需要把里面日志有关的地方去掉就可以了。因为,我没有引入那个类。大家都喜欢用log4j,打算之后加入log4j。
- package demo.myssh.dao;
- import java.util.List;
- import org.hibernate.LockMode;
- import org.hibernate.Query;
- import org.hibernate.criterion.Example;
- import demo.myssh.model.User;
- public class UserDAO extends BaseHibernateDAO {
- // property constants
- public static final String LOGIN_NAME = "loginName";
- public static final String EMAIL = "email";
- public static final String PASSWORD = "password";
- public void save(User transientInstance) {
- try {
- getSession().save(transientInstance);
- } catch (RuntimeException re) {
- throw re;
- }
- }
- public void delete(User persistentInstance) {
- try {
- getSession().delete(persistentInstance);
- } catch (RuntimeException re) {
- throw re;
- }
- }
- public User findById(java.lang.Long id) {
- try {
- User instance = (User) getSession().get("User", id);
- return instance;
- } catch (RuntimeException re) {
- throw re;
- }
- }
- public List findByExample(User instance) {
- try {
- List results = getSession().createCriteria("User")
- .add(Example.create(instance)).list();
- return results;
- } catch (RuntimeException re) {
- throw re;
- }
- }
- public List findByProperty(String propertyName, Object value) {
- try {
- String queryString = "from User as model where model."
- + propertyName + "= ?";
- Query queryObject = getSession().createQuery(queryString);
- queryObject.setParameter(0, value);
- return queryObject.list();
- } catch (RuntimeException re) {
- throw re;
- }
- }
- public List findByLoginName(Object loginName) {
- return findByProperty(LOGIN_NAME, loginName);
- }
- public List findByEmail(Object email) {
- return findByProperty(EMAIL, email);
- }
- public List findByPassword(Object password) {
- return findByProperty(PASSWORD, password);
- }
- public List findAll() {
- try {
- String queryString = "from User";
- Query queryObject = getSession().createQuery(queryString);
- return queryObject.list();
- } catch (RuntimeException re) {
- throw re;
- }
- }
- public User merge(User detachedInstance) {
- try {
- User result = (User) getSession().merge(detachedInstance);
- return result;
- } catch (RuntimeException re) {
- throw re;
- }
- }
- public void attachDirty(User instance) {
- try {
- getSession().saveOrUpdate(instance);
- } catch (RuntimeException re) {
- throw re;
- }
- }
- public void attachClean(User instance) {
- try {
- getSession().lock(instance, LockMode.NONE);
- } catch (RuntimeException re) {
- throw re;
- }
- }
- }
BaseHibernateDAO 不用改,沿用
- package demo.myssh.dao;
- import org.hibernate.Session;
- public class BaseHibernateDAO implements IBaseHibernateDAO {
- public Session getSession() {
- return HibernateSessionFactory.getSession();
- }
- }
IBaseHibernateDAO 不用改,沿用
- package demo.myssh.dao;
- import org.hibernate.Session;
- public interface IBaseHibernateDAO {
- public Session getSession();
- }
HibernateSessionFactory这个类要修改,因为hibernate 4 创建session的方法变了。
- package demo.myssh.dao;
- import org.hibernate.HibernateException;
- import org.hibernate.Session;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.service.ServiceRegistry;
- import org.hibernate.service.ServiceRegistryBuilder;
- public class HibernateSessionFactory {
- private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
- private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
- private static Configuration configuration = new Configuration();
- private static org.hibernate.SessionFactory sessionFactory;
- private static String configFile = CONFIG_FILE_LOCATION;
- private static ServiceRegistry serviceRegistry;
- static {
- try {
- //hibernate 3 的方法
- // configuration.configure(configFile);
- // sessionFactory = configuration.buildSessionFactory();
- //hibernate 4 的方法
- serviceRegistry = new ServiceRegistryBuilder().applySettings(
- configuration.configure().getProperties())
- .buildServiceRegistry();
- sessionFactory = configuration.buildSessionFactory(serviceRegistry);
- } catch (HibernateException e) {
- System.err.println("%%%% Error Creating SessionFactory %%%%");
- e.printStackTrace();
- }
- }
- private HibernateSessionFactory() {
- }
- public static Session getSession() throws HibernateException {
- Session session = (Session) threadLocal.get();
- if (session == null || !session.isOpen()) {
- if (sessionFactory == null) {
- rebuildSessionFactory();
- }
- session = (sessionFactory != null) ? sessionFactory.openSession()
- : null;
- threadLocal.set(session);
- }
- return session;
- }
- public static void rebuildSessionFactory() {
- try {
- // configuration.configure(configFile);
- // sessionFactory = configuration.buildSessionFactory();
- serviceRegistry = new ServiceRegistryBuilder().applySettings(
- configuration.configure().getProperties())
- .buildServiceRegistry();
- sessionFactory = configuration.buildSessionFactory(serviceRegistry);
- } catch (HibernateException e) {
- System.err.println("%%%% Error Creating SessionFactory %%%%");
- e.printStackTrace();
- }
- }
- public static void closeSession() throws HibernateException {
- Session session = (Session) threadLocal.get();
- threadLocal.set(null);
- if (session != null) {
- session.close();
- }
- }
- public static org.hibernate.SessionFactory getSessionFactory() {
- return sessionFactory;
- }
- public static void setConfigFile(String configFile) {
- HibernateSessionFactory.configFile = configFile;
- sessionFactory = null;
- }
- public static Configuration getConfiguration() {
- return configuration;
- }
- }
User实体类不用改,不过还是贴出来吧。
- package demo.myssh.model;
- public class User implements java.io.Serializable {
- private static final long serialVersionUID = -8290754809696899650L;
- private Long userID;
- private String loginName;
- private String email;
- private String password;
- /** default constructor */
- public User() {
- }
- /** full constructor */
- public User(String loginName, String email, String password) {
- this.loginName = loginName;
- this.email = email;
- this.password = password;
- }
- // Property accessors
- public Long getUserID() {
- return this.userID;
- }
- public void setUserID(Long userID) {
- this.userID = userID;
- }
- public String getLoginName() {
- return this.loginName;
- }
- public void setLoginName(String loginName) {
- this.loginName = loginName;
- }
- public String getEmail() {
- return this.email;
- }
- public void setEmail(String email) {
- this.email = email;
- }
- public String getPassword() {
- return this.password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- }
还有映射文件,记得添加下package,让hibernate能找到实体类
- <?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 package="demo.myssh.model">
- <class name="User" table="t_user" catalog="myssh">
- <id name="userID" type="java.lang.Long">
- <column name="pk_user_id" />
- <generator class="identity"></generator>
- </id>
- <property name="loginName" type="java.lang.String">
- <column name="login_name" length="45"/>
- </property>
- <property name="email" type="java.lang.String">
- <column name="email" length="45"/>
- </property>
- <property name="password" type="java.lang.String">
- <column name="password" length="45"/>
- </property>
- </class>
- </hibernate-mapping>
然后是hibernate的配置文件,
这里注意,要添加autocommit属性,因为是用的myeclipse提供的sessionFactory。mapping路径也不要忘了。
- <?xml version='1.0' encoding='utf-8'?>
- <!DOCTYPE hibernate-configuration PUBLIC
- "-//Hibernate/Hibernate Configuration DTD//EN"
- "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
- <hibernate-configuration>
- <session-factory>
- <!-- properties -->
- <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
- <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
- <property name="connection.url">jdbc:mysql://localhost:3306/myssh</property>
- <property name="connection.username">root</property>
- <property name="connection.password"></property>
- <property name="connection.autocommit">true</property>
- <!-- mapping files -->
- <mapping resource="demo/myssh/model/User.hbm.xml" />
- </session-factory>
- </hibernate-configuration>
最后,修改spring的配置文件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:p="http://www.springframework.org/schema/p"
- xmlns:util="http://www.springframework.org/schema/util"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/util
- http://www.springframework.org/schema/util/spring-util-3.0.xsd">
- <bean id="userAction" class="demo.myssh.action.UserAction">
- <property name="userService" ref="userService" />
- </bean>
- <bean id="userService" class="demo.myssh.business.UserService">
- <property name="userDAO" ref="userDAO"></property>
- </bean>
- <bean id="userDAO" class="demo.myssh.dao.UserDAO">
- </bean>
- </beans>
运行访问:http://localhost:8080/user/add.jsp
提交以后,又转回index.jsp,查看数据库
大功告成,最基本简单的ssh框架搭建终于完成。
程序结构再贴下
六、加入log4j
log4j是个很多人喜欢用的日志工具,随大流咯。
log4j的最新版是2.0-beat3,稳妥起见,我用了log4j 1.2.17,下载地址:http://logging.apache.org/log4j/1.2/download.html
导入包,在src目录下添加log4j的配置文件:log4j.properties,下面这个是网络上下载来的,看起来不错。
- #------------------------------------------------------------------------------
- #
- # The following properties set the logging levels and log appender. The
- # log4j.rootCategory variable defines the default log level and one or more
- # appenders. For the console, use 'S'. For the daily rolling file, use 'R'.
- # For an HTML formatted log, use 'H'.
- #
- # To override the default (rootCategory) log level, define a property of the
- # form (see below for available values):
- #
- # log4j.logger. =
- #
- # Available logger names:
- # TODO
- #
- # Possible Log Levels:
- # FATAL, ERROR, WARN, INFO, DEBUG
- #
- #------------------------------------------------------------------------------
- log4j.rootCategory=INFO, S
- log4j.logger.com.dappit.Dapper.parser=ERROR
- log4j.logger.org.w3c.tidy=FATAL
- #------------------------------------------------------------------------------
- #
- # The following properties configure the console (stdout) appender.
- # See http://logging.apache.org/log4j/docs/api/index.html for details.
- #
- #------------------------------------------------------------------------------
- log4j.appender.S = org.apache.log4j.ConsoleAppender
- log4j.appender.S.layout = org.apache.log4j.PatternLayout
- log4j.appender.S.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n
- #------------------------------------------------------------------------------
- #
- # The following properties configure the Daily Rolling File appender.
- # See http://logging.apache.org/log4j/docs/api/index.html for details.
- #
- #------------------------------------------------------------------------------
- log4j.appender.R = org.apache.log4j.DailyRollingFileAppender
- log4j.appender.R.File = logs/bensApps.log
- log4j.appender.R.Append = true
- log4j.appender.R.DatePattern = '.'yyy-MM-dd
- log4j.appender.R.layout = org.apache.log4j.PatternLayout
- log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n
- #------------------------------------------------------------------------------
- #
- # The following properties configure the Rolling File appender in HTML.
- # See http://logging.apache.org/log4j/docs/api/index.html for details.
- #
- #------------------------------------------------------------------------------
- log4j.appender.H = org.apache.log4j.RollingFileAppender
- log4j.appender.H.File = logs/bensApps.html
- log4j.appender.H.MaxFileSize = 100KB
- log4j.appender.H.Append = false
- log4j.appender.H.layout =org.apache.log4j.HTMLLayout
在hibernate的配置里面,添加
- <property name="show_sql">true</property>
- <property name="format_sql">true</property>
这样,就能在控制台显示sql语句了,而且还排了下版。
七、加入c3p0连接池并改用spring提供的sessionFactory
前面的那个,虽然是将ssh整合在一起了,但是,是通过myeclipse自动编写的一个sessionFactory来访问,显然不够,现在加入c3p0链接池并修改成使用spring的sessionFactory来操作数据。
c3p0是一个第三方的链接池,hibernate自己貌似没做。不过在hibernate的压缩包里面,有个c3p0的目录,既然hibernate给出来了,就不用再找了。
spring的sessionFactory是用代理的方式重新包装了hibernate的sessionFactory,所以,还要引入spring的aop包,此外还要引入另外两个第三方的包
aopalliance 和 cglib-nodep。两个都没搜到官方网站,就有下载地址。
aopalliance-1.0.jar,http://mvnrepository.com/artifact/aopalliance/aopalliance
cglib-nodep-2.2.3.jar,http://sourceforge.net/projects/cglib/files/
修改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:p="http://www.springframework.org/schema/p"
- xmlns:util="http://www.springframework.org/schema/util"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/util
- http://www.springframework.org/schema/util/spring-util-3.0.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
- destroy-method="close">
- <property name="driverClass" value="com.mysql.jdbc.Driver" />
- <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/myssh" />
- <property name="user" value="root" />
- <property name="password" value="" />
- <!-- 连接池中保留的最小连接数。 -->
- <property name="minPoolSize" value="5" />
- <!-- 连接池中保留的最大连接数。Default: 15 -->
- <property name="maxPoolSize" value="15" />
- <!-- 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
- <property name="initialPoolSize" value="3" />
- <!-- 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
- <property name="maxIdleTime" value="10" />
- <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
- <property name="acquireIncrement" value="3" />
- <!-- JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
- 如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0 -->
- <property name="maxStatements" value="0" />
- <!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->
- <property name="idleConnectionTestPeriod" value="60" />
- <!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
- <property name="acquireRetryAttempts" value="30" />
- <!-- 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
- 获取连接失败后该数据源将申明已断开并永久关闭。Default: false -->
- <property name="breakAfterAcquireFailure" value="false" />
- <!-- 因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的 时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
- 等方法来提升连接测试的性能。Default: false -->
- <property name="testConnectionOnCheckout" value="false" />
- </bean>
- <!-- Hibernate SessionFactory -->
- <bean id="sessionFactory"
- class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
- <property name="dataSource">
- <ref local="dataSource" />
- </property>
- <property name="mappingDirectoryLocations">
- <list>
- <value>classpath*:/demo/myssh/model</value>
- </list>
- </property>
- <property name="hibernateProperties">
- <props>
- <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
- <prop key="hibernate.show_sql">true</prop>
- <prop key="hibernate.format_sql">true</prop>
- <prop key="hibernate.generate_statistics">true</prop>
- <prop key="hibernate.autoReconnect">true</prop>
- <prop key="hibernate.max_fech_depth">5</prop>
- <prop key="hibernate.jdbc.batch_size">50</prop>
- <prop key="hibernate.jdbc.fetch_size">100</prop>
- </props>
- </property>
- </bean>
- <tx:annotation-driven />
- <bean id="transactionManager"
- class="org.springframework.orm.hibernate4.HibernateTransactionManager">
- <property name="sessionFactory" ref="sessionFactory" />
- </bean>
- <bean id="userAction" class="demo.myssh.action.UserAction">
- <property name="userService" ref="userService" />
- </bean>
- <bean id="userService" class="demo.myssh.business.UserService">
- <property name="userDAO" ref="userDAO"></property>
- </bean>
- <bean id="userDAO" class="demo.myssh.dao.UserDAO">
- <property name="sessionFactory" ref="sessionFactory"></property>
- </bean>
- </beans>
demo.myssh.dao下面只留下UserDAO,其他都删掉。spring的sessionFactory自动打开session会预期一个spring的事务,所以,要么,将所有DAO方法放进事务,要么,自己手动open一下session。我选择了前者。
- package demo.myssh.dao;
- import java.util.List;
- import org.hibernate.LockMode;
- import org.hibernate.Query;
- import org.hibernate.SessionFactory;
- import org.hibernate.criterion.Example;
- import org.springframework.transaction.annotation.Transactional;
- import demo.myssh.model.User;
- public class UserDAO {
- // property constants
- public static final String LOGIN_NAME = "loginName";
- public static final String EMAIL = "email";
- public static final String PASSWORD = "password";
- private SessionFactory sessionFactory;
- public void setSessionFactory(SessionFactory sessionFactory) {
- this.sessionFactory = sessionFactory;
- }
- @Transactional
- public void save(User transientInstance) {
- //HibernateTemplate
- try {
- //getSession().save(transientInstance);
- this.sessionFactory.getCurrentSession().save(transientInstance);
- } catch (RuntimeException re) {
- throw re;
- }
- }
- }
这样算是一个完整的SSH框架搭建完成了。撒花。。。。。。
八、现在对SSH框架的理解
不多说了,上图
顺便发点牢骚:
java和.net在开发web项目的时候,.net是商业软件,由一方提供全套解决方案,你面对的文档是一种,而且微软的文档中经常有例子。而java是开源的,是由多方提供不同构件组成的,你需要面对各种格式规范不一样的文档,在文档查找和阅读方面,真的是非常头疼啊。现在做的这个东西,已经用到了由9个不懂厂商/人提供的jar包,苦啊。
当然,就向技术总监说的,.net这种一方提供的东西,他做好了也就好了,如果他没做好,你想解决很困难。java这种多方提供的东西,这个不行你还能换另外一个,总会有解决。
九、Junit4单元测试
框架完成,开始一点一点添加其他内容。
myeclipse10自带有junit4,直接用就好,当然如果要下载也行。https://github.com/KentBeck/junit/downloads
在之前的基础上,我将dao和service层都改成了接口调用,其他没变。
对UserDAO进行测试,在myeclipse里面直接添加junit test case就好,然后再引入spring的test包:org.springframework.test-3.1.3.RELEASE
UserDAOImplTest代码如下
- package demo.myssh.dao.impl;
- import org.junit.Before;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.test.annotation.Repeat;
- import org.springframework.test.context.ContextConfiguration;
- import org.springframework.test.context.junit4.*;
- import demo.myssh.dao.IUserDAO;
- import demo.myssh.model.User;
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration({"file:WebRoot/WEB-INF/applicationContext.xml"})
- public class UserDAOImplTest {
- @Autowired
- @Qualifier("user")
- private User user;
- @Autowired
- @Qualifier("iUserDAO")
- private IUserDAO userDao;
- @Before
- public void setUp() throws Exception {
- user.setEmail("1email");
- user.setLoginName("1login name");
- user.setPassword("1assword");
- }
- @Test
- @Repeat(5)
- public final void testSave() {
- userDao.save(user);
- //fail("Not yet implemented");
- }
- }
选择文件,run as -- junit test,
简单的测试,这样就算ok了。
十、Junit+GroboUtils进行多线程测试
Junit4不能模拟多线程的情况,需要其他支持,我用的是GroboUtils,最新版本5,下载地址:http://groboutils.sourceforge.net/downloads.html
GroboUtils测试的代码是用网上抄来的,来源:http://www.coderli.com/multi-thread-junit-grobountils
UserDAOImplTest的代码
- package demo.myssh.dao.impl;
- import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;
- import net.sourceforge.groboutils.junit.v1.TestRunnable;
- import org.junit.Before;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.test.annotation.Repeat;
- import org.springframework.test.context.ContextConfiguration;
- import org.springframework.test.context.junit4.*;
- import demo.myssh.dao.IUserDAO;
- import demo.myssh.model.User;
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration({ "file:WebRoot/WEB-INF/applicationContext.xml" })
- public class UserDAOImplTest {
- @Autowired
- @Qualifier("iUserDAO")
- private IUserDAO userDao;
- @Test
- @Repeat(2)
- public void MultiRequestsTest() {
- // 构造一个Runner
- TestRunnable runner = new TestRunnable() {
- @Override
- public void runTest() throws Throwable {
- // 测试内容
- // System.out.println("a");
- userDao.save(new User("aa", "bb", "cc"));
- }
- };
- int runnerCount = 2;
- // Rnner数组,相当于并发多少个。
- TestRunnable[] trs = new TestRunnable[runnerCount];
- for (int i = 0; i < runnerCount; i++) {
- trs[i] = runner;
- }
- // 用于执行多线程测试用例的Runner,将前面定义的单个Runner组成的数组传入
- MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(trs);
- try {
- // 开发并发执行数组里定义的内容
- mttr.runTestRunnables();
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
- }