首頁 > 軟體

Spring Service功能作用詳細講解

2022-12-09 14:02:35

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("新增資料失敗!名稱已經被佔用!");
}

到此這篇關於Spring Service功能作用詳細講解的文章就介紹到這了,更多相關Spring Service內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com