Spring?Service功能作用詳細(xì)講解
1. Spring項目中的核心組成部分
項目的核心組成部分圖解:

2. Spring項目中的Service
2.1 Service的功能作用
Service是項目中用于處理業(yè)務(wù)邏輯的,因為每種數(shù)據(jù)在做某種操作時,應(yīng)該都有某些規(guī)則:
- 例如用戶嘗試登錄時,涉及的規(guī)則可能包含:用戶名對應(yīng)的用戶信息必須存在、提交的密碼必須與數(shù)據(jù)庫中存儲的密碼是匹配的……
- 例如用戶嘗試修改密碼時,涉及的規(guī)則可能包含:當(dāng)前用戶賬號必須存在且處于正常狀態(tài)、提交的原密碼必須與數(shù)據(jù)庫中存儲的密碼是匹配的……
- 例如用戶嘗試注冊時,涉及的規(guī)則可能包含:提交的用戶名必須在數(shù)據(jù)庫不存在,提交的手機(jī)號碼必須在數(shù)據(jù)庫中不存在,提交的電子郵箱必須在數(shù)據(jù)庫中不存在……
這些規(guī)則是用于保障數(shù)據(jù)的有效性、安全性的,使得數(shù)據(jù)可以隨著我們設(shè)定的規(guī)則而產(chǎn)生或發(fā)生變化!
在項目中,關(guān)于Service的開發(fā),通常是先定義接口,再定義類實現(xiàn)此接口,接口名通常使用“數(shù)據(jù)類型Service”這樣格式的名稱,而實現(xiàn)類通常是在接口名的基礎(chǔ)上再添加Impl后綴。
在《阿里巴巴Java開發(fā)手冊》中的規(guī)約:
【強(qiáng)制】對于 Service 和 DAO 類,基于 SOA 的理念,暴露出來的服務(wù)一定是接口,內(nèi)部 的實現(xiàn)類用 Impl 的后綴與接口區(qū)別。
2.2 Service的實現(xiàn)
則在項目的根包下創(chuàng)建service.IAlbumService接口:
public interface IAlbumService {}然后,在根包下創(chuàng)建service.impl.AlbumServiceImpl類,此類需要實現(xiàn)以上的IAlbumService接口:
public class AlbumServiceImpl implements IAlbumService {}文件結(jié)構(gòu)如下圖所示:

然后,需要在接口中設(shè)計“添加相冊”的抽象方法:
xx xx(xx);
關(guān)于抽象方法的名稱:可以完全自定義,當(dāng)前業(yè)務(wù)是“添加相冊”,可以使用addNew、add等。
關(guān)于抽象方法的參數(shù)列表:大多參數(shù)是由客戶端提交到控制器,再由控制器調(diào)用時傳遞過來的參數(shù),另外,也可能是控制器處理出來的某些數(shù)據(jù)(例如Session中的當(dāng)前登錄用戶信息),本次的參數(shù)應(yīng)該包含:相冊名稱、相冊簡介、相冊的排序序號,可以將這3個數(shù)據(jù)封裝到自定義的DTO類中,并使用DTO類型作為參數(shù)。
關(guān)于抽象方法的返回值類型:僅以操作成功為前提來設(shè)計返回值類型,如果操作失敗,將拋出異常。
在項目的根包下創(chuàng)建pojo.dto.AlbumAddNewDTO類:
public class AlbumAddNewDTO {
private String name;
private String description;
private Integer sort;
}并在IAlbumService接口中添加抽象方法:
void addNew(AlbumAddNewDTO albumAddNewDTO);
然后,在AlbumServiceImpl中實現(xiàn)此抽象方法:
@Slf4j
@Service
public class AlbumServiceImpl implements IAlbumService {
@Autowired
private AlbumMapper albumMapper;
public AlbumServiceImpl() {
log.debug("創(chuàng)建業(yè)務(wù)對象:AlbumServiceImpl");
}
@Override
public void addNew(AlbumAddNewDTO albumAddNewDTO) {
// 【稍后再實現(xiàn)】應(yīng)該保證此相冊的名稱是唯一的
// 創(chuàng)建Album類型的對象
// 調(diào)用BeanUtils.copyProperties(源對象, 目標(biāo)對象)將參數(shù)的屬性值復(fù)制到新創(chuàng)建的Album對象中
// 調(diào)用albumMapper的int insert(Album album)方法插入相冊數(shù)據(jù)
}
}初步實現(xiàn)為:
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("創(chuàng)建業(yè)務(wù)對象:AlbumServiceImpl");
}
@Override
public void addNew(AlbumAddNewDTO albumAddNewDTO) {
// 【稍后再實現(xiàn)】應(yīng)該保證此相冊的名稱是唯一的
// 創(chuàng)建Album類型的對象
Album album = new Album();
// 調(diào)用BeanUtils.copyProperties(源對象, 目標(biāo)對象)將參數(shù)的屬性值復(fù)制到新創(chuàng)建的Album對象中
BeanUtils.copyProperties(albumAddNewDTO, album);
// 調(diào)用albumMapper的int insert(Album album)方法插入相冊數(shù)據(jù)
albumMapper.insert(album);
}
}完成后,在src/test/java下的根包下創(chuàng)建service.AlbumServiceTests測試類,并在類中編寫、執(zhí)行測試方法:
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("測試數(shù)據(jù)9998");
album.setDescription("測試數(shù)據(jù)的簡介");
album.setSort(99); // 注意:sort值必須是[0, 255]之間的
service.addNew(album);
log.debug("添加數(shù)據(jù)完成!");
}
}在具體實現(xiàn)過程中,還應(yīng)該保證此次嘗試添加的相冊的名稱是唯一的!
可以通過查詢數(shù)據(jù)庫來得知嘗試添加的相冊的名稱是否已經(jīng)被使用,需要執(zhí)行的SQL語句可以是:
select id from pms_album where name=?
select count(*) from pms_album where name=?
可以選擇使用以上第2種查詢來檢驗相冊名稱是否已經(jīng)被使用,則應(yīng)該在
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>完成后,應(yīng)該在AlbumMapperTests.java中編寫并執(zhí)行測試:
@Test
void countByName() {
String name = "測試數(shù)據(jù)";
int count = mapper.countByName(name);
log.debug("根據(jù)名稱【{}】統(tǒng)計完成,結(jié)果:{}", name, count);
}接下來,可以在Service的實現(xiàn)過程中進(jìn)行檢查,例如:
String albumName = albumAddNewDTO.getName();
int count = albumMapper.countByName(albumName);
if (count > 0) {
// 相冊名稱已經(jīng)被使用,將不允許添加此相冊,應(yīng)該拋出異常
} else {
// 相冊名稱沒有被使用,可以將此相冊數(shù)據(jù)插入到數(shù)據(jù)庫中
}提示:以上代碼中,由于滿足if條件時將拋出異常,所以,可以不必使用else,并且,在后續(xù)的編程中,當(dāng)需要執(zhí)行某些判斷時,應(yīng)該優(yōu)先根據(jù)“拋出異常”或“終止當(dāng)前方法的執(zhí)行”來設(shè)計if的條件!即:
if (count > 0) {
// 相冊名稱已經(jīng)被使用,將不允許添加此相冊,應(yīng)該拋出異常 }
// 相冊名稱沒有被使用,可以將此相冊數(shù)據(jù)插入到數(shù)據(jù)庫中
具體實現(xiàn)為:
@Override
public void addNew(AlbumAddNewDTO albumAddNewDTO) {
// 應(yīng)該保證此相冊的名稱是唯一的
String albumName = albumAddNewDTO.getName();
int count = albumMapper.countByName(albumName);
if (count > 0) {
throw new RuntimeException();
}
// 創(chuàng)建Album類型的對象
Album album = new Album();
// 調(diào)用BeanUtils.copyProperties(源對象, 目標(biāo)對象)將參數(shù)的屬性值復(fù)制到新創(chuàng)建的Album對象中
BeanUtils.copyProperties(albumAddNewDTO, album);
// 調(diào)用albumMapper的int insert(Album album)方法插入相冊數(shù)據(jù)
albumMapper.insert(album);
}為了避免測試時因為相冊名稱沖突出現(xiàn)異常而導(dǎo)致測試失敗,應(yīng)該在測試時捕獲所拋出的異常,例如:
@Test
void addNew() {
AlbumAddNewDTO album = new AlbumAddNewDTO();
album.setName("測試數(shù)據(jù)9998");
album.setDescription("測試數(shù)據(jù)的簡介");
album.setSort(99); // 注意:sort值必須是[0, 255]之間的
try {
service.addNew(album);
log.debug("添加數(shù)據(jù)完成!");
} catch (RuntimeException e) {
log.debug("添加數(shù)據(jù)失敗!名稱已經(jīng)被占用!");
}
}關(guān)于以上實現(xiàn)過程中拋出的異常,使用的是RuntimeException,是不合適的!因為程序出現(xiàn)RuntimeException的原因有很多,例如空指針異常、數(shù)組下標(biāo)越界異常、類型轉(zhuǎn)換異常,都屬于RuntimeException,如果“相冊名稱被占用”時拋出RuntimeException,則此方法的調(diào)用者很難區(qū)分出現(xiàn)異常的真正原因!
通常,建議自定義異常,并且,當(dāng)視為失敗時,拋出此自定義異常的對象!
則在根包下創(chuàng)建ex.ServiceException類,繼承自RuntimeException:
public class ServiceException extends RuntimeException {}提示:本次自定義的異常應(yīng)該繼承自RuntimeException。
然后,在AlbumServiceImpl中添加相冊時,如果相冊名稱被使用,則拋出ServiceException類型的異常:
if (count > 0) {
throw new ServiceException();
}并且,在測試中,捕獲的異常也改為ServiceException:
try {
service.addNew(album);
log.debug("添加數(shù)據(jù)完成!");
} catch (ServiceException e) {
log.debug("添加數(shù)據(jù)失敗!名稱已經(jīng)被占用!");
}到此這篇關(guān)于Spring Service功能作用詳細(xì)講解的文章就介紹到這了,更多相關(guān)Spring Service內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用springmvc的controller層獲取到請求的數(shù)據(jù)方式
這篇文章主要介紹了使用springmvc的controller層獲取到請求的數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
關(guān)于springcloud報錯報UnsatisfiedDependencyException的問題
這篇文章主要介紹了關(guān)于springcloud報錯報UnsatisfiedDependencyException的問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11
如何使用 Shell 腳本查看多個服務(wù)器的端口是否打開的方法
這篇文章主要介紹了如何使用 Shell 腳本來查看多個服務(wù)器的端口是否打開的方法,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06
IDEA JarEditor編輯jar包方式(直接新增,修改,刪除jar包內(nèi)的class文件)
文章主要介紹了如何使用IDEA的JarEditor插件直接修改jar包內(nèi)的class文件,而不需要手動解壓、反編譯和重新打包,通過該插件,可以更方便地進(jìn)行jar包的修改和測試2025-01-01

