前言
在我們開(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