1. 程式人生 > >Spring 入門例項 簡易登入系統(精通Spring+4.x++企業應用開發實戰 學習筆記一)

Spring 入門例項 簡易登入系統(精通Spring+4.x++企業應用開發實戰 學習筆記一)

論壇登入模組

這裡寫圖片描述

在持久層有兩個DAO類,分別是UserDao和LoginLogDao,在業務層對應一個業務類UserService,在展現層擁有一個LoginController類和兩個JSP頁面,分別是登入頁面login.jsp和登入成功頁面main.jsp

DAO為資料訪問物件(Data Access Object DAO)設計模式,以便將低級別的資料訪問邏輯與高級別的業務邏輯分離。
這裡寫圖片描述
這裡寫圖片描述

下載jdk和maven,mysql並配置好環境變數
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

1.建立庫表

(1)登入資料庫

mysql -u root -p 

設定密碼格式為

set password
for 使用者名稱@localhost = password('新密碼');

(2)建立例項對應的資料庫

DROP DATABASE IF EXISTS sampledb;
CREATE DATABASE sampledb DEFAULT CHARACTER SET utf8;
USE sampledb;

預設字符集採用UTF-8
(3)建立例項所用的兩張表
建立使用者表

CREATE TABLE t_user(
  user_id INT AUTO_INCREMENT PRIMARY KEY,
  user_name VARCHAR(30),
  credits INT
, password VARCHAR(32), last_visit datetime, last_ip VARCHAR(23) )ENGINE=InnoDB;

建立用於使用者登入日誌表

CREATE TABLE t_login_log(
  login_log_id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT,
  ip VARCHAR(23),
  login_datetime datetime
)ENGINE=InnoDB;  

InnoDB引擎支援事務

(4)初始化一條資料

INSERT INTO t_user (user_name,password) VALUES
('admin','123456');
COMMIT;

也可以直接執行指令碼檔案sampledb.sql

2.在IDEA中建立一個專案

其中的pom.xml配置從下載的原始碼中直接使用

類包以分層的方式進行組織
這裡寫圖片描述

3.持久層

領域物件(Domain Object)也稱為實體類,它代表了業務的狀態,且貫穿表現層,業務層和持久層,並最終被持久化到資料庫中。

持久層的主要工作就是從資料庫中載入資料並例項化領域物件,或者將領域物件持久化到資料庫表中。

“持久層”,也就是在系統邏輯層面上,專著於實現資料持久化的一個相對獨立的領域(Domain),是把資料儲存到可掉電式儲存裝置中。持久層是負責向(或者從)一個或者多個數據儲存器中儲存(或者獲取)資料的一組類和元件。
這個層必須包括一個業務領域實體的模型(即使只是一個元資料模型)。
這裡寫圖片描述

使用者領域物件

使用者資訊領域物件可以看成t_user表的物件映像,每個欄位對應一個物件屬性。
有3類資訊:userName/password,積分(credits)和最後一次登入的資訊(lastIp,lastVisit)

package com.smart.domain;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable{
    private int userId;
    private String userName;
    private String password;
    private int credits;
    private String lastIp;
    private Date lastVisit;
    //省略get/set方法
}

登入日誌領域物件

使用者每次成功登入後都會記錄一條登入日誌
3類資訊:使用者ID,登入IP和登入時間。(一般情況下還需退出時間)

package com.smart.domain;

import java.io.Serializable;
import java.util.Date;

public class LoginLog implements Serializable {
    private int loginLogId;
    private int userId;
    private String ip;
    private Date loginDate;
   //省略get/set方法
}

UserDao

包括三個方法:
①getMatchCount():
根據使用者名稱和密碼獲取匹配的使用者數。等於1表示使用者名稱/密碼正確,0則錯誤(這是最簡單的使用者身份認證方法,實際中還需很多安全策略)
②findUserByUserName:
根據使用者名稱獲取User物件
③updateLoginInfo():
更新使用者積分,最後登入IP及最後登入時間

package com.smart.dao;


import com.smart.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Repository;
import sun.security.krb5.internal.PAData;

import java.sql.ResultSet;
import java.sql.SQLException;

@Repository  //通過Spring註解一個DAO
public class UserDao {
    private JdbcTemplate jdbcTemplate;  //資料庫相關操作

    private  final static String MATCH_COUNT_SQL = " SELECT count(*) FROM t_user  " +
            " WHERE user_name =? and password=? ";
    private  final static String UPDATE_LOGIN_INFO_SQL = " UPDATE t_user SET " +
            " last_visit=?,last_ip=?,credits=?  WHERE user_id =?";

    @Autowired  //自動注入JdbcTemplate的Bean,@Autowired是一種函式,可以對成員變數、方法和建構函式進行標註,來完成自動裝配的工作
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
        this.jdbcTemplate=jdbcTemplate;
    }
    //返回1表示表示使用者名稱/密碼正確,0則錯誤
    public int getMatchCount(String userName, String password) {
        //第一個引數是傳入的mysql語句,第二個是參入的引數,第三個引數指定需要返回什麼型別
        return jdbcTemplate.queryForObject(MATCH_COUNT_SQL, new Object[]{userName, password}, Integer.class);
    }

    public User findUserByUserName(final String userName){
        String sqlStr = " SELECT user_id,user_name,credits "
                + " FROM t_user WHERE user_name =? ";
        final User user=new User();
        jdbcTemplate.query(sqlStr, new Object[]{userName},
                //匿名類方式實現的回撥函式,RowCallbackHandler只處理單行結果
                new RowCallbackHandler() {   //rs是查詢後返回的結果集
                    public void processRow(ResultSet rs) throws SQLException {
                        user.setUserId(rs.getInt("user_id"));
                        user.setUserName(userName);
                        user.setCredits(rs.getInt("credits"));
                    }
                });
        return  user;
    }

    public void updateLoginInfo(User user) {
        jdbcTemplate.update(UPDATE_LOGIN_INFO_SQL, new Object[] { user.getLastVisit(),
                user.getLastIp(),user.getCredits(),user.getUserId()});
    }
}

@Repository用於標註資料訪問元件,即DAO元件

@Autowired是一種函式,可以對成員變數、方法和建構函式進行標註,來完成自動裝配的工作

Spring JDBC通過一個模板類JdbcTemplate封裝了樣板式的程式碼,使用者通過模板類可以輕鬆地完成大部分資料訪問操作。

query(String sqlStr,Object[] args,RowCallbackHandler rch)有三個引數
①sqlStr:SQL語句,允許使用帶”?”的佔位符
②args:SQL語句中對應位置的引數陣列
③RowCallbackHandler :查詢結果的處理回撥介面,該介面有一個方法processRow(ResultSet rs),負責將查詢的結果從ResultSet裝載到類似於領域物件的物件例項中

在DAO中編寫SQL語句中,通常將SQL語句寫在靜態變數中,如果太長則採用多行字串的方式進行構造。在每行SQL語句的句尾和句前都加一個空格,就可以避免分行SQL組合後的錯誤。

LoginLogDao

package com.smart.dao;

import com.smart.domain.LoginLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository //通過Spring註解一個DAO
public class LoginLogDao {
    private JdbcTemplate jdbcTemplate;

    //儲存登入日誌SQL
    private final static String INSERT_LOGIN_LOG_SQL=
            "INSERT INTO t_login_log(user_id,ip,login_datetime) VALUES(?,?,?)";

    public void insertLoginLog(LoginLog loginLog){
        Object[] args={loginLog.getUserId(),loginLog.getIp(),loginLog.getLoginDate()};
        jdbcTemplate.update(INSERT_LOGIN_LOG_SQL,args);
    }

    @Autowired
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
        this.jdbcTemplate=jdbcTemplate;
    }
}

在Spring中裝配DAO

樣板式的操作都被JdbcTemplate封裝起來了,JdbcTemplate本身需要一個DataSource,這樣它就可以根據需要從DataSource中獲取或返回連線。

UserDao和LoginLogDao都提供了一個帶@Autowired註解的JdbcTemplate變數,所以我們必須先宣告一個數據源,然後定義一個JdbcTemplate Bean,通過Spring容器上下文自動繫結機制進行Bean的注入。

在src\resources(Maven工程中,資原始檔統一放置在resources資料夾中)目錄下建立一個名為smart-context.xml的Spring配置檔案

<?xml version="1.0" encoding="UTF-8" ?>
<!-- ①引入Spring的多個Schema空間的格式定義檔案和引入aop及tx名稱空間鎖對應的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.0.xsd
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- ②掃描類包,將標註Spring註解的類自動轉化Bean,同時完成Bean的注入 -->
    <context:component-scan base-package="com.smart.dao"/>
    <context:component-scan base-package="com.smart.service"/>

    <!--③定義一個使用DBCP 配置資料來源 -->
    <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" />

    <!-- ④配置Jdbc模板  -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
        p:dataSource-ref="dataSource" />

    <!--⑤ 配置事務管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        p:dataSource-ref="dataSource" />

    <!-- ⑥通過AOP配置提供事務增強,讓service包下所有Bean的所有方法擁有事務 -->
    <aop:config proxy-target-class="true">
        <aop:pointcut id="serviceMethod"
            expression="(execution(* com.smart.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>

在②中,掃描類包,將標註Spring註解的類自動轉化Bean,同時完成Bean的注入
這一步中使用Spring的 掃描指定類包中的所有類,這樣在類中定義的Spring註解(如Repository,@Autowired等)才能產生作用

在③配置資料來源
使用dbcp定義了一個數據源,驅動器類為com.mysql.jdbc.Driver,指定MySQL資料庫的服務埠(非預設3306時)

在④配置了JdbcTemplate Bean,將③處宣告的DataSource注入JdbcTemplate中,而這個JdbcTemplare將通過@Autowired自動注入LoginDao和UserDao的Bean中,可見Spring可以很好地將註解配置和XML配置統一起來。

業務層

在此例項中僅有一個業務類UserService,負責將持久層的UserDao和LoginlogDao組織起來,完成使用者/密碼認證,登入日誌記錄等操作。

有3個業務辦法:
①hasMatchUser
用於檢查使用者名稱/密碼的正確性
②findUserByUserName
以使用者名稱來條件載入User物件
③loginSuccess
在使用者登入成功後呼叫,更新使用者最後登入時間和IP資訊,同時記錄使用者登入日誌

package com.smart.service;

import com.smart.dao.LoginLogDao;
import com.smart.dao.UserDao;;

import com.smart.domain.LoginLog;
import com.smart.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service  //將UserService標註為一個服務層的Bean
public class UserService {
    private UserDao userDao;
    private LoginLogDao loginLogDao;

    @Autowired // 注入userDao的Bean
    public void  setUserDao(UserDao userDao){
        this.userDao=userDao;
    }

    @Autowired //注入loginLogDao的Bean
    public void setLoginLogDao(LoginLogDao loginLogDao){
        this.loginLogDao=loginLogDao;
    }

    public boolean hasMatchUser(String userName,String paasword){
        int matchCount=userDao.getMatchCount(userName,paasword);
        return matchCount>0;
    }

    public User findUserByUserName(String userName){
        return userDao.findUserByUserName(userName);
    }

    @Transactional  //標註事務註解,讓該方法執行在事務環境中,否則該方法在事務中無法執行
    public void loginSuccess(User user){
        user.setCredits(5+user.getCredits());
        LoginLog loginLog=new LoginLog();
        loginLog.setUserId(user.getUserId());
        loginLog.setIp(user.getLastIp());
        loginLog.setLoginDate(user.getLastVisit());
        userDao.updateLoginInfo(user);
        loginLogDao.insertLoginLog(loginLog);
    }
}

loginSuccess()方法將兩個Dao組織起來,共同完成一個事務性的資料操作。

loginSuccess()方法根據入參user物件構造出LoginLog物件並將user.credits遞增5,即每登入一次賺錢5個積分,然後呼叫userDao更新到t_user中,再呼叫loginLogDao向t_login_log表中新增一條記錄。

在Spring中裝配Service

在上面中smart-context.xml配置檔案中,可以發現
在①中,在beans宣告處新增aop和tx名稱空間定義檔案的說明,這樣,在配置檔案中就可以使用這兩個空間下的配置標籤了

在②中將service新增到上下文掃描路徑中,以便使service包中類的Spring註解生效

在⑤中,配置的事務管理器,負責宣告式事務的管理,需要應用dataSource Bean

在⑥通過AOP配置提供事務增強,讓service包下所有Bean的所有方法擁有事務
通過aop及tx名稱空間的語法,以AOP的方式為service包下所有類的所有標註@Transactional註解的方法都添加了事務增強,它們都將工作在事務環境中

單元測試

這裡使用的測試採用TestNG框架

在test目錄下,建立com.smart.service,並建立UserService對應的測試類UserServiceTest

package com.smart.service;

import java.util.Date;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.testng.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import com.smart.domain.User;
import static org.testng.Assert.*;

@ContextConfiguration("classpath*:/smart-context.xml") //啟動Spring容器,用於指定Spring的配置檔案
public class UserServiceTest extends AbstractTransactionalTestNGSpringContextTests {
    @Autowired
    private UserService userService;

    @Test //標註測試方法
    public void testHasMatchUser(){
        boolean b1=userService.hasMatchUser("admin","123456");
        boolean b2=userService.hasMatchUser("admin","1111");
        assertTrue(b1);
        assertTrue(!b2);
    }

    @Test
    public void testFindUserByUserName()throws  Exception{
        for(int i=0;i<100;i++){
            User user=userService.findUserByUserName("admin");
            assertEquals(user.getUserName(),"admin");
        }
    }

    @Test
    public void testAddLoginLog(){
        User user = userService.findUserByUserName("admin");
        user.setUserId(1);
        user.setUserName("admin");
        user.setLastIp("192.168.12.7");
        user.setLastVisit(new Date());
        userService.loginSuccess(user);
    }
}

UserServiceTest通過測試基類AbstractTransactionalTestNGSpringContextTests來啟動測試執行器。
@ContextConfiguration用於指定Spring的配置檔案

右鍵Run UserServiceTest可以看到3個業務方法已成功執行

展現層

配置Spring MVC框架

對web.xml(放在/WEB-INF目錄下)進行配置,以便Web容器啟動時能自動啟動Spring容器

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
         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_2_5.xsd">
 <!-- ①從類路徑下載入Spring配置檔案,classpath關鍵字特指類路徑下載入-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:smart-context.xml</param-value>
    </context-param>
    <!-- ②負責啟動Spring容器的監聽器,將應用①處的context引數獲得Spring配置檔案的地址-->
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
   <!-- 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>
   <!--Spring MVC處理的URL-->
    <servlet-mapping> <!-- ④-->
        <servlet-name>smart</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
</web-app>

在①處通過Web容器引數指定Spring配置檔案的地址
在②處指定Spring所提供的ContextLoaderListener的Web容器監聽器,該監聽器在Web容器啟動時自動執行,它會根據contextConfigLocation Web容器引數獲取Spring配置檔案,並啟動Spring容器。

注意,需要將log4J.propertis日誌配置檔案放置在類路徑下,以便日誌引擎自動生效

log4j.rootLogger=INFO,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n

在③處聲明瞭一個Servlet,名為smart,則在/WEB-INF目錄下必須提供一個名為smart-servlet/xml的Spring MVC配置檔案,Spring MVC的Servlet會自動將smart-servlet.xml檔案和Spring的其他配置檔案(smart-dao.xml,smart-service.xml)進行拼裝。

在④處對這個Servlet的URL路徑對映進行定義,讓所有以.html為字尾的URL都能被smart Servlet截獲,進而轉由Spring MVC框架進行處理。

請求被Spring MVC截獲後,首先根據請求的URL查詢到目標的處理控制器,並將請求引數封裝“命令”物件一起傳給控制器處理;然後,控制器呼叫Spring容器中的業務Bean完成業務處理工作並返回結果檢視。

處理登入請求

POJO(Plain Ordinary Java Object,簡單的Java物件)控制器類
LoginController負責處理登入請求,完成登入業務,並根據登入成功與否轉向成功或失敗頁面

package com.smart.web;

import com.smart.domain.User;
import com.smart.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@Controller  //標註為一個Spring MVC的Controller
public class LoginController {
    private UserService userService;

    //②負責處理/index.html的請求
    @RequestMapping(value="/index.html")
    public String loginPage(){
        return "login";
    }

    //③負責處理/loginCheck.html的請求
    @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","使用者名稱或密碼錯誤");
        }else{
            User user=userService.findUserByUserName(loginCommand.getUserName());
            user.setLastIp(request.getLocalAddr());
            user.setLastVisit(new Date());
            userService.loginSuccess(user);
            request.getSession().setAttribute("user",user);//將user放入Session域
            return new ModelAndView("main");
        }
    }

    @Autowired
    public void setUserService(UserService userService){
        this.userService=userService;
    }
}

通過@Controller註解可以將任何一個POJO的類標註為Spring MVC的控制器處理HTTP請求。

通過@RequestMapping指定方法如何對映請求路徑

請求引數會根據引數名稱預設契約自動繫結到相應方法的入參中。如loginCheck(HttpServletRequest request, LoginCommand loginCommand)方法中,請求引數會按匹配繫結到loginCommand的入參中。

請求響應方法可以返回一個ModelAndView,或直接返回一個字串,Spring MVC會解析並轉向目標響應頁面。

LoginCommand是一個POJO,僅包含使用者/密碼兩個屬性

package com.smart.web;

public class LoginCommand {
    private String userName;
    private String password;
    //get/set方法
}

Spring MVC配置檔案
編寫好LoginCommand後,需要在smart-servlet.xml(在/WEB-INF目錄下)宣告該控制器,掃描Web路徑,指定Spring MVC的檢視解析器

<?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/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <!-- ①掃描web包,應用Spring的註解 -->
    <context:component-scan base-package="com.smart.web"/>

    <!-- ②配置檢視解析器,將ModelAndView及字串解析為具體的頁面 -->
    <!-- 通過prefix指定在檢視名前所新增的字首,通過suffix指定在檢視名後新增的字尾
    <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>

在LoginController中的③處,控制器根據登入處理結果分別返回ModelAndView(“login”,”error”,”使用者名稱或密碼錯誤”)和ModelAndView(“main”)。
第一個引數代表檢視的邏輯名,第二為資料模型名稱,第三為資料模型物件,資料模型物件將以資料模型名稱為引數放入request的屬性中

Spring MVC為檢視名到具體檢視的對映提供了許多可選擇的方法,此使用InternalResourceViewResolver,它通過為檢視邏輯名新增字首,字尾的方式進行解析。
如檢視邏輯名為”login”,將解析為/WEB-INF/jsp/login.jsp

JSP檢視頁面

登入頁面為login.jsp和歡迎頁面為main.jsp

登入頁面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>

login.jsp有兩個作用,即作為登入頁面,也作為登入失敗後的響應頁。

在<c:if test=”${!empty error}”>處使用JSTL標籤對登入錯誤返回的資訊進行處理。在JSTL標籤中引用了error變數,這個變數正是ModelAndView(“login”,”error”,”使用者名稱或密碼錯誤”)物件所宣告的error引數

在<form action=”<c:url value=”loginCheck.html”/>” method=”post”>中login.jsp的登入表單提交到/loginController.html,而loginController中@RequestMapping(value = “/loginCheck.html”)負責處理/loginCheck.html的請求。
JSTL標籤會在URL前自動加上應用部署根目錄。假設應用部署在網站的bbt目錄下,則<c:url>標籤將輸出/bbt/loginController.html

由於login.jsp放在WEB-INF/jsp目錄中,無法直接通過URL進行呼叫,所以由LoginController控制類中標註了 @RequestMapping(value=”/index.html”)的loginPage()進行轉發

歡迎頁面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},歡迎您進入論壇,您當前積分為${user.credits};<!-- ① -->
</body>
</html>

①處訪問Session域中的user物件,顯示使用者名稱和積分資訊

執行Web應用

基於Maven工程,執行Web應用有兩種方式:第一種方式是在IDE工具中配置Web應用伺服器,第二種方式是在pom.xml檔案中配置Web應用伺服器外掛
在pom.xml中

<build>
        <plugins>
            <!-- 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>8000</port>
                            <maxIdleTime>60000</maxIdleTime>
                        </connector>
                    </connectors>
                    <contextPath>/bbs</contextPath>
                    <scanIntervalSeconds>0</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>

Jetty外掛常用配置選項說明:
在Connenctors中配置Connector物件,包含Jetty的監聽埠。如果不配置聯結器,則預設監聽埠會被設定為8080.

contextPath可選,用於配置Web應用上下文。如果不配置此項,則預設上下文采用pom.xml中設定的<artfacId>名稱,本例將上下文設定為bbs

overridWebXml可選,是一個應用於Web應用的web.xml的備用web.xml檔案。這個檔案可以放在任何地方。使用者可以根據不同的環境(如測試,開發 等),利用它增加或修改一個web.xml配置。

scanIntervalSeconds 可選,在設定間隔內檢查Web應用是否有變化,有變則自動熱部署。預設為0,表示禁用熱部署,任意一個大於0的數字都表示啟用

systemPropertie可選,允許使用者在設定一個外掛的執行操作時配置系統屬性

點選
這裡寫圖片描述中的這裡寫圖片描述
在Plugins下會自動出現安裝的Jetty外掛。

雙擊jetty:run或jetty:run-exploded,將以執行模式啟動Jetty伺服器,也可以使用Debug執行。

下圖是論壇登入的首頁面
這裡寫圖片描述
密碼或使用者名稱錯誤時
這裡寫圖片描述
這裡輸入admin/123456(之前已先初始化),登入到歡迎頁面中,如圖
這裡寫圖片描述

通過檢視MySQL,可以發現數據庫發生了變化
這裡寫圖片描述