閱讀702 返回首頁    go 阿裏雲 go 技術社區[雲棲]


《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章的內容。

screenshot

圖1-12 TestUserService的運行結果

最後更新:2017-05-31 14:01:51

  上一篇:go  《Spring 3.0就這麼簡單》——1.6 展現層
  下一篇:go  《Spring 3.0就這麼簡單》——1.4 持久層