《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 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章的内容。
图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仓库 立即下载
相关文章
- 面试(4)-spring-Spring面试题和答案
- Spring +quartz获取ApplicationContext上下文
- Spring Boot 之 RESTfull API简单项目的快速搭建(二)
- 53分布式电商项目 - Spring集成ActiveMQ
- 《SpringBoot揭秘:快速构建微服务体系》—第2章2.2节Spring IoC其实很简单
- [转]Spring IOC 一——容器装配Bean的简单使用
- Spring 注解学习手札(八) 补遗——@ExceptionHandler
- 手把手教你搭建Spring Boot+Vue前后端分离
- spring中List,Set,Map集合的输出(详解)
- 《精通Spring MVC 4》——1.6 幕后的Spring Boot
- 《Spring 3.0就这么简单》——1.2 实例功能概述
- 《Spring 3.0就这么简单》——1.4 持久层
- 《Spring 3.0就这么简单》——1.6 展现层
- Spring框架中@PostConstruct注解的使用
- aop注解 spring提供的事务
- Spring Boot RabbitMQ 应用场景
- spring的context---ServletContext WebApplicationContext---Spring各种上下文的关系详解
- Spring MVC 学习笔记 spring mvc Schema-based configuration
- kafka原理和实践(二)spring-kafka简单实践
- Spring MVC简单的HelloWorld例子
- mvn打包spring工程成jar时报Unable to locate Spring NamespaceHandler for XML schema namespace错误解决办法
- 实际工程Quartz与Spring设计与实现一体化的热部署
- 2.6 Spring事务传播
- 曹工说Spring Boot源码(28)-- Spring的component-scan机制,让你自己来进行简单实现,怎么办
- Spring项目中简单几步实现多个动态数据源(多个数据库)切换,通过maven依赖注入相关jar包,来满足不同数据库、不同数据表(不同数据源)的灵活调用
- 整合Struts2、Hibernate、Spring
- spring IOC(Spring 生命周期,先1.构造方式,2,初始化方法,3,目标方法,4,销毁方法)