zl程序教程

您现在的位置是:首页 >  后端

当前栏目

《Spring 3.0就这么简单》——1.5 业务层

Spring 简单 业务 这么 3.0 1.5
2023-09-11 14:17:34 时间

本节书摘来自异步社区《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 

 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"

 p:dataSource-ref="dataSource" / 

 !--④通过AOP配置提供事务增强,让service包下所有Bean的所有方法拥有事务 -- 

 aop:config proxy-target- 

 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{②


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的运行结果


spring注解实现业务层事务管理,当业务层自调用时,事务失效问题解决 前些日子一朋友在需要在目标对象中进行自我调用,且需要实施相应的事务定义,且网上的一种通过BeanPostProcessor的解决方案是存在问题的。因此专门写此篇帖子分析why。 1、预备知识 aop概念请参考【http://www.
异步社区 异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
阿里特邀专家徐雷Java Spring Boot开发实战系列课程(第18讲):制作Java Docker镜像与推送到DockerHub和阿里云Docker仓库 立即下载