《Spring 3.0就这么简单》——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章的内容。

图1-12 TestUserService的运行结果

时间: 2024-08-20 19:00:22

《Spring 3.0就这么简单》——1.5 业务层的相关文章

《Spring 3.0就这么简单》——第1章 快速入门 1.1 Spring概述

第1章 快速入门 本章通过一个简单的例子展现开发Spring Web应用的整体过程,通过这个实例,读者可以快速进入Spring Web应用的世界.实例应用按持久层.业务层和展现层进行组织,从底层DAO程序到Web展现程序逐层演进,一步步地搭建起一个完整的实例.通过本章的学习,读者可以独立完成一个典型的基于Spring的Web应用. 本章主要内容: Spring 概述 用户登录实例介绍 基于 Spring JDBC 的持久层实现 基于 Spring 声明式事务的业务层实现 基于 Spring MV

《Spring 3.0就这么简单》——1.6 展现层

1.6 展现层 业务层和持久层的开发任务已经完成,该是为程序提供界面的时候了.Struts MVC框架由于抢尽天时地利,成为当下最流行的展现层框架.但也有很多人认为Spring MVC相比较于Struts更简单.更强大.更优雅.此外,由于Spring MVC出自于Spring之手,因此和Spring容器没有任何不兼容性,显得天衣无缝. Spring 1.5新增了基于注解的MVC,而且Spring 3.1还提供了REST风格的MVC,Spring MVC已经变得轻便.强大.易用.我们将会在本书的第

《Spring 3.0就这么简单》——1.4 持久层

1.4 持久层 持久层负责数据的访问和操作,DAO类被上层的业务类调用.Spring本身支持多种流行的ORM框架.这里使用Spring JDBC作为持久层的实现技术,关于Spring JDBC的详细内容,请参见第4章的内容.为方便阅读,会对本章涉及的相关知识点进行必要的介绍,所以相信读者在不了解Spring JDBC的情况下,也可以轻松阅读以下的内容.1.4.1 建立领域对象 领域对象(Domain Object)也称为实体类,它代表了业务的状态,一般来说,领域对象属于业务层,但它贯穿展现层.业

《Spring 3.0就这么简单》——导读

前言 Spring作为Java领域的第一开源项目,从其诞生到现在已有10个年头.10年的时间对于计算机业界来说是非常漫长的,在热闹的Java开源领域,无数个开源产品喧嚣登场,但又很快被人们淡忘.能够像Spring一样历经时间洗礼而历久弥香的开源框架真的是寥若星辰,Spring无疑是Java开源世界的一朵奇葩. 在Spring发展的10年中,不但Spring自身不断发展壮大,各种基于Spring的子项目也如雨后春笋一样成长起来,Spring的社区亦蓬勃发展.Rod就和他的骨干团队成立了Spring

《Spring 3.0就这么简单》——1.8 小结

1.8 小结 本章概述了Spring的发展历程,并用Spring MVC.Spring JDBC以及Spring的声明式事务等技术实现了一个常见的景区网站登录模块,让大家对如何使用Spring框架构建Web应用拥有了切身的体验,同时还了解了开发一个简单的Web应用所需要经历的开发过程. 也许用户会抱怨该实例功能的简单性和开发过程的复杂性有点不成正比.但对于一个具有扩展性.灵活性的Web应用来说,这些步骤往往都是必需的,其实我们在完成实例开发的同时也完成了Web框架的搭建,为新功能模块的添加夯实地

《Spring 3.0就这么简单》——1.7 运行Web应用

1.7 运行Web应用 在IDEA中运行Web应用前,首先需要配置好Web应用服务器,这里使用Maven的Web容器插件运行应用.打开当前模块pom.xml文件,添加构建插件配置信息,如代码清单1-21所示. 代码清单1-21 pom.xml <build> <finalName>chapter1</finalName> <plugins> <!-- Jetty插件 --> <plugin> <groupId>org.mo

【spring源码学习】spring配置的事务方式是REQUIRED,但业务层抛出TransactionRequiredException异常问题

(1)spring抛出异常的点:org.springframework.orm.jpa.EntityManagerFactoryUtils public static DataAccessException convertJpaAccessExceptionIfPossible(RuntimeException ex) { // Following the JPA specification, a persistence provider can also // throw these two

Spring中dao的值注入不到业务层,配置文件中的sessionFactory创建也不起作用

问题描述 只有在dao层能得到数据,传不到Biz层,如果在Biz层  buyinfoDAO=new BuyinfoDAOImpl();就没问题了,但就没有依赖注入的意义了java代码: applicationContext.xml配置:<bean id="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><property name=&

Spring 2.0的新特性点评

Spring2.0的发布恐怕算得上2006年Java社区的一件大事了.在Spring2.0发布附带的文档里面对2.0新特性做了概要的介绍,2.0的新特性是自然是我们最关注的方面: 一.Spring的XML配置引入XML Schema语法简化配置 在Spring1.x系列中,bean的配置文件使用DTD,没有namespace的分隔.2.0的一个非常大的改进是引入了XML Schema的namespace,因而可以将bean的配置文件做大幅度的简化.这些简化包括了对bean属性的各种简化,AOP配