【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类:

public class AlbumAddNewDTO {
    private String name;
    private String description;
    private Integer sort;
}

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

void addNew(AlbumAddNewDTO albumAddNewDTO);

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

@Slf4j
@Service
public class AlbumServiceImpl implements IAlbumService  {
    
    @Autowired
    private AlbumMapper albumMapper;
    
    public AlbumServiceImpl() {
        log.debug("创建业务对象:AlbumServiceImpl");
    }

    @Override
    public void addNew(AlbumAddNewDTO albumAddNewDTO) {
        // 【稍后再实现】应该保证此相册的名称是唯一的
        
        // 创建Album类型的对象
        // 调用BeanUtils.copyProperties(源对象, 目标对象)将参数的属性值复制到新创建的Album对象中
		// 调用albumMapper的int insert(Album album)方法插入相册数据
    }

}

初步实现为:

package cn.tedu.csmall.product.service.impl;

import cn.tedu.csmall.product.mapper.AlbumMapper;
import cn.tedu.csmall.product.pojo.dto.AlbumAddNewDTO;
import cn.tedu.csmall.product.pojo.entity.Album;
import cn.tedu.csmall.product.service.IAlbumService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class AlbumServiceImpl implements IAlbumService  {

    @Autowired
    private AlbumMapper albumMapper;

    public AlbumServiceImpl() {
        log.debug("创建业务对象:AlbumServiceImpl");
    }

    @Override
    public void addNew(AlbumAddNewDTO albumAddNewDTO) {
        // 【稍后再实现】应该保证此相册的名称是唯一的

        // 创建Album类型的对象
        Album album = new Album();
        // 调用BeanUtils.copyProperties(源对象, 目标对象)将参数的属性值复制到新创建的Album对象中
        BeanUtils.copyProperties(albumAddNewDTO, album);
        // 调用albumMapper的int insert(Album album)方法插入相册数据
        albumMapper.insert(album);
    }

}

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

package cn.tedu.csmall.product.service;

import cn.tedu.csmall.product.pojo.dto.AlbumAddNewDTO;
import cn.tedu.csmall.product.pojo.entity.Album;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@Slf4j
@SpringBootTest
public class AlbumServiceTests {

    @Autowired
    IAlbumService service;

    @Test
    void addNew() {
        AlbumAddNewDTO album = new AlbumAddNewDTO();
        album.setName("测试数据9998");
        album.setDescription("测试数据的简介");
        album.setSort(99); // 注意:sort值必须是[0, 255]之间的

        service.addNew(album);
        log.debug("添加数据完成!");
    }

}

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

可以通过查询数据库来得知尝试添加的相册的名称是否已经被使用,需要执行的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:

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

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

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

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

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

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

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

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

具体实现为:

@Override
public void addNew(AlbumAddNewDTO albumAddNewDTO) {
    // 应该保证此相册的名称是唯一的
    String albumName = albumAddNewDTO.getName();
    int count = albumMapper.countByName(albumName);
    if (count > 0) {
        throw new RuntimeException();
    }

    // 创建Album类型的对象
    Album album = new Album();
    // 调用BeanUtils.copyProperties(源对象, 目标对象)将参数的属性值复制到新创建的Album对象中
    BeanUtils.copyProperties(albumAddNewDTO, album);
    // 调用albumMapper的int insert(Album album)方法插入相册数据
    albumMapper.insert(album);
}

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

@Test
void addNew() {
    AlbumAddNewDTO album = new AlbumAddNewDTO();
    album.setName("测试数据9998");
    album.setDescription("测试数据的简介");
    album.setSort(99); // 注意:sort值必须是[0, 255]之间的

    try {
        service.addNew(album);
        log.debug("添加数据完成!");
    } catch (RuntimeException e) {
        log.debug("添加数据失败!名称已经被占用!");
    }
}

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

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

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

public class ServiceException extends RuntimeException {
}

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

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

if (count > 0) {
    throw new ServiceException();
}

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

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

个人主页:居然天上楼

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

人生海海,山山而川

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

是对我最大的支持与鞭策

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 可能是因为你没有在Service添加具体的逻辑代码。Service类一般是用来实现业务逻辑的,如果没有具体的语句,可能是因为你还没有为这个Service类添加相应的业务逻辑代码。你可以参考Spring Boot的官方文档或者一些开源项目来学习如何编写Service类。 ### 回答2: 在Spring Bootservice是一种用于处理业务逻辑的组件。它的主要作用是将控制器(Controller)和数据访问层(DAO)进行解耦,实现业务逻辑的复用和集管理。因此,在service下通常不会包含太多的语句。 首先,Spring Boot强调了面向接口编程的思想,因此,service通常会定义一个接口,然后由具体实现类来实现该接口。这样做的好处是可以随时替换不同的实现类,而不影响其他组件的正常工作。所以,在service下可能只会包含接口定义的语句,例如方法签名和注解等。 其次,service的主要职责是组织和调用业务逻辑,而不是实际执行具体的业务操作。它通常会依赖其他的组件,例如DAO、第三方API等,来完成具体的业务操作。因此,在service下的语句往往是一些调用其他组件的语句,例如调用DAO的方法来操作数据库,或者调用第三方API获取数据等。 最后,为了保持代码的整洁和可维护性,通常建议将具体的业务逻辑封装在service方法,而不是直接在service写大量的语句。这样可以使代码结构更清晰,易于理解和修改。同时,将具体的业务逻辑封装在方法,也可以提高代码的可测试性,方便编写和执行单元测试。 综上所述,Spring Bootservice层主要是为了提供业务逻辑的组织与调用,并且强调面向接口编程的思想。因此,在service下通常不会包含太多的语句,而是以接口定义、调用其他组件的语句为主。 ### 回答3: 在Spring BootService是一种用来处理业务逻辑的组件。Service用于封装复杂的业务逻辑,与数据库交互,进行数据处理和计算等操作。在Service,通常不包含太多的语句是因为它遵循了单一职责的原则。 首先,Service层的主要目的是为了提供一种业务逻辑的抽象。它将业务逻辑从Controller层解耦出来,保证了代码的可复用性和可维护性。通过将业务逻辑封装在Service,可以使Controller层更加简洁和专注于处理HTTP请求和响应。 其次,Service层更加关注业务层面的工作,而不是具体的语句实现。在Service,可能会包含一些条件判断、循环操作等基本语句,但是不会包含过多的复杂逻辑。相反,Service层更多的是调用其他组件或者依赖层来完成具体的业务操作,例如调用Repository层进行数据库操作。 此外,Service层的职责也包括处理事务、异常处理和数据验证等。这些都是与具体语句无关的操作,需要Service层来完成。因此,Service层更注重对业务流程的管理和控制,而不是具体的语句实现。 总之,Spring BootService层不包含过多的语句,是为了保证代码的清晰性和可维护性。它主要负责封装业务逻辑,与其他层进行交互,保障系统的高内聚性和低耦合性,提高代码的可读性和可测试性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

居然天上楼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值