黑馬客戶管理系統 1系統概述 1.1系統功能介紹 本系統後臺使用SSM框架編寫,前臺頁面使用當前主流的Bootstrap和jQuery框架完成頁面信息展示功能(關於Bootstrap的知識,有興趣的讀者可參考黑馬程式員編著的《響應式Web開發項目教程》)。系統中主要實現了兩大功能模塊:用戶登錄模塊和 ...
黑馬客戶管理系統
1系統概述
1.1系統功能介紹
本系統後臺使用SSM框架編寫,前臺頁面使用當前主流的Bootstrap和jQuery框架完成頁面信息展示功能(關於Bootstrap的知識,有興趣的讀者可參考黑馬程式員編著的《響應式Web開發項目教程》)。系統中主要實現了兩大功能模塊:用戶登錄模塊和客戶管理模塊,這兩個模塊的主要功能如圖18-1所示。
1.2系統架構設計
本系統根據功能的不同,項目結構可以劃分為以下幾個層次。
· 持久對象層(也稱持久層或持久化層):該層由若幹持久化類(實體類)組成。
· 數據訪問層(DAO層):該層由若幹DAO介面和MyBatis映射文件組成。介面的名稱統一以Dao結尾,且MyBatis的映射文件名稱要與介面的名稱相同。
· 業務邏輯層(Service層):該層由若幹Service介面和實現類組成。在本系統中,業務邏輯層的介面統一使用Service結尾,其實現類名稱統一在介面名後加Impl。該層主要用於實現系統的業務邏輯。
· Web表現層:該層主要包括Spring MVC中的Controller類和JSP頁面。Controller類主要負責攔截用戶請求,並調用業務邏輯層中相應組件的業務邏輯方法來處理用戶請求,然後將相應的結果返回給JSP頁面。為了讓讀者更清晰地瞭解各個層次之間的關係,下麵通過一張圖來描述各個層次的關係和作用,如圖18-2所示。
1.3文件組織結構
在正式講解項目的編寫之前,先來瞭解項目中所涉及的包文件、配置文件以及頁面文件等在項目中的組織結構,如圖18-3所示。
1.4系統開發及運行環境
BOOT客戶管理系統開發環境如下。
· 操作系統:Windows
· Web伺服器:Tomcat 8.0
· Java開發包:JDK8
· 開發工具:Eclipse Java EE IDE for Web Developers
· 資料庫:MySQL 5.5
· 瀏覽器:火狐或IE 8.0以上版本
2資料庫設計
本系統中主要涉及用戶登錄和客戶管理功能,因此在系統中會涉及系統用戶表和客戶信息表。除此之外,客戶信息中的客戶來源和所屬行業等內容是根據數據字典表中的信息查詢出的,所以還會涉及一個數據字典表。這3張表的表結構如表18-1、表18-2和表18-3所示。
3系統環境搭建
3.1準備所需JAR包
由於本系統使用的是SSM框架開發,因此需要準備這三大框架的JAR包。除此之外,項目中還涉及資料庫連接、JSTL標簽等,所以還要準備其他JAR包。整個系統所需要準備的JAR共計35個,具體如下所示。
1.Spring框架所需的JAR包(10個)
主要包括4個核心模塊JAR, AOP開發使用的JAR, JDBC和事務的JAR。
· aopalliance-1.0.jar
· aspectjweaver-1.8.10.jar
· spring-aop-4.3.6.RELEASE.jar
· spring-aspects-4.3.6.RELEASE.jar
· spring-beans-4.3.6.RELEASE.jar
· spring-context-4.3.6.RELEASE.jar
· spring-core-4.3.6.RELEASE.jar
· spring-expression-4.3.6.RELEASE.jar
· spring-jdbc-4.3.6.RELEASE.jar
· spring-tx-4.3.6.RELEASE.jar
2.Spring MVC框架所需要的JAR包(2個)
· spring-web-4.3.6.RELEASE.jar
· spring-webmvc-4.3.6.RELEASE.jar
3.MyBatis框架所需的JAR包(13個)主要包括核心包mybatis-3.4.2.jar,以及其解壓文件夾中lib目錄下的所有JAR。
· ant-1.9.6.jar
· ant-launcher-1.9.6.jar
· asm-5.1.jar· cglib-3.2.4.jar
· commons-logging-1.2.jar
· javassist-3.21.0-GA.jar
· log4j-1.2.17.jar
· log4j-api-2.3.jar
· log4j-core-2.3.jar
· mybatis-3.4.2.jar
· ognl-3.1.12.jar
· slf4j-api-1.7.22.jar
· slf4j-log4j12-1.7.22.jar
4.MyBatis與Spring整合的中間JAR(1個)
· mybatis-spring-1.3.1.jar
5.資料庫驅動JAR包(1個)
· mysql-connector-java-5.1.40-bin.jar
6.數據源dbcp所需JAR包(2個)
· commons-dbcp2-2.1.1.jar
· commons-pool2-2.4.2.jar
7.JSTL標簽庫JAR包(2個)
· taglibs-standard-impl-1.2.5.jar
· taglibs-standard-spec-1.2.5.jar
8.Jackson框架所需JAR包(3個)
· jackson-annotations-2.8.6.jar
· jackson-core-2.8.6.jar
· jackson-databind-2.8.6.jar
9.Java工具類JAR(1個)
· commons-lang3-3.4.jar
上面所需要準備的JAR包(除JSTL的2個JAR包和commons-lang3-3.4.jar外),都是本書前面章節所使用過的。讀者在學習本章時,可以直接下載項目源碼,並使用源碼中的JAR包。
小提示
本書中使用的JSTL版本是1.2.5,此版本中需要引入的JAR包為taglibs-standard-spec-1.2.5.jar(相當於之前的jstl.jar,屬於介面定義類)和taglibs-standard-impl-1.2.5.jar jar(相當於之前的standard.jar,屬於實現類)。這兩個JAR包可以通過網址“http://tomcat.apache.org/download-taglibs.cgi#Standard-1.2.5”下載得到。
3.2準備資料庫資源
通過MySQL 5.5 Command Line Client登錄資料庫後,創建一個名稱為boot_crm的資料庫,並選擇該資料庫。通過SQL命令將本書資源中所提供的boot_crm.sql文件導入到boot_crm資料庫中,即可導入本系統所使用的全部數據,其具體實現SQL命令如下。
(1)創建資料庫。
create database boot_crm;
(2)選擇所創建的資料庫。
use boot_crm;
(3)導入資料庫文件,這裡假設該文件在F盤的根目錄下,其導入命令如下。
source F:\boot_crm.sql;
除此之外,還可以通過其他客戶端軟體導入sql文件,如SQLyog等。
創建crm資料庫,執行sql
3.3準備項目環境
1創建項目,引入JAR包
在Eclipse中,創建一個名稱為boot-crm的Web項目,將系統所準備的全部JAR包複製到項目的lib目錄中,併發布到類路徑下。
2編寫配置文件
(1)在項目目錄下創建一個源文件夾config,併在config文件夾下分別創建資料庫常量配置文件、Spring配置文件、MyBatis配置文件、log4j配置文件、資源配置文件以及Spring MVC配置文件。其中log4j配置文件log4j.properties、資料庫常量配置文件jdbc.properties與MyBatis配置文件mybatis-config.xml的配置與第17章講解整合時的配置代碼基本相同(註意修改資料庫名稱與包名),這裡將不再重覆講解。其他3個配置文件的代碼分別如文件18-1、文件18-2和文件18-3所示。
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 別名 -->
<typeAlises>
<package name="com.itheima.core.pojo"/>
</typeAlises>
</configuration>
jdbc.properties
配置資料庫信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/crm?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
log4j.properties
配置日誌信息
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
applicationContext.xml
需要配置:
載入properties文件,數據源,SqlSessionFactory,Mapper掃描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
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-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/aop http://www.springframework.org/schema/aop/spring-aop-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/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 配置 讀取properties文件 jdbc.properties -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 配置 數據源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 資料庫驅動 -->
<property name="driverClassName" value="${jdbc.driver}" />
<!-- 連接資料庫的url -->
<property name="url" value="${jdbc.url}" />
<!-- 連接資料庫的用戶名 -->
<property name="username" value="${jdbc.username}" />
<!-- 連接資料庫的密碼 -->
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 配置Mybatis的工廠 SqlSessionFactory -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 設置MyBatis核心配置文件所在位置 -->
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
<!-- 設置數據源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 數據源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 傳播行為 -->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
<tx:method name="query*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 切麵 -->
<aop:config>
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* cn.itcast.crm.service.*.*(..))" />
</aop:config>
<!-- 配置Mapper掃描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 設置Mapper掃描包 -->
<property name="basePackage" value="com.itheima.crm.mapper" />
</bean>
<!-- 配置Service掃描 -->
<context:component-scan base-package="com.itheima.crm.service" />
</beans>
上述代碼與上一章整合時的配置文件代碼有所不同的是增加了事務傳播行為以及切麵的配置。在事務的傳播行為中,只有查詢方法的事務為只讀,添加、修改和刪除的操作必須納入事務管理。
resource.properties
#客戶來源
CUSTOMER_FROM_TYPE=002
#客戶行業
CUSTOMER_INDUSTRY_TYPE=001
#客戶級別
CUSTOMER_LEVEL_TYPE=006
上述配置代碼分別表示客戶來源、所屬行業和客戶級別,其值對應的是數據字典表中dict_type_code欄位的值。
springmvc-config.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 配置Controller掃描 -->
<context:component-scan base-package="com.itheima.crm.controller" />
<!-- 載入屬性文件 controller需要的配置信息 -->
<context:property-placeholder location="classpath:resource.properties" />
<!-- 配置註解驅動:處理器映射器和適配器-->
<mvc:annotation-driven />
<!-- 對靜態資源放行,此配置中的文件,將不被前端控制器攔截-->
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/fonts/" mapping="/fonts/**"/>
<!-- 另外一種方式 解決靜態資源無法被springMVC處理的問題 -->
<mvc:default-servlet-handler />
<!-- 配置視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 首碼 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 尾碼 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
上述代碼除配置了需要掃描的包、註解驅動和視圖解析器外,還增加了載入屬性文件和訪問靜態資源的配置。
(2)在web.xml中,配置Spring的監聽器、編碼過濾器和SpringMVC的前端控制器等信息,如文件18-4所示。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>ssm</display-name>
<!-- 系統預設頁面-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 配置spring -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext.xml</param-value>
</context-param>
<!-- 配置監聽器載入spring -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置編碼過濾器,解決post的亂碼問題 -->
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置SpringMVC前端核心控制器 -->
<servlet>
<servlet-name>crm</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<!-- 配置springmvc什麼時候啟動,參數必須為整數 -->
<!-- 如果為0或者大於0,則springMVC隨著容器啟動而啟動 -->
<!-- 如果小於0,則在第一次請求進來的時候啟動 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>crm</servlet-name>
<!-- 所有的請求都進入springMVC -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
3.引入頁面資源將項目運行所需要的CSS文件、字體、圖片、JS、自定義標簽文件和JSP文件按照圖18-3中的結構引入到項目中。
至此,開發系統前的環境準備工作就已經完成。此時如果將項目發佈到Tomcat伺服器並訪問項目首頁地址http://localhost:8080/boot-crm/index.jsp,如圖18-4所示。
從圖18-4可以看出,訪問系統首頁時,頁面所展示的是系統登錄頁面。在下一節中,我們將對系統的登錄功能編寫進行詳細講解。
4用戶登錄模塊
4.1用戶登錄
BOOT客戶管理系統用戶登錄功能的實現流程如圖18-5所示。
從圖18-5可以看出,用戶登錄過程中首先要驗證用戶名和密碼是否正確,如果正確,可以成功登錄系統,系統會自動跳轉到主頁;如果錯誤,則在登錄頁面給出錯誤提示信息。
下麵就依照圖18-5中的流程,來實現系統登錄功能,具體步驟如下。
1創建持久化類
在src目錄下,創建一個com.itheima.crm. pojo包,在包中創建用戶持久化類User,併在User類中定義用戶相關屬性以及相應的getter/setter方法,如文件18-5所示。
文件18-5 User.java
package com.itheima.core.pojo;
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Integer user_id; // 用戶id
private String user_code; // 用戶賬戶
private String user_name; // 用戶名稱
private String use_password; // 用戶密碼
// getter/setter方法
}
2實現DAO
(1)創建用戶DAO層介面。在src目錄下,創建一個com.itheima.crm.dao包,在包中創建一個用戶介面UserDao,併在介面中編寫通過賬號和密碼查詢用戶的方法,如文件18-6所示。
文件18-6 UserDao.java
public interface UserDao {
/**
* 通過賬戶和密碼查詢用戶
* @param usercode
* @param password
* @return
*/
public User findUser(@Param("usercode") String usercode,@Param("password")String password);
}
在上述方法代碼的參數中,@Param("usercode")表示為參數usercode命名,命名後,在映射文件的SQL中,使用#{usercode}就可以獲取usercode的參數值。
(2)創建映射文件。在com.itheima.core.dao包中,創建一個MyBatis映射文件UserDao.xml,併在映射文件中編寫查詢用戶信息的執行語句,如文件18-7所示。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.core.dao.UserDao">
<!-- 查詢用戶 -->
<select id="findUser" parameterType="String" resultType="User">
select *
from sys_user
where user_code = #{usercode}
and user_password = #{password}
and user_state = '1'
</select>
</mapper>
上述代碼通過映射查詢語句來查詢系統用戶表中的可用用戶。
3實現Service
(1)創建用戶Service層介面。在src目錄下,創建一個com.itheima.core.service包,在包中創建UserService介面,併在該介面中編寫一個通過賬號和密碼查詢用戶的方法,如文件18-8所示。
文件18-8 UserService.java
public interface UserService {
/**
* 通過賬號和密碼查詢用戶
* @param usercode
* @param password
* @return
*/
public User findUser(String usercode,String password);
}
(2)創建用戶Service層介面的實現類。在src目錄下,創建一個com.itheima.core.service. impl包,併在包中創建UserService介面的實現類UserServiceImpl,在類中編輯並實現介面中的方法,如文件18-9所示。
文件18-9 UserServiceImpl.java
@Service
@Transactional
public class UserServiceImpl implements UserService {
// 註入Userdao
@Autowired
private UserDao userdao;
@Override
public User findUser(String usercode, String password) {
User user=this.userdao.findUser(usercode, password);
return user;
}
}
在上述代碼的findUser()方法中,調用了UserDao對象中的findUser()方法來查詢用戶信息,並將查詢到的信息返回。
4實現Controller
在src目錄下,創建一個com.itheima.core.web.controller包,在包中創建用戶控制器類UserController,編輯後的代碼如文件18-10所示。
文件18-10 UserController.java
public class UserController {
@Autowired
private UserService userService;
/**
* 用戶登錄
* @param usercode
* @param password
* @param model
* @param session
* @return
*/
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login(String usercode,String password,Model model,HttpSession session) {
User user= userService.findUser(usercode,password);
if(user!=null) {
//將用戶對象添加到Session
session.setAttribute("USER_SESSION", user);
// 跳轉到主頁面
return "customer";
}
model.addAttribute("msg", "賬號或密碼錯誤,請重新登錄!");
// 返回到登錄頁面
return "login";
}
}
在文件18-10中,首先通過@Autowired註解將UserService對象註入到了本類中,然後創建了一個用於用戶登錄的login()方法。由於在用戶登錄時,表單都會以POST方式提交,所以將@RequestMapping註解的method屬性值設置為RequestMethod.POST。在login()方法中,首先通過頁面中傳遞過來的賬號和密碼查詢用戶,然後通過if語句判斷是否存在該用戶。如果存在,就將用戶信息存儲到Session中,並跳轉到系統主頁面;如果不存在,則提示錯誤信息,並返回到登錄頁面。
5實現頁面功能
(1)系統預設首頁index.jsp主要實現了一個轉發功能,在訪問時會轉發到登錄頁面,其實現代碼如文件18-11所示。
文件18-11 index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 轉發到登錄頁面 -->
<jsp:forward page="/WEB-INF/jsp/login.jsp"></jsp:forward>
</body>
</html>
(2)登錄頁面中,主要包含一個登錄表單,其頁面實現代碼如文件18-12所示。
文件18-12 login.jsp
在文件18-12中,核心代碼是用戶登錄操作的form表單,該表單在提交時會通過check()方法檢查賬戶或密碼是否為空,如果為空,則通過標簽提示用戶“賬號或密碼不能為空!”;如果賬號和密碼都已填寫,則將表單提交到以“/login.action”結尾的請求中。
6啟動項目,測試登錄
將項目發佈到Tomcat伺服器並啟動,成功訪問登錄頁面後,即可輸入賬號和密碼登錄系統。在執行登錄操作之前,先查看一下資料庫中sys_user表中的數據,如圖18-6所示。
從圖18-6可以看出,表sys_user中包含4個賬號(user_code)以m開頭的用戶信息。此時在登錄頁面中輸入賬號“m0001”和密碼“123”,單擊“登錄”按鈕後,瀏覽器的顯示結果如圖18-7所示。
從圖18-7可以看出,系統已經成功進入客戶管理頁面,這說明系統登錄成功。此時由於項目中並沒有實現客戶查詢功能,所以客戶列表中沒有任何數據。
4.2 實現登錄驗證
雖然在18.4.1節中已經實現了用戶登錄功能,但是此功能還並不完善。假設在其他控制器類中也包含一個訪問客戶管理頁面的方法,那麼用戶完全可以繞過登錄步驟,而直接通過訪問該方法的方式進入客戶管理頁面。為了驗證上述內容,我們可以在用戶控制器類UserController中編寫一個跳轉到客戶管理頁面的方法,其代碼如下所示。
/**
* 模擬其他類中跳轉到客戶管理頁面的方法
* @return
*/
@RequestMapping(value="/toCustomer.action")
public String toCustomer() {
return "customer";
}
此時,如果通過瀏覽器訪問地址http://localhost:8080/boot-crm/toCustomer.action,瀏覽器就會直接顯示客戶管理頁面
顯然,讓未登錄的用戶直接訪問到客戶管理頁面,是十分不安全的。為了避免此種情況的發生,並提升系統的安全性,我們可以創建一個登錄攔截器來攔截所有請求。只有已登錄用戶的請求才能夠通過,而對於未登錄用戶的請求,系統會將請求轉發到登錄頁面,並提示用戶登錄,其執行流程如圖18-9所示。
實現用戶登錄驗證的具體過程如下。
1創建登錄攔截器類
在src目錄下,創建一個com.itheima.core.interceptor包,併在包中創建登錄攔截器類LoginInterceptor,來實現用戶登錄的攔截功能,編輯後如文件18-13所示。
文件18-13 LoginInterceptor.java
public class LoginInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 獲取請求的URL
String url=request.getRequestURI();
// URL:除了登錄請求外,其他的URL都進行攔截控制
if(url.indexOf("/login.action")>=0) {
return true;
}
// 獲取session
HttpSession session = request.getSession();
User user = (User)session.getAttribute("USER_SESSION");
// 判斷Session中是否有用戶數據,如果有,則返回true,繼續向下執行
if(user!=null) {
return true;
}
// 不符合條件的給出提示信息,並轉發到登錄頁面
request.setAttribute("msg", "您還沒有登錄,請先登錄!");
request.getRequestDispatcher("/WEB_INF/jsp/login.jsp").forward(request, response);
return false;
}
}
在文件18-13的preHandle()方法中,首先獲取了用戶URL請求,然後通過請求來判斷是否為用戶登錄操作,只有對用戶登錄的請求才不進行攔截。接下來獲取了Session對象,並獲取Session中的用戶信息。如果Session中的用戶信息不為空,則表示用戶已經登錄,攔截器將放行;如果Session中的用戶信息為空,則表示用戶未登錄,系統會轉發到登錄頁面,並提示用戶登錄。
2配置攔截器
在springmvc-config.xml文件中,配置登錄攔截器信息,其配置代碼如下。
<!-- 配置攔截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.itheima.core.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
上述配置代碼會將所有的用戶請求都交由登錄攔截器來處理。至此,登錄攔截器的實現工作就已經完成。
發佈項目並啟動Tomcat伺服器後,再次通過瀏覽器訪問地址http://localhost:8080/boot-crm/toCustomer.action時,瀏覽器的顯示結果如圖18-10所示。
從圖18-10可以看出,未登錄的用戶在執行訪問客戶管理頁面方法後,並沒有成功跳轉到客戶管理頁面,而是轉發到了系統登錄頁面,同時在頁面的登錄視窗中也給出了提示信息。這也就說明用戶登錄驗證功能已成功實現。
4.3退出登錄
用戶登錄模塊中還包含一個功能——退出登錄。成功登錄後的用戶會跳轉到客戶管理頁面,並且在頁面中會顯示已登錄的用戶名稱,如圖18-11所示。
從圖18-11可以看出,頁面的右上角中已經顯示了登錄用戶“小韓”,並且彈出列表框最下方為“退出登錄”。那麼要如何實現“退出登錄”功能呢?
在customer.jsp頁面中,圖18-11中彈出列表框的實現代碼如下。
<ul class="dropdown-menu dropdown-user">
<li><a href="#"><i class="fa fa-user fa-fw"></i>
用戶:${USER_SESSION.user_name}
</a>
</li>
<li><a href="#"><i class="fa fa-gear fa-fw"></i> 系統設置</a></li>
<li class="divider"></li>
<li>
<a href="${pageContext.request.contextPath }/logout.action">
<i class="fa fa-sign-out fa-fw"></i>退出登錄
</a>
</li>
</ul>
從上述代碼中可以看出,顯示的登錄用戶名稱是通過EL表達式從Session中獲取的,而單擊“退出登錄”鏈接時,會提交一個以“/logout.action”結尾的請求。為了完成退出登錄功能,我們需要在用戶控制器類中編寫一個退出登錄的方法。在方法執行時,需要清除Session中的用戶信息,並且在退出登錄後,系統要返回到登錄頁面。因此,需要在用戶控制器類UserController中編寫退出登錄和返回到登錄頁面的方法,這兩個方法的實現代碼如下。
/**
* 退出登錄
* @param session
* @return
*/
@RequestMapping(value="/logout.action")
public String logout(HttpSession session) {
// 清除Session
session.invalidate();
// 重定向到登錄頁面的跳轉方法
return "redirect:login.action";
}
/**
* 向用戶登錄頁面跳轉
* @return
*/
@RequestMapping(value="/logout.action",method= RequestMethod.GET)
public String tologin() {
return "login";
}
至此,“退出登錄”的功能代碼就已經編寫完成。重啟項目並登錄系統後,單擊圖18-11中的“退出登錄”即可退出系統。
5客戶管理模塊
客戶管理模塊是本系統的核心模塊,該模塊中實現了對客戶的查詢、添加、修改和刪除功能。在接下來的幾個小節中,將對這幾個功能的實現進行詳細講解。
5.1 查詢客戶
在實際應用中,無論是企業級項目,還是互聯網項目,使用最多的一定是查詢操作。不管是在列表中展示所有數據的操作,還是對單個數據的修改或者刪除操作,都需要先查詢並展示出資料庫中的數據。
查詢操作通常可以分為按條件查詢和查詢所有,但在實際使用時,我們可以將這兩種查詢編寫在一個方法中使用,即當有條件時,就按照條件查詢;當沒有條件時,就查詢所有。同時,由於資料庫中的數據可能有很多,如果讓這些數據在一個頁面中全部顯示出來,勢必會使頁面數據的可讀性變得很差,所以我們還需要考慮將這些數據進行分頁查詢顯示。
綜合上述分析以及客戶頁面的顯示功能,BOOT客戶管理系統的查詢功能需要實現的功能如圖18-12所示。
從圖18-12可以看出,客戶管理模塊中的查詢可分為按照條件查詢和分頁查詢,這兩種查詢操作所查詢出的數據都會顯示在客戶信息列表中。如果未選擇任何條件,那麼客戶信息列表將分頁查詢顯示出所有數據。我們要如何實現客戶的條件查詢和分頁查詢呢?下麵將對客戶管理中的查詢功能實現進行詳細講解,具體步驟如下。
分析:
- 前臺發起請求,需要接收請求過來的查詢條件數據,可以使用pojo接收數據,編寫QueryVo,裡面包含查詢條件屬性和分頁數據。
- 前臺需要分頁顯示,根據準備好的分頁實現,應該返回分頁類Page,而創建Page分頁類需要數據總條數,所以也需要查詢數據總條數的邏輯。
根據分析,DAO需要編寫兩個方法:
- 需要根據條件分頁查詢客戶信息
- 需要根據條件查詢數據總條數
1創建持久化類
在com.itheima.core.pojo包中,創建客戶持久化類、數據字典持久化類、查詢條件包裝類,編輯後如文件18-14和文件18-15所示。
文件18-14 Customer.java
public class Customer implements Serializable{
private static final long serialVersionUID = 1L;
private Long cust_id;// 客戶編號
private String cust_name;// 客戶名稱
private Long cust_user_id;// 負責人id
private Long cust_create_id;// 創建人id
private String cust_source;// 客戶信息來源
private String cust_industry;// 客戶所屬行業
private String cust_level;// 客戶級別
private String cust_linkman;// 聯繫人
private String cust_phone;// 固定電話
private String cust_mobile;// 行動電話
private String cust_zipcode;// 郵政編碼
private String cust_address;// 聯繫地址
private Date cust_createtime;// 創建時間
//getter setter方法
}
在文件18-14中,聲明瞭與客戶數據表對應的屬性並定義了各個屬性的getter/setter方法。
文件18-15 BaseDict.java
public class BaseDict implements Serializable {
private static final long serialVersionUID = 1L;
private String dict_id;// 數據字典id
private String dict_type_code;// 數據字典類別代碼
private String dict_type_name;// 數據字典類別名稱
private String dict_item_name; // 數據字典項目名稱
private String dict_item_code;// 數據字典項目代碼
private Integer dict_sort;// 排序欄位
private String dict_enable;// 是否可用
private String dict_memo;// 備註
//getter setter方法
}
在文件18-15中,聲明瞭與數據字典表對應的屬性並定義了各個屬性的getter/setter方法。
QueryVo.java
public class QueryVo {
private String custName;// 客戶名稱
private String custSource; // 客戶來源
private String custIndustry;// 所屬行業
private String custLevel;// 客戶級別
private Integer page = 1;// 當前頁碼數 預設查詢第1頁
private Integer start;// 起始行 資料庫從哪一條數據開始查
private Integer rows = 10; // 所取行數 每頁顯示數據條數
//getter setter方法
}
受請求參數的QueryVo,裡面包含查詢條件屬性和分頁數據。需要註意的是,屬性中的star和rows用於執行分頁操作,其中start表示分頁操作中的起始行,而rows則表示分頁中所選取的行數。
2實現DAO層
(1)創建客戶DAO層介面和映射文件。
在com.itheima.core.dao包中,創建一個CustomerDao介面,併在介面中編寫查詢客戶列表和客戶總數的方法,然後創建一個與介面同名的映射文件,如文件18-16和文件18-17所示。
文件18-16 CustomerDao.java
public interface CustomerDao {
/**
* 根據queryVo分頁查詢數據
*
* @param queryVo
* @return
*/
List<Customer> queryCustomerByQueryVo(QueryVo queryVo);
/**
* 根據queryVo查詢數據條數
*
* @param queryVo
* @return
*/
Integer queryCountByQueryVo(QueryVo queryVo);
}
文件18-17 CustomerDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.core.dao.CustomerDao">
<!-- SQL片段-->
<sql id="customerQueryVo">
<where>
<if test="custName != null and custName != ''">
AND a.cust_name LIKE '%${custName}%'
</if>
<if test="custSource != null and custSource != ''">
AND a.cust_source = #{custSource}
</if>
<if test="custIndustry != null and custIndustry != ''">
AND a.cust_industry = #{custIndustry}
</if>
<if test="custLevel != null and custLevel != ''">
AND a.cust_level = #{custLevel}
</if>
</where>
</sql>
<!-- 根據queryVo分頁查詢數據 -->
<select id="queryCustomerByQueryVo" parameterType="com.itheima.core.pojo.QueryVo"
resultType="com.itheima.core.pojo.Customer">
SELECT
a.cust_id,
a.cust_name,
a.cust_user_id,
a.cust_create_id,
b.dict_item_name cust_source,
c.dict_item_name cust_industry,
d.dict_item_name cust_level,
a.cust_linkman,
a.cust_phone,
a.cust_mobile,
a.cust_zipcode,
a.cust_address,
a.cust_createtime
FROM
customer a
LEFT JOIN base_dict b ON a.cust_source = b.dict_id
LEFT JOIN base_dict c ON a.cust_industry = c.dict_id
LEFT JOIN base_dict d ON a.cust_level = d.dict_id
<include refid="customerQueryVo" />
<!-- 執行分頁查詢-->
<if test="start != null">
LIMIT #{start}, #{rows}
</if>
</select>
<!-- 根據queryVo查詢數據條數 -->
<select id="queryCountByQueryVo" parameterType="com.itheima.core.pojo.QueryVo"
resultType="integer">
SELECT count(1) FROM customer a
<include refid="customerQueryVo" />
</select>
</mapper>
在文件18-17中,首先編寫了一個SQL片段來作為映射查詢客戶信息的條件,然後編寫了查詢所有客戶的映射查詢方法。在方法的SQL中,分別通過左外連接的方式從數據字典表base_dict中的類別代碼欄位查詢出了相應的類別信息,同時通過limit來實現數據的分頁查詢。最後編寫了一個查詢客戶總數的映射查詢語句用於分頁使用。
(2)創建數據字典DAO層介面和映射文件。
在com.itheima.core.dao包中,創建一個BaseDictDao介面,併在介面中編寫根據類別代碼查詢數據字典的方法,然後創建一個與介面同名的映射文件,如文件18-18和文件18-19所示
文件18-18 BaseDictDao.java
public interface BaseDictDao {
/**
* 根據類別代碼查詢數據
* @param dictTypecode
* @return
*/
public List<BaseDict> queryBaseDictByDictTypeCode(String dictTypecode);
}
文件18-19 BaseDictDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.core.dao.BaseDictDao">
<!-- 根據類別代碼查詢數據 -->
<select id="queryBaseDictByDictTypeCode" parameterType="String"
resultType="com.itheima.core.pojo.BaseDict">
SELECT * FROM base_dict WHERE dict_type_code =
#{dict_type_code}
</select>
</mapper>
3實現Service層
(1)引入分頁標簽類。在src目錄下,創建一個com.itheima.common.utils包,在包中引入分頁時使用的標簽類文件Page.java和NavigationTag.java,這兩個文件可直接從源代碼中獲取,其具體實現代碼如文件18-20和文件18-21所示。
文件18-20 Page.java
public class Page<T> {
private int total;// 總條數
private int page; // 當前頁
private int size; // 每頁數
private List<T> rows; // 結果集
// getter setter 方法
}
文件18-21 NavigationTag.java
/**
* 顯示格式:首頁 上一頁 1 2 3 4 5下一頁 尾頁
*/
public class NavigationTag extends TagSupport {
static final long serialVersionUID = 2372405317744358833L;
/**
* request 中用於保存Page<E> 對象的變數名,預設為“page”
*/
private String bean = "page";
/**
* 分頁跳轉的url地址,此屬性必須
*/
private String url = null;
/**
* 顯示頁碼數量
*/
private int number = 5;
@Override
public int doStartTag() throws JspException {
JspWriter writer = pageContext.getOut();
HttpServletRequest request =
(HttpServletRequest) pageContext.getRequest();
Page page = (Page) request.getAttribute(bean);
if (page == null)
return SKIP_BODY;
url = resolveUrl(url, pageContext);
try {
// 計算總頁數
int pageCount = page.getTotal() / page.getSize();
if (page.getTotal() % page.getSize() > 0) {
pageCount++;
}
writer.print("<nav><ul class=\"pagination\">");
//首頁鏈接路徑
String homeUrl = append(url, "page", 1);
//末頁鏈接路徑
String backUrl = append(url, "page", pageCount);
// 顯示“上一頁”按鈕
if (page.getPage() > 1) {
String preUrl = append(url, "page", page.getPage() - 1);
preUrl = append(preUrl, "rows", page.getSize());
writer.print("<li><a href=\"" + homeUrl + "\">" + "首頁</a></li>");
writer.print("<li><a href=\"" + preUrl + "\">" + "上一頁</a></li>");
} else {
writer.print("<li class=\"disabled\"><a href=\"#\">" + "首頁 </a></li>");
writer.print("<li class=\"disabled\"><a href=\"#\">" + "上一頁 </a></li>");
}
// 顯示當前頁碼的前2頁碼和後兩頁碼
// 若1 則 1 2 3 4 5, 若2 則 1 2 3 4 5, 若3 則1 2 3 4 5,
// 若4 則 2 3 4 5 6 ,若10 則 8 9 10 11 12
int indexPage =1;
if(page.getPage() - 2 <=0){
indexPage=1;
}else if(pageCount-page.getPage() <=2){
indexPage=pageCount-4;
}else{
indexPage= page.getPage() - 2;
}
for (int i= 1;i <= number && indexPage <= pageCount;indexPage++,i++){
if (indexPage == page.getPage()) {
writer.print("<li class=\"active\"><a href=\"#\">" + indexPage
+"<spanclass=\"sr-only\"></span></a></li>");
continue;
}
String pageUrl = append(url, "page", indexPage);
pageUrl = append(pageUrl, "rows", page.getSize());
writer.print("<li><a href=\"" + pageUrl + "\">" + indexPage + "</a></li>");
}
// 顯示“下一頁”按鈕
if (page.getPage() < pageCount) {
String nextUrl = append(url, "page", page.getPage() + 1);
nextUrl = append(nextUrl, "rows", page.getSize());
writer.print("<li><a href=\"" + nextUrl + "\">" + "下一頁</a></li>");
writer.print("<li><a href=\"" + backUrl + "\">" + "尾頁</a></li>");
} else {
writer.print("<li class=\"disabled\"><a href=\"#\">" + "下一頁</a></li>");
writer.print("<li class=\"disabled\"><a href=\"#\">" + "尾頁</a></li>");
}
writer.print("</nav>");
} catch (IOException e) {
e.printStackTrace();
}
return SKIP_BODY;
}
private String append(String url, String key, int value) {
return append(url, key, String.valueOf(value));
}
/**
* 為url 參加參數對兒
*/
private String append(String url, String key, String value) {
if (url == null || url.trim().length() == 0) {
return "";
}
if (url.indexOf("?") == -1) {
url = url + "?" + key + "=" + value;
} else {
if (url.endsWith("?")) {
url = url + key + "=" + value;
} else {
url = url + "&" + key + "=" + value;
}
}
return url;
}
/**
* 為url 添加翻頁請求參數
*/
private String resolveUrl(String url,
javax.servlet.jsp.PageContext pageContext) throws JspException {
Map params = pageContext.getRequest().getParameterMap();
for (Object key : params.keySet()) {
if ("page".equals(key) || "rows".equals(key)){
continue;
}
Object value = params.get(key);
if (value == null){
continue;
}
if (value.getClass().isArray()) {
url = append(url, key.toString(), ((String[]) value)[0]);
} else if (value instanceof String) {
url = append(url, key.toString(), value.toString());
}
}
return url;
}
public String getBean() {
return bean;
}
public void setBean(String bean) {
this.bean = bean;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public void setNumber(int number) {
this.number = number;
}
}
(2)創建數據字典及客戶的Service層介面。在com.itheima.core.service包中創建一個名稱為BaseDictService和CustomerService的介面,編輯後如文件18-22和文件18-23所示。
文件18-22 BaseDictService.java
public interface BaseDictService {
/**
* 根據類別代碼查詢
*
* @param dictTypeCode
* @return
*/
List<BaseDict> queryBaseDictByDictTypeCode(String dictTypeCode);
}
文件18-23 CustomerService.java
public interface CustomerService {
/**
* 根據條件分頁查詢客戶
*
* @param queryVo
* @return
*/
Page<Customer> queryCustomerByQueryVo(QueryVo queryVo);
}
3)創建數據字典及客戶Service層介面的實現類。在com.itheima.core.service.impl包中分別創建數據字典和客戶Service層介面的實現類BaseDictServiceImpl和CustomerServiceImpl,編輯後的代碼如文件18-24和文件18-25所示。
文件18-24 BaseDictServiceImpl.java
@Service
public class BaseDictServiceImpl implements BaseDictService{
@Autowired
private BaseDictDao baseDictDao;
@Override
public List<BaseDict> queryBaseDictByDictTypeCode(String dictTypeCode) {
return baseDictDao.queryBaseDictByDictTypeCode(dictTypeCode);
}
}
文件18-23 CustomerServiceImpl.java
@Service
public class CustomerServiceImpl implements CustomerService {
@Autowired
private CustomerDao customerDao;
@Override
public Page<Customer> queryCustomerByQueryVo(QueryVo queryVo) {
// 判斷參數對象
if(null != queryVo) {
if(StringUtils.isNotBlank(queryVo.getCustName())) {
queryVo.setCustName(queryVo.getCustName());
}
if(StringUtils.isNotBlank(queryVo.getCustSource())) {
queryVo.setCustSource(queryVo.getCustSource());
}
if(StringUtils.isNotBlank(queryVo.getCustIndustry())) {
queryVo.setCustIndustry(queryVo.getCustIndustry());
}
if(StringUtils.isNotBlank(queryVo.getCustLevel())) {
queryVo.setCustLevel(queryVo.getCustLevel());
}
if(StringUtils.isNotBlank(queryVo.getCustName())) {
queryVo.setCustName(queryVo.getCustName());
}
if(StringUtils.isNotBlank(queryVo.getCustName())) {
queryVo.setCustName(queryVo.getCustName());
}
}
// 設置查詢條件,從哪一條數據開始查 page和rows有初始值
queryVo.setStart((queryVo.getPage() - 1) * queryVo.getRows());
// 查詢數據結果集
List<Customer> list = this.customerDao.queryCustomerByQueryVo(queryVo);
// 查詢到的數據總條數
int total = this.customerDao.queryCountByQueryVo(queryVo);
// 封裝返回的page對象
Page<Customer> page = new Page<Customer>();
page.setPage(queryVo.getPage());// 當前頁 和參數一樣
page.setRows(list);// 結果集
page.setSize(queryVo.getRows());// 每頁數 和參數一樣
page.setTotal(total);// 總條數
return page;
}
在文件18-25的實現方法中,首先判斷參數是否為空,然後判斷條件查詢中的客戶名稱、信息來源、所屬行業和客戶級別是否為空,只有不為空時,才添加到參數對象中。接下來獲取了頁面傳遞過來的當前頁page和每頁數信息rows,由此得到起始行start。然後查詢所有的客戶信息以及客戶總數。最後將查詢出的所有信息封裝到Page對象中並返回。
4實現Controller
在com.itheima.core.controller包中,創建客戶控制器類CustomerController,編輯後如文件18-26所示。
文件18-26 CustomerController.java
@Controller
public class CustomerController {
// 客戶來源
@Value("${CUSTOMER_FROM_TYPE}")
private String CUSTOMER_FROM_TYPE;
// 客戶行業
@Value("${CUSTOMER_INDUSTRY_TYPE}")
private String CUSTOMER_INDUSTRY_TYPE;
// 客戶級別
@Value("${CUSTOMER_LEVEL_TYPE}")
private String CUSTOMER_LEVEL_TYPE;
@Autowired
private BaseDictService baseDictService;
@Autowired
private CustomerService customerService;
/**
* 顯示用戶列表
* @return
*/
@RequestMapping(value="/customer/list")
public String list(Model model,QueryVo queryVo) {
// 已在tomcat的server.xml中修改了uri的編碼為UTF-8,此處代碼可不寫 <Connector URIEncoding="UTF-8" />
// try {
// // 解決get請求亂碼問題
// if (StringUtils.isNotBlank(queryVo.getCustName())) {
// queryVo.setCustName(new String(queryVo.getCustName().getBytes("ISO-8859-1"), "UTF-8"));
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// 客戶來源
List<BaseDict> fromType = baseDictService.queryBaseDictByDictTypeCode(CUSTOMER_FROM_TYPE);
// 所屬行業
List<BaseDict> industryType =baseDictService.queryBaseDictByDictTypeCode(CUSTOMER_INDUSTRY_TYPE);
// 客戶級別
List<BaseDict> levelType = baseDictService.queryBaseDictByDictTypeCode(CUSTOMER_LEVEL_TYPE);
// 把前端頁面需要顯示的數據放到模型中
model.addAttribute("fromType", fromType);
model.addAttribute("industryType", industryType);
model.addAttribute("levelType", levelType);
// 條件、分頁查詢數據
Page<Customer> page = this.customerService.queryCustomerByQueryVo(queryVo);
// 把分頁查詢的結果放到模型中
model.addAttribute("page", page);
// 數據回顯
model.addAttribute("custName", queryVo.getCustName());
model.addAttribute("custSource", queryVo.getCustSource());
model.addAttribute("custIndustry", queryVo.getCustIndustry());
model.addAttribute("custLevel", queryVo.getCustLevel());
return "customer";
}
在客戶控制器類中,首先聲明瞭customerService和baseDictService屬性,並通過@Autowired註解將這兩個對象註入到本類中;然後分別定義了客戶來源、所屬行業和客戶級別屬性,並通過@Value註解將resource.properties文件中的屬性值賦給這3個屬性;最後編寫了查詢客戶列表的方法來執行查詢操作,其中第1個參數page的預設值為1,表示從第1條開始,第2個參數的預設值為10,表示每頁顯示10條數據。
5實現頁面顯示
(1)在18.3.3小節準備項目環境時,已經說明瞭需要引入自定義標簽文件。在本項目中,自定義標簽文件主要用於實現分頁功能,其標簽名稱為commons.tld,標簽中的實現代碼如文件18-27所示。
文件18-27 commons.tld
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<!-- 指定標簽庫的版本號 -->
<tlib-version>2.0</tlib-version>
<!-- 指定JSP的版本號 -->
<jsp-version>1.2</jsp-version>
<!-- 指定標簽庫的名稱 -->
<short-name>common</short-name>
<!-- 指定標簽庫的URI -->
<uri>http://itheima.com/common/</uri>
<!-- 指定標簽庫的顯示名稱 -->
<display-name>Common Tag</display-name>
<!-- 指定標簽庫的描述 -->
<description>Common Tag library</description>
<!-- 註冊一個自定義標簽 -->
<tag>
<!-- 指定註冊的自定義標簽名稱 -->
<name>page</name>
<!-- 指定自定義標簽的標簽處理器類 -->
<tag-class>com.itheima.common.utils.NavigationTag</tag-class>
<!-- 指定標簽體類型 -->
<body-content>JSP</body-content>
<!-- 描述 -->
<description>create navigation for paging</description>
<!-- 指定標簽中的屬性 -->
<attribute>
<!-- 指定屬性名稱 -->
<name>url</name>
<!-- 該屬性為true時表示其指定是屬性為必須屬性 -->
<required>true</required>
<!-- 該屬性用於指定能不能使用表達式來動態指定數據,為true時表示可以 -->
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>bean</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>number</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
在文件18-27中,第13行代碼就是我們在使用自定義標簽時引入的URI,第23行代碼指定了自定義標簽的處理器類,其他內容參見代碼註釋信息。小提示在實際開發時,分頁功能通常都會使用通用的工具類,或分頁組件來實現,而這些工具類和組件一般不需要開發人員自己編寫,只需學會使用即可。所以本書中的分頁工具類和上面的分頁標簽文件讀者只需直接引入,並且掌握如何使用,而不需要自己編寫。
(2)在customer.jsp中,編寫條件查詢和顯示客戶列表以及分頁查詢的代碼,具體如文件18-28所示。
文件18-28 customer.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="itheima" uri="http://itcast.cn/common/"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName()
+ ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>客戶管理-BootCRM</title>
<!-- 引入css樣式文件 -->
<!-- Bootstrap Core CSS -->
<link href="<%=basePath%>css/bootstrap.min.css" rel="stylesheet" />
<!-- MetisMenu CSS -->
<link href="<%=basePath%>css/metisMenu.min.css" rel="stylesheet" />
<!-- DataTables CSS -->
<link href="<%=basePath%>css/dataTables.bootstrap.css" rel="stylesheet" />
<!-- Custom CSS -->
<link href="<%=basePath%>css/sb-admin-2.css" rel="stylesheet" />
<!-- Custom Fonts -->
<link href="<%=basePath%>css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<link href="<%=basePath%>css/boot-crm.css" rel="stylesheet" type="text/css" />
</head>
<body>
...
<!-- 客戶列表查詢部分 start-->
<div id="page-wrapper">
<div class="row">
<div class="col-lg-12">