聊聊如何讓你的業務程式碼具有可擴充套件性

linyb極客之路發表於2022-04-02

前言

在我們開發過程中,會經常碰到這麼一些需求,比如在在主流程執行前,要做一些前置事件,在主流程執行之後,做一些收尾工作。對一些新手程式設計師,他可能會直接寫類似如下的程式碼

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

對有一定工作經驗的程式設計師,他可能會用AOP或者用一些設計模式比如模板模式。那我們今天來聊聊下使用spring + spi + aop + 責任鏈來實現上面的需求

程式碼實現過程分析

假設主流程只需做一次前置處理和一次後置處理,則虛擬碼如下

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

此時我們可以用模板模式或者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
    }
}

如果對這種寫法不適應,可以採用@Aspect + @Around方式,效果一個樣。

當主流程需要多次前置處理和多次後置處理時,我們的程式碼可能就變成

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
    }
}

此時這些前置處理或者後置處理看起來就像是一條鏈,於是我們就可以考慮採用一些設計模式比如責任鏈或者採用管道模式。本示例我們使用責任鏈模式

程式碼實現

1、建立處理器介面
public interface AbstarctHandler extends Ordered {

    /**
     * 預處理回撥,實現服務的預處理
     * @return true表示流程繼續,false表示流程中斷,不會繼續呼叫其他處理器或者服務
     */
    default boolean preHandler(Invocation invocation){
        return true;
    }

    /**
     * 整個請求處理完畢回撥方法。類似try-catch-finally中的finally。多個afterCompletion按倒序輸出
     */
    default void afterCompletion(Invocation invocation){}


}
2、建立處理器鏈
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、業務邏輯和責任鏈整合
@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) {
             if(!abstarctHandler.preHandler(invocation)){
                 isCanExec = false;
                 break;
             }
            canExecCount++;
        }

           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、建立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、配置切點
    @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、編寫業務服務
public interface HelloService {

    String sayHello(String username);
}
@Service
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String username) {
        return "hello : " + username;
    }
}
2、編寫處理器

一種通過@Component

@Component
public class HelloServiceNameInterceptor implements AbstarctHandler {

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

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

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

一種通過SPI

public class HelloServiceSpiInterceptor implements AbstarctHandler {

    @Override
    public boolean preHandler(Invocation invocation) {
        Object[] args = invocation.getArgs();
        System.out.println("引數轉換-->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("引數轉換-->afterCompletion:" + Arrays.toString(invocation.getArgs()));
    }

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

配置SPI

內容如下

com.github.lybgeek.cor.test.interceptor.HelloServiceSpiInterceptor
3、配置切點表示式
lybgeek:
  pointcut:
    expression: execution(* com.github.lybgeek.cor.test.service..*.*(..))
4、測試

觀察控制檯

發現處理器正常工作

總結

所謂的可擴充套件,指在新增功能時,不需要或者少修改原有的功能。用設計原則來講就是對修改關閉,對擴充套件開放。本文的示例如果心細的朋友就會發現,這跟springmvc的攔截器實現是很像的

demo連結

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

相關文章