《Spring 3.0就這麼簡單》——1.5 業務層
本節書摘來自異步社區《Spring 3.0就這麼簡單》一書中的第1章,第1.5節,作者: 陳雄華 , 林開雄著,更多章節內容可以訪問雲棲社區“異步社區”公眾號查看
1.5 業務層
在景區網站登錄實例中,業務層僅有一個業務類,即UserService。UserService負責將持久層的UserDao和LoginLoginDao組織起來完成用戶/密碼認證、登錄日誌記錄等操作。
1.5.1 UserService
UserService業務接口有3個業務方法,其中hasMatchUser()用於檢查用戶名/密碼的正確性;findUserByUserName()以用戶名為條件加載User對象;loginSuccess()方法在用戶登錄成功後調用,更新用戶最後登錄時間和IP信息同時記錄用戶登錄日誌。
下麵,我們來實現這個業務類,UserService的實現類需要調用DAO層的兩個DAO完成業務邏輯操作,如代碼清單1-9所示。
代碼清單1-9 UserService
package com.smart.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.smart.dao.LoginLogDao;
import com.smart.dao.UserDao;
import com.smart.domain.LoginLog;
import com.smart.domain.User;
@Service①
public class UserService {
@Autowired //②
private UserDao userDao;
@Autowired //③
private LoginLogDao loginLogDao;
public boolean hasMatchUser(String userName, String password) {
int matchCount =userDao.getMatchCount(userName, password);
return matchCount > 0;
}
public User findUserByUserName(String userName) {
return userDao.findUserByUserName(userName);
}
public void loginSuccess(User user) {
LoginLog loginLog = new LoginLog();
loginLog.setUserId(user.getUserId());
loginLog.setIp(user.getLastIp());
loginLog.setLoginDate(user.getLastVisit());
loginLogDao.insertLoginLog(loginLog);
}
}
首先,我們在①處通過@Service注解,將UserService標注為一個服務層的Bean,然後在②和③處注入userDao和loginLogDao這兩個DAO層的Bean。hasMatchUser()和findUserByUserName()業務方法簡單地調用DAO來完成對應的功能;loginSuccess()方法根據入參user對象構造出LoginLog對象,並調用loginLogDao向t_login_log表中添加一條記錄。
實戰經驗
在實際應用中,一般不會直接在數據庫中以明文的方式保存用戶的密碼,因為這樣很容易造成密碼泄密的問題。所以需要將密碼加密後以密文的方式進行保存;另外一種更有效的辦法是僅保存密碼的MD5摘要,由於相等的兩字符串摘要值也相等,在登錄驗證時,通過比較摘要的方式就可以判斷用戶所輸入的密碼是否正確。由於不能通過密碼摘要反推出原來的密碼,即使內部人員可以查看用戶信息表也無法知道用戶的密碼。所以,摘要存儲方式已經成為大部分係統密碼存儲的通用方式。此外,為了防止黑客通過工具進行密碼的暴力破解,目前大多數Web應用都使用了圖片驗證碼功能,驗證碼具有一次性消費的特征,每次登錄都不相同,這樣工具暴力破解就無用武之地了。
loginSuccess()將兩個DAO組織起來共同完成一個事務性的數據操作:更新t_user表記錄並添加t_login_log表記錄。但從UserService中卻看不出任何事務操作的影子,這正是Spring的高明之處,它讓用戶從事務操作單調機械的代碼中解脫出來,專注完成那些不可或缺的業務工作。通過Spring聲明式事務配置,即可讓業務類享受EJB聲明式事務的好處。下一節將了解如何賦予業務類事務管理的能力。
1.5.2 在Spring中裝配Service
事務管理的代碼雖然不用出現在程序代碼中,但必須以某種方式告訴Spring哪些業務類需要工作於事務環境下以及事務的規則等內容,以便Spring根據這些信息自動為目標業務類添加事務管理的功能。
打開原來的applicationContext.xml文件,更改代碼如代碼清單1-10所示。
代碼清單1-10 applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--①引入aop及tx命名空間所對應的Schema文件-->
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:p="https://www.springframework.org/schema/p"
xmlns:context="https://www.springframework.org/schema/context"
xmlns:aop="https://www.springframework.org/schema/aop"
xmlns:tx="https://www.springframework.org/schema/tx"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.1.xsd
https://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context-3.1.xsd
https://www.springframework.org/schema/tx*
https://www.springframework.org/schema/tx/spring-tx-3.1.xsd
https://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<context:component-scan base-package="com.smart.dao"/>
<!--② 掃描service類包,應用Spring的注解配置 -->
<context:component-scan base-package="com.smart.service"/>
…
<!--③ 配置事務管理器 -->
<bean
p:dataSource-ref="dataSource" />
<!--④通過AOP配置提供事務增強,讓service包下所有Bean的所有方法擁有事務 -->
<aop:config proxy-target->
<aop:pointcut
expression=" execution(* com.smart.service..*(..))" />
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
</aop:config>
<tx:advice transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" />
</tx:attributes>
</tx:advice>
</beans>
在①處的聲明處再添加aop和tx命名空間的schema定義文件的說明,這樣在配置文件中就可以使用這兩個空間中的配置標簽了。
在②處將com.smart.service添加到上下文掃描路徑中,以便使service包中類的Spring注解生效。
在③處定義了一個基於數據源的DataSourceTransactionManager事務管理器,該事務管理器負責聲明式事務的管理。該管理器需要引用dataSource Bean。
在④處通過AOP及tx命名空間的語法,以AOP的方式為com.smart.service包下所有類的所有方法都添加了事務增強,即它們都將工作於事務環境中。關於Spring事務的配置,詳見本書的第6章。
這樣,就完成了業務層的程序開發和配置工作,接下來,需要對該業務類進行簡單的單元測試,以便檢驗業務方法的正確性。
1.5.3 單元測試
TestNG是一種基於注釋的新一代單元測試框架,通過添加靈活的裝置、測試分類、參數測試和依賴測試等特性來克服JUnit的不足之處。因此,本書的所有示例代碼將采用TestNG 6.3.1作為測試基礎框架。首先在pom.xml文件中配置TestNG、Spring-test兩個類庫依賴,如代碼清單1-11所示。
代碼清單1-11 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/ xsd/maven-4.0.0.xsd">
...
<dependencies>
...
<!-- 依賴的測試類庫-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
</dependency>
</dependencies>
</project>
選中單元測試所在的包文件夾service並單擊鼠標右鍵,執行New→Java Class命令創建UserService的單元測試類,單擊OK按鈕,創建UserServiceTest的單元用例,並編寫如代碼清單1-12所示的測試代碼。
代碼清單1-12 UserServiceTest
package com.smart.service;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.*;
import static org.testng.Assert.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import com.smart.domain.User;
@ContextConfiguration(locations={"/applicationContext.xml"})①
public class UserServiceTest
extends AbstractTestNGSpringContextTests{②
@Autowired
private UserService userService;③
@Test ④
public void hasMatchUser() {
boolean b1 = userService.hasMatchUser("admin", "123456");
boolean b2 = userService.hasMatchUser("admin", "1111");
assertTrue(b1);
assertTrue(!b2);
}
@Test
public void findUserByUserName() {
User user = userService.findUserByUserName("admin");
assertEquals(user.getUserName(), "admin");
}
…
}
首先,Spring 3.1的測試框架可以和TestNG整合,通過Spring提供的測試基類AbstractTestNGSpringContextTests,可以將Spring容器和TestNG測試框架整合。@ContextConfiguration也是Spring提供的注解,它用於指定Spring的配置文件。
在測試類中可以使用Spring的@Autowired將Spring容器中的Bean注入測試類中。在測試方法前通過TestNG的@Test注解即可將方法標注為測試方法。
在IDEA中,選中UserServiceTest測試用例,單擊鼠標右鍵,選擇Run“UserServiceTest”菜單項,運行該測試用例以檢驗業務類方法的正確性。
從單元測試的運行結果(見圖1-12)可以看到3個業務方法已經成功執行,在後台數據庫中,用戶將發現已經有一條新的登錄日誌添加到t_login_log表中。關於Spring應用測試的內容,可以參見本書第8章的內容。
圖1-12 TestUserService的運行結果
最後更新:2017-05-31 14:01:51