聊聊如何讓你的業(yè)務(wù)代碼具有可擴(kuò)展性

前言

在我們開(kāi)發(fā)過(guò)程中,會(huì)經(jīng)常碰到這么一些需求,比如在在主流程執(zhí)行前,要做一些前置事件,在主流程執(zhí)行之后,做一些收尾工作。對(duì)一些新手程序員,他可能會(huì)直接寫類似如下的代碼

  public void execute(){
        doBefore();
        doBiz();
        doAfter();
    }

對(duì)有一定工作經(jīng)驗(yàn)的程序員,他可能會(huì)用AOP或者用一些設(shè)計(jì)模式比如模板模式。那我們今天來(lái)聊聊下使用spring + spi + aop + 責(zé)任鏈來(lái)實(shí)現(xiàn)上面的需求

代碼實(shí)現(xiàn)過(guò)程分析

假設(shè)主流程只需做一次前置處理和一次后置處理,則偽代碼如下

  public void execute(){
        doBefore();
        doBiz();
        doAfter();
    }

此時(shí)我們可以用模板模式或者AOP,這邊我們采用AOP。其偽代碼如下

public class CorMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
       
        try {
            doBefore();
            Object result = invocation.proceed();
            return result;
        } catch (Throwable throwable) {
           log.error("{}",e);
        } finally {
            doAfter();
        }

        return null
    }
}

如果對(duì)這種寫法不適應(yīng),可以采用@Aspect + @Around方式,效果一個(gè)樣。

當(dāng)主流程需要多次前置處理和多次后置處理時(shí),我們的代碼可能就變成

public class CorMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
       
        try {
            doBefore();
            doBefore();
            doBefore();
            ....
            Object result = invocation.proceed();
            return result;
        } catch (Throwable throwable) {
           log.error("{}",e);
        } finally {
            doAfter();
            doAfter();
            doAfter();
            ...
        }

        return null
    }
}

此時(shí)這些前置處理或者后置處理看起來(lái)就像是一條鏈,于是我們就可以考慮采用一些設(shè)計(jì)模式比如責(zé)任鏈或者采用管道模式。本示例我們使用責(zé)任鏈模式

代碼實(shí)現(xiàn)

1、創(chuàng)建處理器接口

public interface AbstarctHandler extends Ordered {

    /**
     * 預(yù)處理回調(diào),實(shí)現(xiàn)服務(wù)的預(yù)處理
     * @return true表示流程繼續(xù),false表示流程中斷,不會(huì)繼續(xù)調(diào)用其他處理器或者服務(wù)
     */
    default boolean preHandler(Invocation invocation){
        return true;
    }

    /**
     * 整個(gè)請(qǐng)求處理完畢回調(diào)方法。類似try-catch-finally中的finally。多個(gè)afterCompletion按倒序輸出
     */
    default void afterCompletion(Invocation invocation){}


}

2、創(chuàng)建處理器鏈

public class MethodInterceptorChain {

    private final List<AbstarctHandler> abstarctHandlers = new ArrayList<>();


    public void addHandler(AbstarctHandler handler){
        abstarctHandlers.add(handler);
    }

    public List<AbstarctHandler> getHanlders(){
        if(CollectionUtils.isEmpty(abstarctHandlers)){
            return Collections.emptyList();
        }
        AnnotationAwareOrderComparator.sort(abstarctHandlers);
        return Collections.unmodifiableList(abstarctHandlers);
    }

}

3、業(yè)務(wù)邏輯和責(zé)任鏈整合

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CorHandlerInterceptor {

    private MethodInterceptorChain chain;


    public Object invoke(Invocation invocation) throws Exception {
        List<AbstarctHandler> abstarctHandlers = chain.getHanlders();
        if(CollectionUtils.isEmpty(abstarctHandlers)){
            invocation.invoke();
        }

        boolean isCanExec = true;
        int canExecCount = 0;
        for (AbstarctHandler abstarctHandler : abstarctHandlers) {
                 canExecCount++;
             if(!abstarctHandler.preHandler(invocation)){
                 isCanExec = false;
                 break;
             }
        
        }

           try{
               if(isCanExec){
                   return invocation.invoke();
               }
           }catch (Exception e){
               throw new Exception(e);
           }finally {
               for (int i = 0; i < canExecCount; i++) {
                   int j = canExecCount - i - 1;
                   abstarctHandlers.get(j).afterCompletion(invocation);
               }
           }

        return null;
    }



}


4、創(chuàng)建AOP切面

public class CorMethodInterceptor implements MethodInterceptor {

    private CorHandlerInterceptor corHandlerInterceptor;

    public CorMethodInterceptor(CorHandlerInterceptor corHandlerInterceptor) {
        this.corHandlerInterceptor = corHandlerInterceptor;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Invocation invoker = Invocation.builder()
                .args(invocation.getArguments())
                .method(invocation.getMethod())
                .target(invocation.getThis()).build();

        return corHandlerInterceptor.invoke(invoker);
    }
}

5、配置切點(diǎn)

    @Bean
    @ConditionalOnMissingBean
    public AspectJExpressionPointcutAdvisor aspectJExpressionPointcutAdvisor(PointcutProperites pointcutProperites, CorHandlerInterceptor corHandlerInterceptor){
        AspectJExpressionPointcutAdvisor aspectJExpressionPointcutAdvisor = new AspectJExpressionPointcutAdvisor();
        aspectJExpressionPointcutAdvisor.setExpression(pointcutProperites.getExpression());
        aspectJExpressionPointcutAdvisor.setAdvice(new CorMethodInterceptor(corHandlerInterceptor));
        return aspectJExpressionPointcutAdvisor;
    }

6、處理器注入spring

  @Bean
    @ConditionalOnMissingBean
    public CorHandlerInterceptor corHandlerInterceptor(ObjectProvider<List<AbstarctHandler>> provider){
        MethodInterceptorChain methodInterceptorChain = new MethodInterceptorChain();
        loadedHandlerBySpring(provider, methodInterceptorChain);
        loadedHanlderBySpi(methodInterceptorChain);
        CorHandlerInterceptor corHandlerInterceptor = new CorHandlerInterceptor();
        corHandlerInterceptor.setChain(methodInterceptorChain);
        return corHandlerInterceptor;
    }

    @Bean
    @ConditionalOnMissingBean
    public DefaultHandler defaultHandler(){
        return new DefaultHandler();
    }

    private void loadedHanlderBySpi(MethodInterceptorChain methodInterceptorChain) {
        ServiceLoader<AbstarctHandler> serviceLoader = ServiceLoader.load(AbstarctHandler.class);
        Iterator<AbstarctHandler> iterator = serviceLoader.iterator();
        while(iterator.hasNext()){
            AbstarctHandler abstarctHandler = iterator.next();
            log.info("load hander by spi -> 【{}】",abstarctHandler.getClass().getName());
            methodInterceptorChain.addHandler(abstarctHandler);
        }
    }


    private void loadedHandlerBySpring(ObjectProvider<List<AbstarctHandler>> provider, MethodInterceptorChain methodInterceptorChain) {
        List<AbstarctHandler> getListBySpring = provider.getIfAvailable();
        if(!CollectionUtils.isEmpty(getListBySpring)){
            for (AbstarctHandler abstarctHandler : getListBySpring) {
                log.info("load hander by spring -> 【{}】",abstarctHandler.getClass().getName());
                methodInterceptorChain.addHandler(abstarctHandler);
            }
        }
    }

示例演示

1、編寫業(yè)務(wù)服務(wù)

public interface HelloService {

    String sayHello(String username);
}

@Service
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String username) {
        return "hello : " + username;
    }
}

2、編寫處理器

一種通過(guò)@Component

@Component
public class HelloServiceNameInterceptor implements AbstarctHandler {

    @Override
    public boolean preHandler(Invocation invocation) {
        Object[] args = invocation.getArgs();
        System.out.println("名稱校驗(yàn)-->preHandler");
        for (Object arg : args) {
            if("張三".equals(arg)){
                return false;
            }
        }
        return true;
    }

    @Override
    public void afterCompletion(Invocation invocation) {
        System.out.println("名稱校驗(yàn)-->afterCompletion:" + Arrays.toString(invocation.getArgs()));
    }

    @Override
    public int getOrder() {
        return 102;
    }
}

一種通過(guò)SPI

public class HelloServiceSpiInterceptor implements AbstarctHandler {

    @Override
    public boolean preHandler(Invocation invocation) {
        Object[] args = invocation.getArgs();
        System.out.println("參數(shù)轉(zhuǎn)換-->preHandler");
        for (int i = 0; i < args.length; i++) {
            if("lisi".equals(args[i])){
                args[i] = "李四";
            }
        }
        return true;
    }

    @Override
    public void afterCompletion(Invocation invocation) {
        System.out.println("參數(shù)轉(zhuǎn)換-->afterCompletion:" + Arrays.toString(invocation.getArgs()));
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

配置SPI
[圖片上傳失敗...(image-d1bd04-1648871763464)]
內(nèi)容如下

com.github.lybgeek.cor.test.interceptor.HelloServiceSpiInterceptor

3、配置切點(diǎn)表達(dá)式

lybgeek:
  pointcut:
    expression: execution(* com.github.lybgeek.cor.test.service..*.*(..))

4、測(cè)試

觀察控制臺(tái)
[圖片上傳失敗...(image-9ae1f3-1648871763464)]
發(fā)現(xiàn)處理器正常工作

總結(jié)

所謂的可擴(kuò)展,指在新增功能時(shí),不需要或者少修改原有的功能。用設(shè)計(jì)原則來(lái)講就是對(duì)修改關(guān)閉,對(duì)擴(kuò)展開(kāi)放。本文的示例如果心細(xì)的朋友就會(huì)發(fā)現(xiàn),這跟springmvc的攔截器實(shí)現(xiàn)是很像的

demo鏈接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-cor

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容