foreword
In our development process, we often encounter such requirements, such as doing some pre-events before the execution of the main process, and doing some finishing work after the execution of the main process. For some novice programmers, he may directly write code like the following
public void execute(){
doBefore();
doBiz();
doAfter();
}
For programmers with certain work experience, he may use AOP or some design patterns such as template pattern. Then let's talk today about using spring + spi + aop + chain of responsibility to achieve the above requirements
Code implementation process analysis
Assuming that the main process only needs to do one preprocessing and one postprocessing, the pseudo code is as follows
public void execute(){
doBefore();
doBiz();
doAfter();
}
At this point we can use template mode or AOP, here we use AOP. Its pseudo code is as follows
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
}
}
If you are not used to this way of writing, you can use the @Aspect + @Around method, and the effect is the same.
When the main process requires multiple preprocessing and multiple postprocessing, our code may become
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
}
}
At this point, these pre-processing or post-processing looks like a chain, so we can consider adopting some design patterns such as chain of responsibility or pipeline pattern. In this example we use the Chain of Responsibility pattern
Code
1. Create a processor interface
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. Create a processor chain
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. Business logic and chain of responsibility integration
@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. Create AOP slices
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. Configure the pointcut
@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. The processor injects 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);
}
}
}
demo
1. Write business services
public interface HelloService {
String sayHello(String username);
}
@Service
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String username) {
return "hello : " + username;
}
}
2. Write the processor
one via @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;
}
}
A via 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;
}
}
Configure SPI
The content is as follows
com.github.lybgeek.cor.test.interceptor.HelloServiceSpiInterceptor
3. Configure the pointcut expression
lybgeek:
pointcut:
expression: execution(* com.github.lybgeek.cor.test.service..*.*(..))
4. Test
watch console
Found that the processor is working properly
Summarize
The so-called extensibility means that when new functions are added, there is no need or less modification of the original functions. In terms of design principles, it is closed for modification and open for extension. If you are careful friends of the example in this article, you will find that this is very similar to the interceptor implementation of springmvc
demo link
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-cor
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。