亚洲精品中文免费|亚洲日韩中文字幕制服|久久精品亚洲免费|一本之道久久免费

      
      

            <dl id="hur0q"><div id="hur0q"></div></dl>

                Spring Boot 業(yè)務邏輯層

                關于業(yè)務邏輯層(Service層)

                業(yè)務邏輯層是被Controller直接調(diào)用的層(Controller不允許直接調(diào)用持久層),通常,在業(yè)務邏輯層中編寫的代碼是為了保證數(shù)據(jù)的完整性和安全性,使得數(shù)據(jù)是隨著我們設定的規(guī)則而產(chǎn)生或發(fā)生變化。

                通常,在業(yè)務邏輯層的代碼會由接口和實現(xiàn)類組件,其中,接口被視為是必須的

              1. – 推薦使用基于接口的編程方式
              2. – 部分框架在處理某些功能時,會使用基于接口的代理模式,例如Spring JDBC框架在處理事務時
              3. 在接口中,聲明抽象方法時,僅以操作成功為前提來設計返回值類型(不考慮失?。?,如果業(yè)務在執(zhí)行過程可能出現(xiàn)某些失?。ú环纤O定的規(guī)則),可以通過拋出異常來表示!

                關于拋出的異常,通常是自定義的異常,并且,自定義異常通常是`RuntimeException`的子類,主要原因:

              4. – 不必顯式的拋出或捕獲,因為業(yè)務邏輯層的異常永遠是拋出的,而控制器層會調(diào)用業(yè)務邏輯層,在控制器層的Controller中其實也是永遠拋出異常的,這些異常會通過Spring MVC統(tǒng)一處理異常的機制進行處理,關于異常的整個過程都是固定流程,所以,沒有必要顯式拋出或捕獲
              5. – 部分框架在處理某些事情時,默認只對`RuntimeException`的子孫類進行識別并處理,例如Spring JDBC框架在處理事務時
              6. 所以,在實際編寫業(yè)務邏輯層之前,應該先規(guī)劃異常,例如先創(chuàng)建`ServiceException`類:

                public class ServiceException extends RuntimeException { //TODU}

                接下來,再創(chuàng)建具體的對應某種“失敗”的異常,例如,在添加管理員時,可能因為“用戶名已經(jīng)存在”而失敗,則創(chuàng)建對應的`UsernameDuplicateException`異常:

                public class UsernameDuplicateException extends ServiceException { //TODU}

                另外,當插入數(shù)據(jù)時,如果返回的受影響行數(shù)不是1時,必然是某種錯誤,則創(chuàng)建對應的插入數(shù)據(jù)異常:

                public class InsertException extends ServiceException { //TODU}

                關于抽象方法的參數(shù),應該設計為客戶端提交的數(shù)據(jù)類型或?qū)姆庋b類型,不可以是數(shù)據(jù)表對應的實體類型!如果使用封裝的類型,這種類型在類名上應該添加某種后綴,例如`DTO`或其它后綴,例如:

                public class AdminAddNewDTO implements Serializable { private String username; private String password; private String nickname; private String avatar; private String phone; private String email; private String description; // Setters & Getters // hashCode(), equals() // toString()}

                然后,在`cn.celinf.boot.demo.service`包下聲明接口及抽象方法:

                public interface IAdminService { void addNew(AdminAddNewDTO adminAddNewDTO);}

                并在以上`service`包下創(chuàng)建`impl`子包,再創(chuàng)建`AdminServiceImpl`類:

                import cn.celinf.boot.demo.entity.Admin;import cn.celinf.boot.demo.ex.InsertException;import cn.celinf.boot.demo.ex.UsernameDuplicateException;import cn.celinf.boot.demo.mapper.AdminMapper;import cn.celinf.boot.demo.pojo.dto.AdminAddNewDTO;import cn.celinf.boot.demo.service.IAdminService;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.time.LocalDateTime;@Servicepublic class AdminServiceImpl implements IAdminService { @Autowired private AdminMapper adminMapper; @Override public void addNew(AdminAddNewDTO adminAddNewDTO) { // 通過參數(shù)獲取用戶名 String username = adminAddNewDTO.getUsername(); // 調(diào)用adminMapper的Admin getByUsername(String username)方法執(zhí)行查詢 Admin queryResult = adminMapper.getByUsername(username); // 判斷查詢結(jié)果是否不為null if (queryResult != null) { // 是:表示用戶名已經(jīng)被占用,則拋出UsernameDuplicateException throw new UsernameDuplicateException(); } // 通過參數(shù)獲取原密碼 String password = adminAddNewDTO.getPassword(); // 通過加密方式,得到加密后的密碼encodedPassword String encodedPassword = password; // 創(chuàng)建當前時間對象now > LocalDateTime.now() LocalDateTime now = LocalDateTime.now(); // 創(chuàng)建Admin對象 Admin admin = new Admin(); // 補全Admin對象的屬性值:通過參數(shù)獲取username,nickname…… admin.setUsername(username); admin.setNickname(adminAddNewDTO.getNickname()); admin.setAvatar(adminAddNewDTO.getAvatar()); admin.setPhone(adminAddNewDTO.getPhone()); admin.setEmail(adminAddNewDTO.getEmail()); admin.setDescription(adminAddNewDTO.getDescription()); // 以上這些從一個對象中把屬性賦到另一個對象中,還可以使用: // BeanUtils.copyProperties(adminAddNewDTO, admin); // 補全Admin對象的屬性值:password > encodedPassword admin.setPassword(encodedPassword); // 補全Admin對象的屬性值:isEnable > 1 admin.setIsEnable(1); // 補全Admin對象的屬性值:lastLoginIp > null // 補全Admin對象的屬性值:loginCount > 0 admin.setLoginCount(0); // 補全Admin對象的屬性值:gmtLastLogin > null // 補全Admin對象的屬性值:gmtCreate > now admin.setGmtCreate(now); // 補全Admin對象的屬性值:gmtModified > now admin.setGmtModified(now); // 調(diào)用adminMapper的insert(Admin admin)方法插入管理員數(shù)據(jù),獲取返回值 int rows = adminMapper.insert(admin); // 判斷以上返回的結(jié)果是否不為1,拋出InsertException異常 if (rows != 1) { throw new InsertException(); } }}

                以上代碼未實現(xiàn)對密碼的加密處理!關于密碼加密,相關的代碼應該定義在別的某個類中,不應該直接將加密過程編寫在以上代碼中,因為加密的代碼需要在多處應用(添加用戶、用戶登錄、修改密碼等),并且,從分工的角度上來看,也不應該是業(yè)務邏輯層的任務!所以,在`cn.celinf.boot.demo.util`(包不存在,則創(chuàng)建)下創(chuàng)建`PasswordEncoder`類,用于處理密碼加密:

                package cn.celinf.boot.demo.util;@Componentpublic class PasswordEncoder { public String encode(String rawPassword) { return “aaa” + rawPassword + “aaa”; }}

                完成后,需要在`AdminServiceImpl`中自動裝配以上`PasswordEncoder`,并在需要加密時調(diào)用`PasswordEncoder`對象的`encode()`方法。

                控制器層開發(fā)

                Spring MVC是用于處理控制器層開發(fā)的,在使用Spring Boot時,在`pom.xml`中添加`spring-boot-starter-web`即可整合Spring MVC框架及相關的常用依賴項(包含`jackson-databind`),可以將已存在的`spring-boot-starter`直接改為`spring-boot-starter-web`,因為在`spring-boot-starter-web`中已經(jīng)包含了`spring-boot-starter`。

                先在項目的根包下創(chuàng)建`controller`子包,并在此子包下創(chuàng)建`AdminController`,此類應該添加`@RestController`和`@RequestMapping(value = “/admins”, produces = “application/json; charset=utf-8”)`注解,例如:

                @RestController@RequestMapping(values = “/admins”, produces = “application/json; charset=utf-8”)public class AdminController {}

                由于已經(jīng)決定了服務器端響應時,將響應JSON格式的字符串,為保證能夠響應JSON格式的結(jié)果,處理請求的方法返回值應該是自定義的數(shù)據(jù)類型,則從此前學習的`spring-mvc`項目中找到`JsonResult`類及相關類型,復制到當前項目中來。

                @Autowiredprivate IAdminService adminService;// 注意:暫時使用@RequestMapping,不要使用@PostMapping,以便于直接在瀏覽器中測試// http://localhost:8080/admins/add-new?username=root&password=1234@RequestMapping(“/add-new”)public JsonResult addNew(AdminAddNewDTO adminAddNewDTO) { adminService.addNew(adminAddNewDTO); return JsonResult.ok();}

                完成后,運行啟動類,即可啟動整個項目,在`spring-boot-starter-web`中,包含了Tomcat的依賴項,在啟動時,會自動將當前項目打包并部署到此Tomcat上,所以,執(zhí)行啟動類時,會執(zhí)行此Tomcat,同時,因為是內(nèi)置的Tomcat,只為當前項目服務,所以,在將項目部署到Tomcat時,默認已經(jīng)將Context Path(例如spring_mvc_war_exploded)配置為空字符串,所以,在啟動項目后,訪問的URL中并沒有此前遇到的Context Path值。

                當項目啟動成功后,即可在瀏覽器的地址欄中輸入網(wǎng)址進行測試訪問!

                【注意】:如果是未添加的管理員賬號,可以成功執(zhí)行結(jié)束,如果管理員賬號已經(jīng)存在,由于尚未處理異常,會提示500錯誤。

                public enum State {OK(200),ERR_USERNAME(201),ERR_PASSWORD(202),ERR_INSERT(500); // 新增的枚舉值// 原有其它代碼}

                然后,在`cn.celinf.boot.demo.controller`下創(chuàng)建`handler.GlobalExceptionHandler`類,用于統(tǒng)一處理異常,例如:

                package cn.celinf.boot.demo.controller.handler;import cn.celinf.boot.demo.ex.ServiceException;import cn.celinf.boot.demo.ex.UsernameDuplicateException;import cn.celinf.boot.demo.web.JsonResult;import cn.celinf.boot.demo.web.State;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvicepublic class GlobalExceptionHandler { @ExceptionHandler(ServiceException.class) public JsonResult handleServiceException(ServiceException e) { if (e instanceof UsernameDuplicateException) { return JsonResult.fail(State.ERR_USERNAME, “用戶名錯誤!”); } else { return JsonResult.fail(State.ERR_INSERT, “插入數(shù)據(jù)失?。?#8221;); } }}

                完成后,重新啟動項目,當添加管理員時的用戶名沒有被占用時,將正常添加,當用戶名已經(jīng)被占用時,會根據(jù)處理異常的結(jié)果進行響應!

                由于在統(tǒng)一處理異常的機制下,同一種異常,無論是在哪種業(yè)務中出現(xiàn),處理異常時的描述信息都是完全相同的,也無法精準的表達錯誤信息,這是不合適的!另外,基于面向?qū)ο蟮摹胺止ぁ彼枷?,關于錯誤信息(異常對應的描述信息),應該是由Service來描述,即“誰拋出誰描述”,因為拋出異常的代碼片段是最了解、最明確出現(xiàn)異常的原因的!

                為了更好的描述異常的原因,應該在自定義的`ServiceException`和其子孫類異常中添加基于父類的全部構(gòu)造方法(5個),然后,在`AdminServiceImpl`中,當拋出異常時,可以在異常的構(gòu)造方法中添加`String`類型的參數(shù),對異常發(fā)生的原因進行描述,例如:

                @Overridepublic void addNew(AdminAddNewDTO adminAddNewDTO) { // ===== 原有其它代碼 ===== // 判斷查詢結(jié)果是否不為null if (queryResult != null) { // 是:表示用戶名已經(jīng)被占用,則拋出UsernameDuplicateException log.error(“此賬號已經(jīng)被占用,將拋出異常”); throw new UsernameDuplicateException(“添加管理員失敗,用戶名(” + username + “)已經(jīng)被占用!”); } // ===== 原有其它代碼 ===== // 判斷以上返回的結(jié)果是否不為1,拋出InsertException異常 if (rows != 1) { throw new InsertException(“添加管理員失敗,服務器忙,請稍后再次嘗試!”); }}

                最后,在處理異常時,可以調(diào)用異常對象的`getMessage()`方法獲取拋出時封裝的描述信息,例如:

                @ExceptionHandler(ServiceException.class)public JsonResult handleServiceException(ServiceException e) { if (e instanceof UsernameDuplicateException) { return JsonResult.fail(State.ERR_USERNAME, e.getMessage()); } else { return JsonResult.fail(State.ERR_INSERT, e.getMessage()); }}

                完成后,再次重啟項目,當用戶名已經(jīng)存在時,可以顯示在Service中描述的錯誤信息!

                //響應的JSON數(shù)據(jù)例如:{“state”:200,”message”:null,”data”:null}//添加失敗時,響應的JSON數(shù)據(jù)例如:{“state”:201,”message”:”添加管理員失敗,用戶名(liuguobin)已經(jīng)被占用!”,”data”:null}

                可以看到,無論是成功還是失敗,響應的JSON中都包含了不必要的數(shù)據(jù)(為`null`的數(shù)據(jù)),這些數(shù)據(jù)屬性是沒有必要響應到客戶端的,如果需要去除這些不必要的值,可以在對應的屬性上使用注解進行配置,例如:

                //NON_NULL 則響應的JSON中只會包含不為`null`的部分。@Datapublic class JsonResult implements Serializable { // 狀態(tài)碼,例如:200 private Integer state; // 消息,例如:”登錄失敗,用戶名不存在” @JsonInclude(JsonInclude.Include.NON_NULL) private String message; // 數(shù)據(jù) @JsonInclude(JsonInclude.Include.NON_NULL) private T data; // ===== 原有其它代碼 =====}

                此注解還可以添加在類上,則作用于當前類中所有的屬性,例如:

                @Data@JsonInclude(JsonInclude.Include.NON_NULL)public class JsonResult implements Serializable { // ===== 原有其它代碼 =====}

                即使添加在類上,也只對當前類的3個屬性有效,后續(xù),當響應某些數(shù)據(jù)時,`data`屬性可能是用戶、商品、訂單等類型,這些類型的數(shù)據(jù)中為`null`的部分依然會被響應到客戶端去,所以,還需要對這些類型也添加相同的注解配置!

                以上做法相對比較繁瑣,可以在`application.properties` / `application.yml`中添加全局配置,則作用于當前項目中所有響應時涉及的類,例如在`properties`中配置為:

                //yml文件配置寫法spring: jackson: default-property-inclusion: non_null

                注意:當你需要在`yml`中添加以上配置時,前綴屬性名可能已經(jīng)存在,則不允許出現(xiàn)重復的前綴屬性名的:

                spring: profiles: active: dev jackson: default-property-inclusion: non_null

                最后,以上配置只是“默認”配置,如果在某些類型中還有不同的配置需求,仍可以在類或?qū)傩陨贤ㄟ^`@JsonInclude`進行配置。

                15. 解決跨域問題

                在使用前后端分離的開發(fā)模式下,前端項目和后端項目可能是2個完全不同的項目,并且,各自己獨立開發(fā),獨立部署,在這種做法中,如果前端直接向后端發(fā)送異步請求,默認情況下,在前端會出現(xiàn)類似以下錯誤:

                //web端 跨域錯誤Access to XMLHttpRequest at ‘http://localhost:8080/admins/add-new’ from origin ‘http://localhost:8081’ has been blocked by CORS policy:No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

                以上錯誤信息的關鍵字是`CORS`,通常稱之為“跨域問題”。

                在基于Spring MVC框架的項目中,當需要解決跨域問題時,需要一個Spring MVC的配置類(實現(xiàn)了`WebMvcConfigurer`接口的類),并重寫其中的方法,以允許指定條件的跨域訪問,例如:

                //解決跨域的config配置import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.CorsRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class SpringMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(“/**”) .allowedOriginPatterns(“*”) .allowedMethods(“*”) .allowedHeaders(“*”) .allowCredentials(true) .maxAge(3600); }}

                16. 關于客戶端提交請求參數(shù)的格式

                通常,客戶端向服務器端發(fā)送請求時,請求參數(shù)可以有2種形式,第1種是直接通過`&`拼接各參數(shù)與值,例如:

                // FormData//第一種 直接url地址拼接// username=root&password=123456&nickname=jackson&phone=13800138001let data = ‘username=’ + this.ruleForm.username+ ‘&password=’ + this.ruleForm.password+ ‘&nickname=’ + this.ruleForm.nickname+ ‘&phone=’ + this.ruleForm.phone//第2種方式是使用JSON語法來組織各參數(shù)與值let data = {‘username’: this.ruleForm.username, // ‘root”password’: this.ruleForm.password, // ‘123456”nickname’: this.ruleForm.nickname, // ‘jackson”phone’: this.ruleForm.phone, // ‘13800138001’};

                具體使用哪種做法,取決于服務器端的設計:

                – 如果服務器端處理請求的方法中,在參數(shù)前添加了`@RequestBody`,則允許使用以上第2種做法(JSON數(shù)據(jù))提交請求參數(shù),不允許使用以上第1種做法(使用`&`拼接)

                – 如果沒有使用`@RequestBody`,則只能使用以上第1種做法

                學習記錄,如有侵權(quán)請聯(lián)系刪除

                鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場,版權(quán)歸原作者所有,如有侵權(quán)請聯(lián)系管理員(admin#wlmqw.com)刪除。
                用戶投稿
                上一篇 2022年6月13日 15:28
                下一篇 2022年6月13日 15:29

                相關推薦

                • 分享4條發(fā)微商朋友圈的方法(微商朋友圈應該怎么發(fā))

                  對于微商朋友來說,朋友圈的重要性不言而喻了。 那么微商的朋友圈到底該怎么發(fā)呢? 為什么同樣是經(jīng)營一個朋友圈,有的微商看起來逼格滿滿,實際效果也不錯;而有的卻動都不動就被屏蔽甚至拉黑…

                  2022年11月27日
                • 30個無加盟費的項目(茶顏悅色奶茶店加盟費多少)

                  茶顏悅色又爆了,8月18日,茶顏悅色南京門店正式開業(yè),開張不到半小時,門店就人滿為患,消費者的購買熱情十分高漲,而由于人流量過大造成擁堵,茶顏悅色也不得不暫停營業(yè)。 當然,這里面排…

                  2022年11月27日
                • 存儲過程語法(sql server存儲過程語法)

                  今天小編給各位分享存儲過程語法的知識,其中也會對sql server存儲過程語法進行解釋,如果能碰巧解決你現(xiàn)在面臨的問題,別忘了關注本站,現(xiàn)在開始吧! oracle存儲過程基本語法…

                  2022年11月26日
                • 凈利潤率越高越好嗎(凈利潤率多少合適)

                  一、持續(xù)增收不增利,平均凈利潤率首次跌入個位數(shù) 2021年,增收不增利依舊是行業(yè)主流。具體來看,大部分企業(yè)營業(yè)收入呈增長態(tài)勢,E50企業(yè)平均同比增速達到17.3%,但是利潤增速則明…

                  2022年11月26日
                • 《寶可夢朱紫》獒教父屬性是什么?獒教父屬性一覽

                  寶可夢朱紫里獒教父是一只很強的寶可夢,很多玩家不清楚獒教父的屬性是什么樣的,下面就給大家?guī)韺毧蓧糁熳祥峤谈笇傩砸挥[,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 獒教父屬性一覽…

                  2022年11月25日
                • 《寶可夢朱紫》夢特性怎么獲得?隱藏特性獲取方法推薦

                  寶可夢朱紫里有很多寶可夢都是擁有夢特性會變強的寶可夢,很多玩家不知道夢特性怎么獲得,下面就給大家?guī)韺毧蓧糁熳想[藏特性獲取方法推薦,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 …

                  2022年11月25日
                • 《寶可夢朱紫》奇魯莉安怎么進化?奇魯莉安進化方法分享

                  寶可夢朱紫中的奇魯莉安要怎么進化呢?很多玩家都不知道,下面就給大家?guī)韺毧蓧糁熳掀骠斃虬策M化方法分享,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 奇魯莉安進化方法分享 奇魯莉安…

                  2022年11月25日
                • 《寶可夢朱紫》太晶化強力寶可夢推薦 太晶化哪些寶可夢最強?

                  寶可夢朱紫游戲中寶可夢種類繁多,不過有的寶可夢比較強,有的稍弱一些,那么太晶化化哪些寶可夢最強呢,為了便于大家更好的體驗游戲,這里給大家?guī)砹藢毧蓧糁熳咸Щ瘡娏毧蓧敉扑],一起來…

                  2022年11月25日
                • 寶可夢朱紫四大天王屬性怎么樣 四大天王屬性數(shù)值介紹

                  寶可夢朱紫四大天王屬性如何?四大天王的屬性數(shù)值玩家們還是挺好奇的,想要了解四大天王屬性的可以看看下面小編的介紹,小編會把四大天王的屬性數(shù)值全都分享在下面,各位趕緊來小編這里多了解一…

                  2022年11月25日
                • 寶可夢朱紫皮卡丘多少級進化 皮卡丘進化條件攻略

                  寶可夢朱紫皮卡丘進化條件是什么?皮卡丘怎么進化?作為最受歡迎的電屬性寶可夢,大家都很想知道皮卡丘進化的方法,今天小編這就在下面的攻略中分享皮卡丘進化條件,各位可以趕緊來小編這里了解…

                  2022年11月25日

                聯(lián)系我們

                聯(lián)系郵箱:admin#wlmqw.com
                工作時間:周一至周五,10:30-18:30,節(jié)假日休息