SpringAop實操之記錄關鍵業務請求

來源:互聯網
上載者:User

標籤:tar   gty   完成   影響   怎麼   Fix   win   技術   需求   

  AOP,中文名稱,切面。在不影響業務代碼情況下,實現想要的功能,是個真炫酷的事。這不,最近來了個需求 。

  業務需求:希望記錄多個關鍵業務的請求及操作情況!這本身是ok的,但是這些需求規律性太強,咱們不能硬幹,否則就一個字low。

  aop是個好東西,記錄日誌是必須的。

  記錄資料也一樣的,那麼也是可以用aop來實現的,這裡藉助註解一起解決問題吧。

  因為是關鍵業務,所以意味著不是所有業務,那麼自然地就想到了,可以使用過濾的方式,也就是使用註解,如果有註解那麼就代表要記錄資料,否則不記錄。

  記錄過程需要做什麼呢?

  首先,因為是記錄業務資料,那麼我們可以抽象出一個方法出來,也就是 xxx.addRec()。也就是就切面裡只需調用記錄方法即可。

  那麼還剩下幾個問題,怎樣找到這個方法?參數的處理怎麼辦?需要使用規範限制嗎?

  找到這個方法,我們可以使用反射調用,當然需要註解進行調用的類和方法名稱(如果固定死則無需標註),用反射的好處就是自由,想怎麼寫就怎麼寫,要麼失敗要麼成功。但是low!

  參數判定,可以根據輸入的類,進行if..else.. 判定,然後直接處理相應參數即可。這樣可以很自由,想怎麼寫參數就怎麼寫參數。但是這樣的話,這個切面就和業務代碼完全耦合在一起了,還多了n多無謂的判斷。很low!

  那麼問題來了,怎樣才不low呢?

幾個理論可以借鑒下:

  1. 面向介面編程而非面向類編程;

  2. 使用枚舉值進行註解統一規劃;

  3. 使用泛型,進行參數上下限處理;

  4. 使用模板方法模式封裝參數;

  5. 使用線程池;

讓我們細看下~

  面向介面編程,即規定所有業務處理類都實現一個公用的介面,從而使方法不至於淩亂,定義一個清晰的介面,更易於理解含義。各實作類別只需關注自己的邏輯即可。
使用枚舉值進行註解參數的限定,可以使所有業務操作都在一處進行羅列,且都符合必須的規範。另外,當哪天發現參數無法滿足某些需求時,可方便地在該枚舉中加入相應參數以完成需求。
  使用泛型,將參數限定一定範圍內,如要求入庫參數必須繼承某個基類以實現統一參數處理,也防止了傳入任意參數而必須做相應轉換的效能消耗。
  使用模板方法模式進行參數封裝,因入參中都要求繼承一個基類,也就是具有共性的參數,所以應具備自動處理公用參數的能力,但是不可越權處理各業務實現的處理,應讓實作類別有能力自行處理個人化參數(實作類別也可以不處理)。交由實作類別處理個人化參數時,應使其具有絕對的能力,不應限制其發揮。
  使用線程池技術,將額外的工作交給額外的線程,從而使主業務不愛影響。
具體代碼擼一遍:
1. 註解開啟新篇章,無註解,不工作。

@Componentpublic class UserTestService {    // 添加註解,代表需要進行相應的邏輯處理    @BizRecordTrans(bizType = BizRecordTypeEnum.USER_ADD_FLOW)    public ResponseEntity<Boolean> addUser(UserlDto terminalReq) {        ResponseEntity<Boolean> ret = ResponseBuilder.buildResponse();        // 完成自己的業務...        return ret;    }}

2. 切面配置,做一個幕後的老兵。

@Component@Aspectpublic class BizRecordAop {    private Logger logger = LoggerFactory.getLogger(this.getClass());    @Around("execution(* com.xxx.dubbo.*.*(..))) and @annotation(com.xxx.spring.annotation.BizRecordTrans)")    public Object deal(ProceedingJoinPoint pjp) throws Throwable {        Object retVal = null;        String methodName = pjp.getSignature().getName();        Object[] args = pjp.getArgs();        Class<?> classTarget = pjp.getTarget().getClass();        Class<?>[] argTypes = ((MethodSignature) pjp.getSignature()).getParameterTypes();        Method objMethod = classTarget.getMethod(methodName, argTypes);        BizRecordTrans bizRecordTrans = objMethod.getAnnotation(BizRecordTrans.class);        BizRecordTypeEnum bizRecordTypeEnum = null;        if (BizRecordTrans != null) {            bizRecordTypeEnum = BizRecordTrans.bizType();        }        try {            retVal = pjp.proceed();        } catch (Exception e) {            // ignore            throw e;        } finally {            if (bizRecordTrans != null) {                Class<? extends SvcBaseEntity> bizRecEntityCls = bizRecordTypeEnum.getBizRecEntityCls();                SvcBaseEntity recEntity = (SvcBaseEntity) bizRecEntityCls.newInstance();                if (args[0] instanceof BaseDto) {                    BaseDto baseDto = (BaseDto) args[0];                    fillBaseFields(recEntity, baseDto);                }                try {                    ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) SpringContextsUtil.getBean("threadPoolTaskExecutor");                    executor.execute(() -> {                        BizRecBaseService<BaseEntity> recService = (BizRecBaseService<SvcBaseEntity>) SpringContextsUtil                            .getBean(bizRecordTypeEnum.getHandlerBeanName());                        recService.fitOwnParams(recEntity, args);                        recService.addRecord(recEntity);                    });                } catch (Exception e) {                    logger.error("發生異常", e);                }            }        }        return retVal;    }    /**     * 填充公用參數     */    private void fillBaseFields(BaseEntity baseEntity, BaseDto base) {        baseEntity.setUserId(base.getUserId());        baseEntity.setSeqId(base.getSeqId());        baseEntity.setAddIp(base.getAddIp());    }}

 

3. 寫一個基礎介面,讓業務去實現。

public interface BizRecordBaseService<T extends BaseEntity> {    /**     * 執行業務方法     */    public Integer addRecord(T bizRecord);    /**     * 個人化參數填充方法,選擇性實現     */    default void fixOwnParams(T bizRecord, Object[] rawData) {        System.out.println("可以不實現");    }}

 

4. 寫一個基礎類,讓所有其他業務實體繼承以實現公用參數的封裝。

@Datapublic class SvcBaseEntity {    /**     * ID      */    private Long id;        /**     * 使用者ID      */    private Long userId;    /**     * 請求序列id     */    private String seqId;    /**     * 添加ip     */    private String addIp;    }

5. 打一個組合拳,搞定。

  原理淺顯,易懂。效能嘛,也ok,無需太擔心。

  嘿! 每一個奇怪的業務,都是一次實踐的機會。

SpringAop實操之記錄關鍵業務請求

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.