<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【進(jìn)階篇】Java 實際開發(fā)中積累的幾個小技巧(二)

      前言

      筆者目前從事一線 Java 開發(fā)今年是第 3 個年頭了,從 0-1 的 SaaS、PaaS 的項目做過,基于多租戶的標(biāo)準(zhǔn)化開發(fā)項目也做過,項目的 PM 也做過...

      在實際的開發(fā)中積累了一些技巧和經(jīng)驗,包括線上 bug 處理、日常業(yè)務(wù)開發(fā)、團(tuán)隊開發(fā)規(guī)范等等。現(xiàn)在在這里分享出來,作為成長的記錄和知識的更新,希望與大家共勉。

      免責(zé)聲明:以下所有demo、代碼和測試都是出自筆者本人的構(gòu)思和實踐,不涉及企業(yè)隱私和商業(yè)機(jī)密,屬于個人的知識積累分享。

      六、自定義注解

      Spring 中的自定義注解可以靈活地定制項目開發(fā)時需要的切面 AOP 操作,一般來說在接口處設(shè)置的自定義注解是使用的最多的。下面筆者以一個項目全局通用的接口請求操作日志持久化為例子,分享一下自定義注解開發(fā)的一些小技巧。

      6.1定義注解

      這一步先定義出具體的注解狀態(tài)和屬性:

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.METHOD)
      @Inherited
      public @interface OperateLog {
      
          /**
           * 線索Id
           */
          String trackId() default "";
      
          /**
           * 具體操作行為
           */
          OperationEnum operation();
      }
      

      其中的具體行為操作枚舉需要提前準(zhǔn)備好,方便后續(xù)切面內(nèi)的日志操作持久化:

      @Getter
      @RequiredArgsConstructor
      public enum OperationEnum {
      
          XX_MODULE_ADD("xx模塊","新增xx"),
          XX_MODULE_UPDATE("xx模塊","修改xx");
      
          private final String module;
      
          private final String detail;
      }
      

      6.2切面實現(xiàn)

      這一步是具體的切面實現(xiàn),切面實現(xiàn)的關(guān)鍵在于:切面在注解聲明方法的哪種順序執(zhí)行,即選擇 5 種通知的哪一種。

      對于日志記錄這種類型的,一般來說切面會在方法返回結(jié)果之后執(zhí)行(@AfterReturning),即操作有結(jié)果后再記錄日志;而像用戶登錄或者接口權(quán)限校驗的自定義注解,一般來說切面會在方法調(diào)用前(@Before)就執(zhí)行。具體切面里的邏輯如下:

      @Aspect
      @Component
      public class OperateLogAOP {
      
          @Resource
          private OperationLogService operationLogService;
      
          /**
           * 切面在方法返回結(jié)果之后執(zhí)行,即操作有結(jié)果后再記錄日志
           * @param joinPoint
           * @param operateLog
           */
          @AfterReturning(value = "@annotation(operateLog)")
          public void operateLogAopMethod(JoinPoint joinPoint, OperateLog operateLog){
              //從自定義注解中取出參數(shù)
              String trackId = operateLog.trackId();
              Assert.hasText(trackId, "trackId param error!");
              //處理參數(shù)的值,即輸入的業(yè)務(wù)id值
              MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
              Object[] args = joinPoint.getArgs();
              String businessLogId = (String) AopUtils.getFieldValue(args, methodSignature, trackId);
              //操作描述
              String module = operateLog.operation().getModule();
              String detail = operateLog.operation().getDetail();
              //獲取請求 http request
              HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
              //持久化入庫
              OperationLog operationLog = OperationLog.builder()
                      .trackId(businessLogId).module(module).detail(detail)
                      .ip(IpUtil.getUserIp(request)).createTime(new Date())
                      .operatorUuid(UserDataBuilder.get().getUserUuid())
                      .operatorName(UserDataBuilder.get().getUserName())
                      .build();
              operationLogService.save(operationLog);
          }
      }
      

      6.3業(yè)務(wù)使用

      前面兩步完成后,就到最后的業(yè)務(wù)使用了。一般來說日志類型的自定義注解會放在 Controller 層的接口前,具體示例如下:

          /**
           * 編輯
           * @return 是否成功
           */
          @PostMapping("update")
          @OperateLog(trackId = "studyDTO.id", operation = OperationEnum.XX_MODULE_UPDATE)
          public BaseResponse<Boolean> updateStudy(@RequestBody StudyDTO studyDTO) {
              return ResultUtils.success(studyService.updateStudy(studyDTO));
          }
      

      七、抽象類和接口

      為什么在業(yè)務(wù)設(shè)計的時候需要注意抽象類和接口的運(yùn)用呢?如果只是依靠類的單一范圍原則,那么業(yè)務(wù)的實現(xiàn)會擰成一大坨,并且代碼的耦合會變緊。

      抽象類非常適合多個子類共享共同特征和屬性,但也兼容自己獨(dú)有的行為情況,同時為子類的定制實現(xiàn)留出空間。

      而接口則是解耦的最基本工具,接口允許將方法的定義與其實現(xiàn)分開,這種分離使得多個不相關(guān)的類能夠?qū)崿F(xiàn)同一組方法,從而保證了項目中不同部分之間的相互通信。

      7.1隔離業(yè)務(wù)層與 ORM 層

      • Mongo 示例

        抽象類的繼承關(guān)系如下:

        @Service
        public class WorkerServiceImpl extends AbstractWorkerServiceImpl implements WorkerService {}
        
        public abstract class AbstractWorkerServiceImpl extends BaseServiceImpl<Worker, String> implements IWorkerService {}
        

        接口的繼承關(guān)系如下:

        public interface WorkerService extends IWorkerService {}
        
        public interface IWorkerService extends BaseService<Worker, String> {}
        

        底層的繼承和實現(xiàn):

        /**
         * 以下抽象類和接口中還有自定義的一些數(shù)據(jù)庫方法,與 MongoTemplate 和 MongoRepository 形成互補(bǔ)
         */
        public abstract class BaseServiceImpl<T, ID> implements BaseService<T, ID> {}
        
      • MySQL 示例

        至于 MySQL 可以直接引用 mybaitisplus 的包,里面有現(xiàn)成的實現(xiàn),都是一些數(shù)據(jù)庫語句的 Java 實現(xiàn)。必要的情況下還可以同時引入 mybaitis 包來處理一些復(fù)雜的 sql 語句。

        抽象類的繼承關(guān)系如下:

        @Service
        public class StudyServiceImpl extends ServiceImpl<StudyMapper, Study> implements StudyService {}
        

        接口的繼承關(guān)系如下:

        public interface StudyService extends IService<Study> {}
        

        底層的繼承和實現(xiàn):

        /**
         * 以下抽象類和接口都來源于 com.baomidou.mybatisplus 包
         */
        public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {}
        

      7.2隔離子系統(tǒng)的業(yè)務(wù)實現(xiàn)

      • facade模式

        facade 稱為外觀模式:為子系統(tǒng)中的各類(或方法)提供簡潔一致的入口,隱藏子系統(tǒng)的復(fù)雜性。facade 層也通常充當(dāng)一個中介的角色,為上層的調(diào)用者提供統(tǒng)一接口的同時,不直接暴露底層的實現(xiàn)細(xì)節(jié)。

        例如在遠(yuǎn)程調(diào)用時,facade 層可以提供一個顆粒度比較粗的接口,它負(fù)責(zé)將外部請求轉(zhuǎn)發(fā)給合適的服務(wù)進(jìn)行處理。

        service層,只關(guān)心數(shù)據(jù),在 service 內(nèi)直接注入mapper

        /**
         * 只關(guān)心數(shù)據(jù),本質(zhì)上是數(shù)據(jù)庫的一些操作
         */
        @Service
        public class PersonService extends ServiceImpl<PersonMapper, Person> {
            @Resource
            private PersonMapper mapper;
            //其它數(shù)據(jù)庫語句
            ...
        }
        

        facade 層,只關(guān)心業(yè)務(wù),在 facade內(nèi)直接注入 service

        /**
         * 只關(guān)心業(yè)務(wù),不繼承也不實現(xiàn),被 controller 層引用
         */
        @Service
        public class PersonFacade {
            @Resource
            private PersonService service;
            //業(yè)務(wù)具體方法邏輯
            ...
        }
        

        上述模式的優(yōu)點(diǎn)是將數(shù)據(jù)處理和業(yè)務(wù)處理明確地分開,業(yè)務(wù)、數(shù)據(jù)與視圖層的通信靠的是 Bean 注入的方式,并不是強(qiáng)依賴于類的繼承和接口實現(xiàn),對于外部來說很好地屏蔽了具體的實現(xiàn)邏輯。

        但是可能潛在的缺點(diǎn)也有:當(dāng)業(yè)務(wù)簡單的時候,facade 與 service 之間的邊界會比較模糊,即 facade 層的存在可能是沒有必要的。

      7.3選擇對比

      如果在實際項目里的話,這兩者只能選其一。

      筆者對于兩者在不同的項目中都使用過,實踐下來的建議是:選擇抽象類和接口做業(yè)務(wù)與數(shù)據(jù)的隔離。

      原因無它:抽象類和接口的搭配使用從本質(zhì)上詮釋了 Java 的繼承、封裝和多態(tài),與面向?qū)ο蟮乃枷胍幻}相承。


      文章小結(jié)

      作為開發(fā)技巧系列文章的第二篇,本文的內(nèi)容不多但貴在實用。在之后的文章中我會分享一些關(guān)于真實項目中處理高并發(fā)、緩存的使用、異步/解耦等內(nèi)容,敬請期待。

      那么今天的分享到這里就暫時結(jié)束了,如有不足和錯誤,還請大家指正。或者你有其它想說的,也歡迎大家在評論區(qū)交流!

      posted @ 2024-04-16 10:09  CodeBlogMan  閱讀(1395)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 中文字幕人妻中文AV不卡专区| 国产一区二区三区不卡视频| 日本一区二区三本视频在线观看| 色国产视频| 亚洲乱熟乱熟女一区二区| 国产精品国产主播在线观看| 久久99国产精品尤物| 男女激情一区二区三区| 91精品国产吴梦梦在线观看永久 | 二区三区亚洲精品国产| 日日猛噜噜狠狠扒开双腿小说| 猫咪社区免费资源在线观看| 日韩中文字幕人妻精品| 开心久久综合激情五月天| 国内永久福利在线视频图片| 国产精品成人午夜福利| 久久综合色一综合色88| 婷婷色综合成人成人网小说| 国偷自产一区二区三区在线视频| 99久久国产成人免费网站| 成全世界免费高清观看| 乱人伦人妻精品一区二区| 久久99精品久久久久久齐齐| 99精品久久久久久久婷婷| 国产一区二区av天堂热| 一区二区三区四区黄色网| 最新国产AV最新国产在钱| 欧美性猛交xxxx乱大交丰满| 四虎成人精品无码永久在线| 中文字幕一区二区三区麻豆| 高潮潮喷奶水飞溅视频无码| 午夜DY888国产精品影院| 国内外成人综合免费视频| 亚洲熟妇少妇任你躁在线观看无码| 国产成人综合色视频精品| 亚洲精品乱码久久久久久蜜桃 | 古田县| 日区中文字幕一区二区| 中文字幕精品人妻av在线| 2020国产欧洲精品网站| 日日躁夜夜躁狠狠躁超碰97|