《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="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-3.1.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx*
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop
http://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 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..*(..))" />
   <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>

在①处的声明处再添加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="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/ 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的运行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值