• 【Spring项目中的Service理解】


    目录

    1. Spring项目中的核心组成部分

    2. Spring项目中的Service

     2.1 Service的功能作用

     2.2 Service的实现


    1. Spring项目中的核心组成部分

    项目的核心组成部分图解:

     

    2. Spring项目中的Service

     2.1 Service的功能作用

    Service是项目中用于处理业务逻辑的,因为每种数据在做某种操作时,应该都有某些规则:

    • 例如用户尝试登录时,涉及的规则可能包含:用户名对应的用户信息必须存在、提交的密码必须与数据库中存储的密码是匹配的……
    • 例如用户尝试修改密码时,涉及的规则可能包含:当前用户账号必须存在且处于正常状态、提交的原密码必须与数据库中存储的密码是匹配的……
    • 例如用户尝试注册时,涉及的规则可能包含:提交的用户名必须在数据库不存在,提交的手机号码必须在数据库中不存在,提交的电子邮箱必须在数据库中不存在……

    这些规则是用于保障数据的有效性、安全性的,使得数据可以随着我们设定的规则而产生或发生变化!

    在项目中,关于Service的开发,通常是先定义接口,再定义类实现此接口,接口名通常使用“数据类型Service”这样格式的名称,而实现类通常是在接口名的基础上再添加Impl后缀。

    在《阿里巴巴Java开发手册》中的规约:

    • 【强制】对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部 的实现类用 Impl 的后缀与接口区别。

     2.2 Service的实现

    则在项目的根包下创建service.IAlbumService接口:

    public interface IAlbumService {}

    然后,在根包下创建service.impl.AlbumServiceImpl类,此类需要实现以上的IAlbumService接口:

    public class AlbumServiceImpl implements IAlbumService {}

    文件结构如下图所示:

     

    然后,需要在接口中设计“添加相册”的抽象方法:

    xx xx(xx);

    关于抽象方法的名称:可以完全自定义,当前业务是“添加相册”,可以使用addNewadd等。

    关于抽象方法的参数列表:大多参数是由客户端提交到控制器,再由控制器调用时传递过来的参数,另外,也可能是控制器处理出来的某些数据(例如Session中的当前登录用户信息),本次的参数应该包含:相册名称、相册简介、相册的排序序号,可以将这3个数据封装到自定义的DTO类中,并使用DTO类型作为参数。

    关于抽象方法的返回值类型:仅以操作成功为前提来设计返回值类型,如果操作失败,将抛出异常。

    在项目的根包下创建pojo.dto.AlbumAddNewDTO类:

    1. public class AlbumAddNewDTO {
    2. private String name;
    3. private String description;
    4. private Integer sort;
    5. }

    并在IAlbumService接口中添加抽象方法:

    void addNew(AlbumAddNewDTO albumAddNewDTO);

    然后,在AlbumServiceImpl中实现此抽象方法:

    1. @Slf4j
    2. @Service
    3. public class AlbumServiceImpl implements IAlbumService {
    4. @Autowired
    5. private AlbumMapper albumMapper;
    6. public AlbumServiceImpl() {
    7. log.debug("创建业务对象:AlbumServiceImpl");
    8. }
    9. @Override
    10. public void addNew(AlbumAddNewDTO albumAddNewDTO) {
    11. // 【稍后再实现】应该保证此相册的名称是唯一的
    12. // 创建Album类型的对象
    13. // 调用BeanUtils.copyProperties(源对象, 目标对象)将参数的属性值复制到新创建的Album对象中
    14. // 调用albumMapper的int insert(Album album)方法插入相册数据
    15. }
    16. }

    初步实现为:

    1. package cn.tedu.csmall.product.service.impl;
    2. import cn.tedu.csmall.product.mapper.AlbumMapper;
    3. import cn.tedu.csmall.product.pojo.dto.AlbumAddNewDTO;
    4. import cn.tedu.csmall.product.pojo.entity.Album;
    5. import cn.tedu.csmall.product.service.IAlbumService;
    6. import lombok.extern.slf4j.Slf4j;
    7. import org.springframework.beans.BeanUtils;
    8. import org.springframework.beans.factory.annotation.Autowired;
    9. import org.springframework.stereotype.Service;
    10. @Slf4j
    11. @Service
    12. public class AlbumServiceImpl implements IAlbumService {
    13. @Autowired
    14. private AlbumMapper albumMapper;
    15. public AlbumServiceImpl() {
    16. log.debug("创建业务对象:AlbumServiceImpl");
    17. }
    18. @Override
    19. public void addNew(AlbumAddNewDTO albumAddNewDTO) {
    20. // 【稍后再实现】应该保证此相册的名称是唯一的
    21. // 创建Album类型的对象
    22. Album album = new Album();
    23. // 调用BeanUtils.copyProperties(源对象, 目标对象)将参数的属性值复制到新创建的Album对象中
    24. BeanUtils.copyProperties(albumAddNewDTO, album);
    25. // 调用albumMapper的int insert(Album album)方法插入相册数据
    26. albumMapper.insert(album);
    27. }
    28. }

    完成后,在src/test/java下的根包下创建service.AlbumServiceTests测试类,并在类中编写、执行测试方法:

    1. package cn.tedu.csmall.product.service;
    2. import cn.tedu.csmall.product.pojo.dto.AlbumAddNewDTO;
    3. import cn.tedu.csmall.product.pojo.entity.Album;
    4. import lombok.extern.slf4j.Slf4j;
    5. import org.junit.jupiter.api.Test;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.boot.test.context.SpringBootTest;
    8. @Slf4j
    9. @SpringBootTest
    10. public class AlbumServiceTests {
    11. @Autowired
    12. IAlbumService service;
    13. @Test
    14. void addNew() {
    15. AlbumAddNewDTO album = new AlbumAddNewDTO();
    16. album.setName("测试数据9998");
    17. album.setDescription("测试数据的简介");
    18. album.setSort(99); // 注意:sort值必须是[0, 255]之间的
    19. service.addNew(album);
    20. log.debug("添加数据完成!");
    21. }
    22. }

    在具体实现过程中,还应该保证此次尝试添加的相册的名称是唯一的!

    可以通过查询数据库来得知尝试添加的相册的名称是否已经被使用,需要执行的SQL语句可以是:

    select id from pms_album where name=?
    select count(*) from pms_album where name=?

    可以选择使用以上第2种查询来检验相册名称是否已经被使用,则应该在

    AlbumMapper.java接口中添加:

    int countByName(String name);

    并在AlbumMapper.xml中配置SQL:

    1. <!-- int countByName(String name); -->
    2. <select id="countByName" resultType="int">
    3. SELECT count(*) FROM pms_album WHERE name=#{name}
    4. </select>

    完成后,应该在AlbumMapperTests.java中编写并执行测试:

    1. @Test
    2. void countByName() {
    3. String name = "测试数据";
    4. int count = mapper.countByName(name);
    5. log.debug("根据名称【{}】统计完成,结果:{}", name, count);
    6. }

    接下来,可以在Service的实现过程中进行检查,例如:

    1. String albumName = albumAddNewDTO.getName();
    2. int count = albumMapper.countByName(albumName);
    3. if (count > 0) {
    4. // 相册名称已经被使用,将不允许添加此相册,应该抛出异常
    5. } else {
    6. // 相册名称没有被使用,可以将此相册数据插入到数据库中
    7. }

    提示:以上代码中,由于满足if条件时将抛出异常,所以,可以不必使用else,并且,在后续的编程中,当需要执行某些判断时,应该优先根据“抛出异常”或“终止当前方法的执行”来设计if的条件!即:

    if (count > 0) {
        // 相册名称已经被使用,将不允许添加此相册,应该抛出异常
    }

    // 相册名称没有被使用,可以将此相册数据插入到数据库中

    具体实现为:

    1. @Override
    2. public void addNew(AlbumAddNewDTO albumAddNewDTO) {
    3. // 应该保证此相册的名称是唯一的
    4. String albumName = albumAddNewDTO.getName();
    5. int count = albumMapper.countByName(albumName);
    6. if (count > 0) {
    7. throw new RuntimeException();
    8. }
    9. // 创建Album类型的对象
    10. Album album = new Album();
    11. // 调用BeanUtils.copyProperties(源对象, 目标对象)将参数的属性值复制到新创建的Album对象中
    12. BeanUtils.copyProperties(albumAddNewDTO, album);
    13. // 调用albumMapper的int insert(Album album)方法插入相册数据
    14. albumMapper.insert(album);
    15. }

    为了避免测试时因为相册名称冲突出现异常而导致测试失败,应该在测试时捕获所抛出的异常,例如:

    1. @Test
    2. void addNew() {
    3. AlbumAddNewDTO album = new AlbumAddNewDTO();
    4. album.setName("测试数据9998");
    5. album.setDescription("测试数据的简介");
    6. album.setSort(99); // 注意:sort值必须是[0, 255]之间的
    7. try {
    8. service.addNew(album);
    9. log.debug("添加数据完成!");
    10. } catch (RuntimeException e) {
    11. log.debug("添加数据失败!名称已经被占用!");
    12. }
    13. }

    关于以上实现过程中抛出的异常,使用的是RuntimeException,是不合适的!因为程序出现RuntimeException的原因有很多,例如空指针异常、数组下标越界异常、类型转换异常,都属于RuntimeException,如果“相册名称被占用”时抛出RuntimeException,则此方法的调用者很难区分出现异常的真正原因!

    通常,建议自定义异常,并且,当视为失败时,抛出此自定义异常的对象!

    则在根包下创建ex.ServiceException类,继承自RuntimeException

    1. public class ServiceException extends RuntimeException {
    2. }

    提示:本次自定义的异常应该继承自RuntimeException。

    然后,在AlbumServiceImpl中添加相册时,如果相册名称被使用,则抛出ServiceException类型的异常:

    1. if (count > 0) {
    2. throw new ServiceException();
    3. }

    并且,在测试中,捕获的异常也改为ServiceException

    1. try {
    2. service.addNew(album);
    3. log.debug("添加数据完成!");
    4. } catch (ServiceException e) {
    5. log.debug("添加数据失败!名称已经被占用!");
    6. }

    个人主页:居然天上楼

    感谢你这么可爱帅气还这么热爱学习~~

    人生海海,山山而川

    你的点赞👍 收藏⭐ 留言📝 加关注✅

    是对我最大的支持与鞭策

  • 相关阅读:
    了解 HTTPS 中间人攻击:保护你的网络安全
    腾讯小程序音视频 TRTC live-pusher 黑屏等各种问题
    侯捷 C++ STL标准库和泛型编程 —— 4 分配器 + 5 迭代器
    NR/5G - PUSCH repetition次数
    java计算机毕业设计精品旅游项目管理系统源码+mysql数据库+系统+lw文档+部署
    Vue——formcreate表单设计器自定义组件实现
    支付宝支付项目
    Android 关于IC卡的读写和加密
    主流商业智能(BI)工具的比较(二):Power BI与Domo
    SQL语句-中级
  • 原文地址:https://blog.csdn.net/weixin_72612071/article/details/128106087