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

    xiaoxiao2024-03-12  130

    本节书摘来自异步社区《Spring 3.0就这么简单》一书中的第1章,第1.5节,作者: 陈雄华 , 林开雄著,更多章节内容可以访问云栖社区“异步社区”公众号查看

    1.5 业务层

    在景区网站登录实例中,业务层仅有一个业务类,即UserService。UserService负责将持久层的UserDao和LoginLoginDao组织起来完成用户/密码认证、登录日志记录等操作。

    1.5.1 UserServiceUserService业务接口有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的运行结果

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)