javascript
Spring-Spring MVC + Spring JDBC + Spring Transaction + Maven 构建web登录模块
- 概述
- 功能簡(jiǎn)介
- 環(huán)境準(zhǔn)備
- 構(gòu)建工具M(jìn)aven
- 數(shù)據(jù)庫(kù)腳本Oracle
- 建立工程
- 類包及Spring配置文件規(guī)劃
- 持久層
- 建立領(lǐng)域?qū)ο?ul>
- 用戶領(lǐng)域?qū)ο?/li>
- 登錄日志領(lǐng)域?qū)ο?/li>
- UserDao
- LoginLogDao
- 在Spring中裝配DAO
- UserService
- 在Spring中裝配Service
- 配置Spring MVC 框架
- 處理登錄請(qǐng)求
- POJO控制器類
- Spring MVC 配置文件
- ModelAndView的解析配置
- JSP視圖頁(yè)面
- loginjsp
- mainjsp
- 第一種方式 maven插件部署web應(yīng)用服務(wù)器
- 第二種方式 部署到容器中
概述
本模塊主要涉及以下部分:
- Maven構(gòu)建web項(xiàng)目的過(guò)程
- 登錄流程介紹
- 基于Spring JDBC的持久層實(shí)現(xiàn)
- 基于Spring 聲明式事務(wù)的業(yè)務(wù)層實(shí)現(xiàn)
- 基于Spring MVC的展現(xiàn)層實(shí)現(xiàn)
- 運(yùn)行Web應(yīng)用
功能簡(jiǎn)介
用戶訪問(wèn)login.jsp,返回帶有用戶名和密碼輸入框的登錄頁(yè)面
用戶輸入用戶名和密碼,提交表單到服務(wù)器,Spring根據(jù)配置文件調(diào)用LoginController控制器響應(yīng)請(qǐng)求
LoginController調(diào)用UserService的hasMatchUser()方法來(lái)判斷用戶是否存在
如果用戶不存在,跳轉(zhuǎn)到登錄頁(yè)面,給出提示,否則進(jìn)入下一步
如果用戶存在,LoginController調(diào)用UserService的findUserByName方法,進(jìn)行登錄成功后的業(yè)務(wù)處理:首先調(diào)用updateLoginInfo的updateLoginInfo方法 增加5分積分,然后創(chuàng)建一個(gè)LoginLog對(duì)象,將LoginLog寫(xiě)入數(shù)據(jù)庫(kù)
重定向到主頁(yè)面,將響應(yīng)返回給用戶
環(huán)境準(zhǔn)備
構(gòu)建工具M(jìn)aven
請(qǐng)參考上篇博文 Maven-EclipseEE使用Maven構(gòu)建Java web項(xiàng)目從0到1
數(shù)據(jù)庫(kù)腳本(Oracle)
-- Create table create table TEMP_USER (user_id NUMBER not null,user_name VARCHAR2(50),password VARCHAR2(10),credits NUMBER,last_visit DATE,last_ip VARCHAR2(20) ) tablespace TAB_XGJpctfree 10initrans 1maxtrans 255storage(initial 64Kminextents 1maxextents unlimited); -- Create/Recreate primary, unique and foreign key constraints alter table TEMP_USERadd constraint PK_USER_ID primary key (USER_ID)using index tablespace TAB_XGJpctfree 10initrans 2maxtrans 255;-- Create sequence create sequence TEMP_USER_ID_SEQ minvalue 1 maxvalue 999999999 start with 1 increment by 1 cache 20;-- Create table create table TEMP_LOGIN_LOG (login_log_id NUMBER not null,user_id NUMBER not null,ip VARCHAR2(20),login_datetime DATE ) tablespace TAB_XGJpctfree 10initrans 1maxtrans 255storage(initial 64Kminextents 1maxextents unlimited); -- Create/Recreate primary, unique and foreign key constraints alter table TEMP_LOGIN_LOGadd constraint PK_LOGIN_LOG_ID primary key (LOGIN_LOG_ID)using index tablespace TAB_XGJpctfree 10initrans 2maxtrans 255; alter table TEMP_LOGIN_LOGadd constraint FK_USER_ID foreign key (USER_ID)references TEMP_USER (USER_ID);-- Create sequence create sequence TEMP_LOGIN_LOG_ID_SEQ minvalue 1 maxvalue 999999999 start with 1 increment by 1 cache 20;--simulate data INSERT INTO TEMP_USER (user_id,user_name,password) VALUES(TEMP_USER_ID_SEQ.Nextval ,'xgj','123456'); COMMIT;建立工程
請(qǐng)參考上篇博文 Maven-EclipseEE使用Maven構(gòu)建Java web項(xiàng)目從0到1
類包及Spring配置文件規(guī)劃
類包以分層的方式進(jìn)行組織,供劃分為dao、domain、service、web。
domain對(duì)象嚴(yán)格意義上講屬于業(yè)務(wù)層,但是由于domain對(duì)象可能同時(shí)被持久層和展現(xiàn)層共享,所以一般單獨(dú)的將其劃分在一個(gè)包中。
持久層
持久層負(fù)責(zé)數(shù)據(jù)的訪問(wèn)和操作,DAO類被上層的業(yè)務(wù)類調(diào)用。
這里使用Spring JDBC作為持久層的實(shí)現(xiàn)技術(shù)。
用戶領(lǐng)域?qū)ο?/h3>
可以看成對(duì)TEMP_USER表的對(duì)象映射,每個(gè)字段對(duì)應(yīng)一個(gè)對(duì)象屬性。
package com.xgj.domain;import java.io.Serializable; import java.util.Date;/*** @ClassName: User* @Description: User實(shí)體類,可以簡(jiǎn)單的理解為對(duì) temp_user表的字段映射* @author: Mr.Yang* @date: 2017年6月24日 下午6:45:12*/// 領(lǐng)域?qū)ο笠话愣家獙?shí)現(xiàn)Serializable接口,以便可以序列化 public class User implements Serializable {private int userId;private String username;private String password;private int credit;private Date lastVisit;private String lastIP;public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}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;}public int getCredit() {return credit;}public void setCredit(int credit) {this.credit = credit;}public Date getLastVisit() {return lastVisit;}public void setLastVisit(Date lastVisit) {this.lastVisit = lastVisit;}public String getLastIP() {return lastIP;}public void setLastIP(String lastIP) {this.lastIP = lastIP;}}package com.xgj.domain;import java.io.Serializable;
import java.util.Date;/*** * @ClassName: LoginLog* * @Description: 登錄信息實(shí)體類,可以簡(jiǎn)單的理解為對(duì) TEMP_LOGIN_LOG表的字段映射* * @author: Mr.Yang* * @date: 2017年6月24日 下午8:12:28* * *///領(lǐng)域?qū)ο笠话愣家獙?shí)現(xiàn)Serializable接口,以便可以序列化
public class LoginLog implements Serializable {private int login_log_id;private int user_id;private String ip;private Date login_datetime;public int getLogin_log_id() {return login_log_id;}public void setLogin_log_id(int login_log_id) {this.login_log_id = login_log_id;}public int getUser_id() {return user_id;}public void setUser_id(int user_id) {this.user_id = user_id;}public String getIp() {return ip;}public void setIp(String ip) {this.ip = ip;}public Date getLogin_datetime() {return login_datetime;}public void setLogin_datetime(Date login_datetime) {this.login_datetime = login_datetime;}
}
UserDao
首先來(lái)定義訪問(wèn)User的DAO。
按照設(shè)計(jì),主要包括3個(gè)方法
- getMatchCount(String username,String passwd)
- findUserbyUsername(tring username)
- updateLoginInfo(User user) 更新用戶積分、最后登錄IP和最后登錄時(shí)間
Spring 2.5之后可以使用注解的方式定義Bean,較之XML的配置方式,注解配置的方式更加簡(jiǎn)單明了,推薦使用。
通過(guò)@Repository定義一個(gè)DAO Bean,然后使用@Autowired將Spring容器中的Bean注入進(jìn)來(lái)。
操作數(shù)據(jù)庫(kù)使用Spring JDBC ,對(duì)傳統(tǒng)的JDBC API進(jìn)行了薄層封裝。 Spring JDBC通過(guò)模板類
org.springframework.jdbc.core.JdbcTemplate封裝了樣板式的o碼,用戶可以很輕松的完成大部分?jǐn)?shù)據(jù)訪問(wèn)操作。
LoginLogDao
LoginLogDao 負(fù)責(zé)記錄用戶的登錄日志,僅僅有一個(gè)方法 insertLoginLog(LoginLog loginLog)
package com.xgj.dao;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository;import com.xgj.domain.LoginLog;@Repository // 通過(guò)Spring注解定義一個(gè)DAO public class LoginLogDao {private JdbcTemplate jdbcTemplate;// 保存登陸日志SQLprivate final static String INSERT_LOGIN_LOG_SQL = "INSERT INTO TEMP_LOGIN_LOG(login_log_id,user_id,ip,login_datetime) VALUES(TEMP_LOGIN_LOG_ID_SEQ.nextval,?,?,?)";@Autowired // 自動(dòng)注入JdbcTemplate的Beanpublic void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}public void insertLoginLog(LoginLog loginLog) {Object[] args = { loginLog.getUser_id(), loginLog.getIp(), loginLog.getLogin_datetime() };jdbcTemplate.update(INSERT_LOGIN_LOG_SQL, args);System.out.println("成功");}}在Spring中裝配DAO
我們?cè)诰帉?xiě)DAO接口實(shí)現(xiàn)類的時(shí)候,我們并沒(méi)有看到打開(kāi)、釋放數(shù)據(jù)庫(kù)connection的代碼, 那DAO類是如何訪問(wèn)數(shù)據(jù)庫(kù)的呢?
是的 JdbcTemplate封裝起來(lái)了, JdbcTemplate 本身需要一個(gè)數(shù)據(jù)源DataSource,這樣它就可以從DataSource中獲取或者返回連接。
在UserDAO和LoginDAO都提供了一個(gè)帶@Autowired注解的JdbcTemplate變量,所以我們必須先聲明一個(gè)數(shù)據(jù)源,然后定義一個(gè)JdbcTemplate變量,通過(guò)Spring的容器上下文自動(dòng)綁定機(jī)制注入bean.
Maven工程,在src\resources(資源文件目錄),創(chuàng)建Spring的配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!--引用Spring的多個(gè)Schema空間的格式定義文件--> <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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd<!-- 掃描類包,將標(biāo)注Spring注解的類自動(dòng)轉(zhuǎn)化Bean,同時(shí)完成Bean的注入 --><context:component-scan base-package="com.xgj.dao"/><!-- 配置數(shù)據(jù)源 http://langgufu.iteye.com/blog/2200588--><!-- mysql<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver"p:url="jdbc:mysql://localhost:3306/sampledb"p:username="root"p:password="123456" /> --><!-- oracle 兩種寫(xiě)法<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/><property name="url" value="jdbc:oracle:thin:@172.25.246.11:1521:cc"/><property name="username" value="cctb"/><property name="password" value="zsmart2017"/></bean> --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"p:driverClassName="oracle.jdbc.driver.OracleDriver"p:url="jdbc:oracle:thin:@172.25.246.11:1521:cc"p:username="cctb"p:password="smart" /><!-- 配置Jdbc模板 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource" /></beans>- 我們使用<context:component-scan base-package="目錄名稱"/> 掃描指定類包下的所有類,這樣在類中定義的注解比如@Respoitiry @Autowired等才會(huì)產(chǎn)生作用
- 我們使用Jakarta的DBCP開(kāi)源數(shù)據(jù)實(shí)現(xiàn)方案定義一個(gè)數(shù)據(jù)源
- 配置 將 上一步的DataSource注入到JdbcTemplate中, 而這個(gè)JdbcTemplate將通過(guò)@Autowired自動(dòng)注入到LoginDao和UserDao的Bean中, 可見(jiàn)Spring可以很好地將注解配置和XML配置統(tǒng)一起來(lái)。
這樣 就完成了登錄模塊的持久層所有的開(kāi)發(fā)工作
業(yè)務(wù)層
我們?cè)O(shè)計(jì)的登錄模塊,業(yè)務(wù)層僅有一個(gè)業(yè)務(wù)類 UserService。
UserService負(fù)責(zé)將持久層的UserDAO和LoginLogDao組織起來(lái),完成用戶密碼認(rèn)證、登錄日志記錄等操作。
UserService
三個(gè)業(yè)務(wù)方法
UserService的實(shí)現(xiàn)類需要調(diào)用DAO層的兩個(gè)DAO完成業(yè)務(wù)邏輯操作。
package com.xgj.service;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;import com.xgj.dao.LoginLogDao; import com.xgj.dao.UserDao; import com.xgj.domain.LoginLog; import com.xgj.domain.User;@Service // 將UserService標(biāo)注為一個(gè)服務(wù)層的bean public class UserService {private UserDao userDao;private LoginLogDao loginLogDao;@Autowired // 注入beanpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}@Autowired // 注入beanpublic void setLoginLogDao(LoginLogDao loginLogDao) {this.loginLogDao = loginLogDao;}public boolean hasMatchUser(String username, String password) {return userDao.getMatchCount(username, password) > 0;}public User findUserByName(String username) {return userDao.findUserbyUsername(username);}@Transactionalpublic void loginSuccess(User user) {// credit 增加5分user.setCredit(5 + user.getCredit());LoginLog loginLog = new LoginLog();loginLog.setUser_id(user.getUserId());loginLog.setIp(user.getLastIP());loginLog.setLogin_datetime(user.getLastVisit());userDao.updateLoginInfo(user);loginLogDao.insertLoginLog(loginLog);}}其中 loginSuccess方法將兩個(gè)DAO組織起來(lái),共同完成一個(gè)事務(wù)性的數(shù)據(jù)操作:更新temp_user表 并 添加temp_login_Log表的記錄。
我們?cè)赨serService中看不出來(lái)任何事務(wù)操作的影子,這正是Spring的高明之處,通過(guò)注解和配置文件配置的方式,將我們從事務(wù)操作單調(diào)機(jī)械的代碼中解脫出來(lái)。
下面我們來(lái)看下Spring是如何裝載Service的
在Spring中裝配Service
事務(wù)管理的代碼雖然僅僅是以注解@Transactional的方式出現(xiàn)在程序代碼中,但是我們必須告訴Spring哪些業(yè)務(wù)類需要工作在事務(wù)環(huán)境下以及事務(wù)的規(guī)則等,以方便Spring根據(jù)這些信息自動(dòng)為目標(biāo)業(yè)務(wù)類添加事務(wù)管理的功能。
<?xml version="1.0" encoding="UTF-8" ?> <!--(1)引入aop以及tx命名空間和對(duì)應(yīng)的Schema文件--> <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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.3.xsd"><context:component-scan base-package="com.xgj.dao"/><!-- 2)掃描類Service類包,將標(biāo)注Spring注解的類自動(dòng)轉(zhuǎn)化Bean,同時(shí)完成Bean的注入 --><context:component-scan base-package="com.xgj.service"/><!-- 配置數(shù)據(jù)源 http://langgufu.iteye.com/blog/2200588--><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"p:driverClassName="oracle.jdbc.driver.OracleDriver"p:url="jdbc:oracle:thin:@172.25.246.11:1521:cc"p:username="cctb"p:password="smart" /><!-- 配置Jdbc模板 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource" /><!-- (3)配置事務(wù)管理器 --><bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="dataSource" /><!-- (4)通過(guò)AOP配置提供事務(wù)增強(qiáng),讓service包下所有Bean的所有方法擁有事務(wù) --><aop:config proxy-target-class="true"><aop:pointcut id="serviceMethod"expression="(execution(* com.xgj.service..*(..))) and (@annotation(org.springframework.transaction.annotation.Transactional))" /><aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" /></aop:config><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="*" /></tx:attributes></tx:advice> </beans>查看 (1)(2)(3)(4) 步驟以及說(shuō)明。
這樣就完成了業(yè)務(wù)層的程序開(kāi)發(fā)和配置工作, 接下來(lái)需要對(duì)該業(yè)務(wù)類進(jìn)行單元測(cè)試
使用TestNGN對(duì)業(yè)務(wù)類進(jìn)行單元測(cè)試
TestNG和Junit相比有了重大的改進(jìn),我們?cè)谶@里使用TestNGN使用單元測(cè)試。
步驟:
- 添加TestNGN依賴包到跟模塊的pom.xml文件中
其中 版本為 6.8.7
<properties><testng.version>6.8.7</testng.version> </properties>因本模塊采用Maven構(gòu)建 ,所以在約定目錄下 src\test\java測(cè)試目錄下創(chuàng)建和UserService一致的包結(jié)構(gòu),并創(chuàng)建UserService對(duì)應(yīng)的測(cè)試類UserServiceTest ,目錄如下:
編寫(xiě)測(cè)試代碼
Spring4.0的測(cè)試框架很好的整合了TestNGN單元測(cè)試框架, 測(cè)試代碼通過(guò)擴(kuò)展Spring測(cè)試框架提供的測(cè)試基類AbstractTransactionTestNGSpringContextTests來(lái)啟動(dòng)測(cè)試運(yùn)行器。
通過(guò)@ContextConfiguration注解來(lái)指定Spring的配置文件。
使用@Autowired 將Spring容器中的Bean注入到測(cè)試類中。
在測(cè)試方法前通過(guò)TestNGN的@Test注解方法即可將方法標(biāo)注為測(cè)試方法。
- 運(yùn)行測(cè)試類
選中項(xiàng)目,右鍵 Runas —Maven Test
觀察運(yùn)行結(jié)果,確保無(wú)錯(cuò)誤。
展現(xiàn)層
我們已經(jīng)開(kāi)發(fā)完成了持久層和業(yè)務(wù)層 ,是時(shí)候提供一個(gè)簡(jiǎn)單的頁(yè)面了~
在這里我們使用SpringMVC作為展現(xiàn)層的技術(shù)選擇。
Spring3.0提供了REST風(fēng)格的MVC,使Spring MVC變的更加輕便、已用。
Spring4.0對(duì)MVC進(jìn)行了全面的增強(qiáng),支持跨域注解@CrossOrigin配置、GroovyWeb集成、Gson、Jackson、Protobuf的HttpMessageConverter消息轉(zhuǎn)換器等。
配置Spring MVC 框架
- 配置web.xml,以便Web容器啟動(dòng)的時(shí)候能夠自動(dòng)啟動(dòng)Spring容器。
在(1)處通過(guò)web容器上線文參數(shù)指定Spring配置文件的地址,多個(gè)配置文件使用逗號(hào)或者空格分隔,建議逗號(hào)。
然后在(2)處指定Spring所提供的ContextLoaderListener的web容器監(jiān)聽(tīng)器,該監(jiān)聽(tīng)器活在web容器啟動(dòng)時(shí)自動(dòng)運(yùn)行,它會(huì)根據(jù)contextConfigLocationWeb容器參數(shù)獲取Spring配置文件,并啟動(dòng)Spring容器。
注意需要將log4j.propertis日志配置文件放在類路徑下,以便日志引擎自動(dòng)生效。
最后需要配置SpringMVC相關(guān)的信息, SpringMVC也是通過(guò)一個(gè)Servlet來(lái)截取URL請(qǐng)求,然后再進(jìn)行相關(guān)的處理
<!--(1)Spring MVC 的主Servlet--><servlet><servlet-name>smart</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>3</load-on-startup></servlet><!--(2)Spring MVC 處理的URL--><servlet-mapping><servlet-name>smart</servlet-name><url-pattern>*.html</url-pattern></servlet-mapping>在(1)處生命了一個(gè)Servlet,Spring MVC 也擁有一個(gè)Spring配置文件(后面涉及),該配置文件的文件名稱和此處定義的Servlet名有一個(gè)契約,即<Servlet名>-servlet.xml的形式.
在這里,我們定義的Servlet的名稱為smart ,因此在/WEB-INF目錄下必須提供一個(gè)名為smart-servlet.xml的Spring MVC 配置文件。但是這個(gè)配置文件無(wú)需通過(guò)web.xml的contextConfigLocation上下文參數(shù)進(jìn)行聲明, Spring MVC 會(huì)自動(dòng)將smart-servlet.xml文件和Spring的其他配置文件進(jìn)行拼裝。
在(2)處對(duì)這個(gè)Servlet的URL路徑進(jìn)行定義,在這里讓所有以.html為后綴的URL都能被smart Servlet截獲, 進(jìn)而轉(zhuǎn)由Spring MVC框架進(jìn)行處理。
這種方式的好處: 使用.html后綴 ,一方面用戶不能通過(guò)URL直接指導(dǎo)我們采用了何種服務(wù)端技術(shù),另一方面.html是靜態(tài)網(wǎng)頁(yè)的后綴,可以騙過(guò)搜索引擎,增加被收錄的概率。 推薦采用這種后綴。
對(duì)已那些真正無(wú)需任何動(dòng)態(tài)處理的靜態(tài)網(wǎng)頁(yè),則可以使用.htm后綴加以區(qū)分,以避免被框架截獲。
當(dāng)請(qǐng)求被Spring MVC截獲后,首先根據(jù)請(qǐng)求的URL來(lái)查找目標(biāo)的處理控制器,并將請(qǐng)求參數(shù)封裝“命令”對(duì)象一起傳給控制器處理, 然后控制器調(diào)用Spring容器中的業(yè)務(wù)Bean完成業(yè)務(wù)處理工作并返回結(jié)果視圖。
處理登錄請(qǐng)求
POJO控制器類
首先編寫(xiě)LoginController類,負(fù)責(zé)處理登錄請(qǐng)求,完成登錄業(yè)務(wù)。
package com.xgj.web;import java.util.Date;import javax.servlet.http.HttpServletRequest;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView;import com.xgj.domain.User; import com.xgj.service.UserService;//(1)標(biāo)注稱為Spring MVC的Controller @RestController public class LoginController{private UserService userService;// (2)負(fù)責(zé)處理 /index.html的請(qǐng)求@RequestMapping(value = "/index.html")public String loginPage(){return "login";}// (3)負(fù)責(zé)處理 /loginCheck.html的請(qǐng)求@RequestMapping(value = "/loginCheck.html")public ModelAndView loginCheck(HttpServletRequest request,LoginCommand loginCommand){boolean isValidUser = userService.hasMatchUser(loginCommand.getUserName(),loginCommand.getPassword());if (!isValidUser) {return new ModelAndView("login", "error", "密碼錯(cuò)誤,請(qǐng)重新登錄");} else {User user = userService.findUserByName(loginCommand.getUserName());user.setLastIP(request.getLocalAddr());user.setLastVisit(new Date());userService.loginSuccess(user);request.getSession().setAttribute("user", user);return new ModelAndView("main");}}// 自動(dòng)注入bean@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;} }在(1)處通過(guò)Spring MVC的 @RestController(或者@Controller)注解可以將任何一個(gè)POJO的類標(biāo)注為Spring MVC的控制器,處理HTTP請(qǐng)求。 當(dāng)然了 標(biāo)注了 @RestController(或者@Controller)首先是一個(gè)Bean,所以可以通過(guò)使用@Autowired進(jìn)行Bean的注入。
一個(gè)控制器可以擁有多個(gè)處理映射不同HTTP請(qǐng)求路徑的方法,比如(2)(3)。
請(qǐng)求參數(shù)會(huì)根據(jù)參數(shù)名稱默認(rèn)契約自動(dòng)綁定到相應(yīng)方法的入?yún)⒅小?比如 在(3)中的loginCheck(HttpServletRequest request,LoginCommand loginCommand)方法中,請(qǐng)求參數(shù)會(huì)按照名稱匹配到loginCommand的入?yún)⒅小?
請(qǐng)求響應(yīng)方法可以返回一個(gè)ModelAndView,或者直接返回一個(gè)字符串,SoringMVC會(huì)解析并轉(zhuǎn)向目標(biāo)響應(yīng)頁(yè)面。
ModelAndView對(duì)象既包括視圖信息,又包括視圖渲染所需要的模型數(shù)據(jù)信息。
剛才提到的LoginCommand對(duì)象是一個(gè)POJO,沒(méi)有繼承特定的父類或者實(shí)現(xiàn)特定的接口。 LoginCommand僅僅包括用戶名和密碼這兩個(gè)屬性(和請(qǐng)求的用戶密碼參數(shù)名稱一樣)
package com.xgj.web;public class LoginCommand {private String userName;private String password;public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;} }Spring MVC 配置文件
編寫(xiě)好LoginCommand后,需要在smart-servlet.xml中聲明該控制器,掃描Web路徑,指定SpringMVC的視圖解析器。
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <!-- 掃描web包,應(yīng)用Spring的注解 --><context:component-scan base-package="com.xgj.web"/> <!-- 配置視圖解析器,將ModelAndView及字符串解析為具體的頁(yè)面 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"p:viewClass="org.springframework.web.servlet.view.JstlView" p:prefix="/WEB-INF/jsp/"p:suffix=".jsp" /></beans>ModelAndView的解析配置
在LoginController 控制器類中
..... return new ModelAndView("login", "error", "密碼錯(cuò)誤,請(qǐng)重新登錄"); ...... return new ModelAndView("main");ModelAndView的第一個(gè)參數(shù)代表視圖的邏輯名,第二、第三個(gè)參數(shù)分別為數(shù)據(jù)模型名稱和數(shù)據(jù)模型對(duì)象,數(shù)據(jù)模型對(duì)象將以數(shù)據(jù)模型名稱為參數(shù)名放置到request的屬性中
那么Spring是如何將視圖邏輯名稱解析為具體的視圖頁(yè)面呢?
同樣的 我們需要在smart-servlet中提供一個(gè)定義解析規(guī)則的bean
Spring MVC為視圖名到具體視圖的映射提供了需要到中方法,這里我們使用 InternalResourceViewResolver。
它通過(guò)為視圖邏輯名添加前后綴的方式進(jìn)行進(jìn)行解析。
比如 邏輯視圖名為 “l(fā)ogin”,將根據(jù)配置解析為/WEB-INF/jsp/login.jsp ,同樣的 邏輯視圖名為“main”,將解析為/WEB-INF/jsp/main.jsp
JSP視圖頁(yè)面
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html><head><title>登錄</title></head><body><c:if test="${!empty error}"><font color="red"><c:out value="${error}" /></font></c:if> <form action="<c:url value="loginCheck.html"/>" method="post">用戶名:<input type="text" name="userName"><br>密 碼:<input type="password" name="password"><br><input type="submit" value="登錄" /><input type="reset" value="重置" /></form></body> </html>JSTL標(biāo)簽中中引用了 error變量正是LoginController中返回的 return new ModelAndView(“l(fā)ogin”, “error”, “密碼錯(cuò)誤,請(qǐng)重新登錄”); 對(duì)象所聲明的error參數(shù)。
由于我們將 jsp放在了 WEB-INF/jsp目錄下,所以無(wú)法通過(guò)URL直接訪問(wèn),必須通過(guò)LoginController控制類中標(biāo)注了@RequestMapping(value = “/index.html”)的loginPage()方法進(jìn)行轉(zhuǎn)發(fā)。
main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>論壇</title> </head> <body>${user.username},歡迎您進(jìn)入論壇,您當(dāng)前積分為${user.credit}; </body> </html>main頁(yè)面 僅適用JSTL標(biāo)簽顯示一條歡迎信息。
運(yùn)行Web應(yīng)用
第一種方式 maven插件部署web應(yīng)用服務(wù)器
這里我們使用jetty服務(wù)器插件,首先在pom.xml中配置插件
<!-- jetty插件 --><plugin><groupId>org.mortbay.jetty</groupId><artifactId>maven-jetty-plugin</artifactId><version>6.1.25</version><configuration><connectors><connector implementation="org.mortbay.jetty.nio.SelectChannelConnector"><port>8080</port><maxIdleTime>60000</maxIdleTime></connector></connectors><contextPath>/${project.artifactId}</contextPath><scanIntervalSeconds>5</scanIntervalSeconds></configuration></plugin>運(yùn)行: 選中項(xiàng)目,右鍵 Run as - Run Jetty
http://localhost:8080/hello-spring4/index.html
第二種方式 部署到容器中
在eclipse中配置 Tomcat 容器,然后
選中項(xiàng)目,右鍵 Run as - Run on Server
同樣可以達(dá)到訪問(wèn)的目的
總結(jié)
雖然是一個(gè)小的登錄模塊,重點(diǎn)是體會(huì)開(kāi)發(fā)的過(guò)程,使用Spring MVC、SpringJDBC、Spring聲明式事務(wù)等技術(shù)。
附件 pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.xgj</groupId><artifactId>hello-spring4</artifactId><packaging>war</packaging><version>0.0.1-SNAPSHOT</version><name>hello-spring4 Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>${servlet.version}</version><scope>provided</scope></dependency><!-- spring 依賴 --><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>commons-dbcp</groupId><artifactId>commons-dbcp</artifactId><version>${commons-dbcp.version}</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>${aspectj.version}</version></dependency><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>${testng.version}</version><scope>test</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version><scope>test</scope></dependency><!-- 添加oracle jdbc driver --><dependency><groupId>com.oracle</groupId><artifactId>ojdbc6</artifactId><version>${oracle.version}</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>${jstl.version}</version></dependency></dependencies><build><finalName>hello-spring4</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><source>1.7</source><target>1.7</target></configuration></plugin><!-- jetty插件 --><plugin><groupId>org.mortbay.jetty</groupId><artifactId>maven-jetty-plugin</artifactId><version>6.1.25</version><configuration><connectors><connector implementation="org.mortbay.jetty.nio.SelectChannelConnector"><port>8080</port><maxIdleTime>60000</maxIdleTime></connector></connectors><contextPath>/${project.artifactId}</contextPath><scanIntervalSeconds>5</scanIntervalSeconds></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.17</version><configuration><parallel>methods</parallel><threadCount>10</threadCount></configuration></plugin></plugins></build><properties><file.encoding>UTF-8</file.encoding><spring.version>4.3.9.RELEASE</spring.version><servlet.version>3.1.0</servlet.version><aspectj.version>1.8.1</aspectj.version><commons-dbcp.version>1.4</commons-dbcp.version><jetty.version>8.1.8.v20121106</jetty.version><slf4j.version>1.7.5</slf4j.version><testng.version>6.8.7</testng.version><oracle.version>11.2.0.4.0</oracle.version><jstl.version>1.2</jstl.version></properties> </project>總結(jié)
以上是生活随笔為你收集整理的Spring-Spring MVC + Spring JDBC + Spring Transaction + Maven 构建web登录模块的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Maven-EclipseEE使用Mav
- 下一篇: Spring-IoC容器