一、建立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();
- }
- }
- }