一.写在开始

​源码地址​

做业务系统过程中,难免出现非主线业务逻辑,如果长久将非主线业务代码 塞入主线业务代码中,最终会导致主线业务代码冗余又繁琐。后期难以维护不说,看起来也不够优雅清晰。

监听者模式,可以将主线与非主线逻辑 实现优雅的隔离。而今天 就是用 AOP的方式,来实现Spring的publish-event  监听者模式。

===============================

 

二.设计代码

1.大体结构

【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java

 

 【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义_02

【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_03

 

 

 

 

 

 

 

2.自定义枚举 MyEnum

该枚举的定义,即不同的 非主线的补充逻辑业务,就定义一个枚举值。


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


package com.sxd.swapping.enums;

import java.io.Serializable;

/**
* 自定义枚举
*/
public enum MyEnum implements Serializable {

ONE(1,"第一种补偿业务"),
TWO(2,"第二种补偿业务");


//值
int value;

//描述
String desc;

MyEnum(int value, String desc) {
this.value = value;
this.desc = desc;
}

public int getValue() {
return value;
}

public String getDesc() {
return desc;
}


/**
* 根据值 获取 枚举
* @param value
* @return
*/
public static MyEnum valueOf(int value) {
MyEnum[] instances = MyEnum.values();
for(MyEnum instance : instances){
if(instance.getValue() == value){
return instance;
}
}

return null;
}
}

View Code

 

 

3.自定义注解 @MyAnno

该注解就是在 主线流程的 被切面的方法上加的注解。

这里定义了两个属性,例如 MyEnum 即代表被切面的方法 需要做一种或多种 非主线的补充逻辑,就传入一个或多个枚举值即可。


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


package com.sxd.swapping.annotation;

import com.sxd.swapping.enums.MyEnum;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 自定义注解

*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MyAnno {

String myName();

MyEnum[] myEnums() default {};
}

View Code

 

 

4.线程池配置  application.properties

为自定义线程池做配置准备


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


#线程池配置
thread.pool.core.size=10
thread.pool.max.size=10
thread.pool.queue.capacity=10000
thread.pool.alive.seconds=1000

View Code

 

 

5.自定义线程池  ThreadPoolConfiguration

定义多种 独立的线程池。 在不同业务块种,使用各自独立的线程池,完成异步业务的执行。【在不同实例部署,即不同业务块在各自的线程池种获取线程完成业务执行,尽量避免线程资源的争抢】

这里需要定义线程池,是使用在 做非主线的补充逻辑时,进行异步的逻辑执行。


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


package com.sxd.swapping.thread;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@ConditionalOnProperty(name = "thread.pool.core.size")
public class ThreadPoolConfiguration {


@Value("${thread.pool.core.size}")
private Integer coreSize;

@Value("${thread.pool.max.size}")
private Integer maxSize;

@Value("${thread.pool.queue.capacity}")
private Integer queueCapacity;

@Value("${thread.pool.alive.seconds}")
private Integer keepAliveSeconds;

/**
* 业务1专用线程池
* @return
*/
@Bean(name = "taskExecutor1")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
poolTaskExecutor.setCorePoolSize(coreSize);
poolTaskExecutor.setMaxPoolSize(maxSize);
poolTaskExecutor.setQueueCapacity(queueCapacity);
poolTaskExecutor.setKeepAliveSeconds(keepAliveSeconds);
return poolTaskExecutor;
}

/**
* 业务2专用线程池
* @return
*/
@Bean(name = "taskExecutor2")
public ThreadPoolTaskExecutor taskExecutor2() {
ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
poolTaskExecutor.setCorePoolSize(coreSize);
poolTaskExecutor.setMaxPoolSize(maxSize);
poolTaskExecutor.setQueueCapacity(queueCapacity);
poolTaskExecutor.setKeepAliveSeconds(keepAliveSeconds);
return poolTaskExecutor;
}
}

View Code

 

 

6.自定义监听上下文MyOperationHandlerContext

该上下文其实很简单,就是将 不同业务枚举 和 对应得实现handler关系建立起来,以提供给 监听器 根据枚举上得 传入枚举项 ,找对应handler去执行具体实现。


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


package com.sxd.swapping.aop.context;

import com.sxd.swapping.aop.handler.MyAbstractOperationHandler;
import com.sxd.swapping.aop.handler.MyOneOperationHandler;
import com.sxd.swapping.aop.handler.MyTwoOperationHandler;
import com.sxd.swapping.enums.MyEnum;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
* 获取业务补充逻辑 上下文
*/
@Component
public class MyOperationHandlerContext implements ApplicationContextAware {

private Map<MyEnum, MyAbstractOperationHandler> operationHandlerMap;


@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (operationHandlerMap == null) {
operationHandlerMap = new HashMap<>();
operationHandlerMap.put(MyEnum.ONE,applicationContext.getBean(MyOneOperationHandler.class));
operationHandlerMap.put(MyEnum.TWO,applicationContext.getBean(MyTwoOperationHandler.class));
}
}

public MyAbstractOperationHandler getOperationHandler(MyEnum myEnum){
return operationHandlerMap.get(myEnum);
}

}

View Code

 

 

7.自定义监听事件 MyAspectEvent

该事件,即作为中间传递得过程变量,需要继承自 ApplicationEvent, 将注解定义属性 和 被切方法得入参 出参 等全部都作为承载体去流转


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


package com.sxd.swapping.aop.doamin;

import com.sxd.swapping.enums.MyEnum;
import org.springframework.context.ApplicationEvent;

public class MyAspectEvent extends ApplicationEvent {


MyEnum[] myEnums;

String myName;

Object[] requertParams;

Object returnVal;

public MyAspectEvent(Object source) {
super(source);
}




public MyEnum[] getMyEnums() {
return myEnums;
}

public void setMyEnums(MyEnum[] myEnums) {
this.myEnums = myEnums;
}

public String getMyName() {
return myName;
}

public void setMyName(String myName) {
this.myName = myName;
}

public Object[] getRequertParams() {
return requertParams;
}

public void setRequertParams(Object[] requertParams) {
this.requertParams = requertParams;
}

public Object getReturnVal() {
return returnVal;
}

public void setReturnVal(Object returnVal) {
this.returnVal = returnVal;
}
}

View Code

 

 

 

8.AOP切面编程 + Spring的publish-event  核心实现  MyAspect

该示例中,是定在 被切方法执行后,进入切面, 组装监听事件 MyAspectEvent, 最后通过 Spring的publish-event 机制,将监听事件推送出去。


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


package com.sxd.swapping.aop.aspect;

import com.sxd.swapping.annotation.MyAnno;
import com.sxd.swapping.aop.doamin.MyAspectEvent;
import com.sxd.swapping.enums.MyEnum;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
* AOP 切面编程
* Spring的publish-event 监听者模式
*
*/
@Component
@Aspect
public class MyAspect implements ApplicationContextAware {

private ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}


//切点在加了该注解的方法上
@Pointcut("@annotation(com.sxd.swapping.annotation.MyAnno)")
public void myCutPoint(){
}

/**
* 方法执行后 进入切面
*
* 监听者模式 将事件发布出去
*
* @param joinPoint 切点
* @param returnVal 被切方法的范围值
*/
@AfterReturning(value = "myCutPoint()", returning = "returnVal")
public void afterMethodDo(JoinPoint joinPoint,Object returnVal){
//被切方法的入参
Object[] args = joinPoint.getArgs();
//被代理方法信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
//获取方法上的自己的注解 及 信息
MyAnno annotation = method.getAnnotation(MyAnno.class);
MyEnum[] myEnums = annotation.myEnums();
String myName = annotation.myName();


MyAspectEvent event = new MyAspectEvent(applicationContext);
event.setRequertParams(args);
event.setReturnVal(returnVal);
event.setMyName(myName);
event.setMyEnums(myEnums);

// 发布事件对象
applicationContext.publishEvent(event);
}


}

View Code

 

 

 

9.自定义抽象逻辑处理类MyAbstractOperationHandler

抽象类,具体实现类下一点


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


package com.sxd.swapping.aop.handler;

import com.sxd.swapping.aop.doamin.MyAspectEvent;

/**
* 抽象的业务补充逻辑类
*/
public abstract class MyAbstractOperationHandler {

/**
* 具体的业务处理方法
* 交由不同业务的具体子类去 实现各自的业务逻辑
*
* @param operateEvent
*/
public abstract void handleBusiness(MyAspectEvent operateEvent);
}

View Code

 

 

 

10.具体各种补充业务的逻辑处理类

每种补充逻辑业务  具体的代码实现handler


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


package com.sxd.swapping.aop.handler;

import com.sxd.swapping.aop.doamin.MyAspectEvent;
import org.springframework.stereotype.Component;

/**
* 第一种业务上补充逻辑
*/
@Component
public class MyOneOperationHandler extends MyAbstractOperationHandler {

@Override
public void handleBusiness(MyAspectEvent operateEvent) {

String myName = operateEvent.getMyName();

//获取到切面方法的入参
Object[] requertParams = operateEvent.getRequertParams();

//获取到切面方法的出参
Object returnVal = operateEvent.getReturnVal();

System.out.println("第一种补充业务-打印方法入参:" + requertParams[0].toString());
System.out.println("做第一种补充业务:"+myName);
System.out.println("第一种补充业务-打印方法出参:"+ returnVal.toString());


}

}

View Code

【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


package com.sxd.swapping.aop.handler;

import com.sxd.swapping.aop.doamin.MyAspectEvent;
import org.springframework.stereotype.Component;

/**
* 第二种业务上的补充逻辑
*/
@Component
public class MyTwoOperationHandler extends MyAbstractOperationHandler {


@Override
public void handleBusiness(MyAspectEvent operateEvent) {


String myName = operateEvent.getMyName();

//获取到切面方法的入参
Object[] requertParams = operateEvent.getRequertParams();

//获取到切面方法的出参
Object returnVal = operateEvent.getReturnVal();

System.out.println("第二种补充业务-打印方法入参:" + requertParams[0].toString());
System.out.println("做第二种补充业务:"+myName);
System.out.println("第二种补充业务-打印方法出参:"+ returnVal.toString());

}
}

View Code

 

 

 

11.自定义监听器MyEventListener

即Spring的publish-event 的监听接收者。接收到 push出来的event之后,根据不同枚举,从上下文中取到对应实现的handler,异步 执行具体的业务逻辑。


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


package com.sxd.swapping.aop.listener;


import com.sxd.swapping.aop.context.MyOperationHandlerContext;
import com.sxd.swapping.aop.doamin.MyAspectEvent;
import com.sxd.swapping.aop.handler.MyAbstractOperationHandler;
import com.sxd.swapping.enums.MyEnum;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
*监听者模式 的监听者对象
*/
@Component
public class MyEventListener {

@Resource
private MyOperationHandlerContext operationHandlerContext;

@Resource
private ThreadPoolTaskExecutor taskExecutor1;

//使用@Async指定线程池 的 异步调用 https://www.jb51.net/article/105214.htm
@Async("taskExecutor1")
@EventListener
public void listener(MyAspectEvent myAspectEvent){
//获取到 自定义注解上的属性
MyEnum[] myEnums = myAspectEvent.getMyEnums();

//根据自定义注解 使用处 指定的补充事件枚举项,分别执行 对应的具体补充事件handler实现的逻辑
for (MyEnum myEnum : myEnums) {
MyAbstractOperationHandler operationHandler = operationHandlerContext.getOperationHandler(myEnum);
taskExecutor1.execute(() -> operationHandler.handleBusiness(myAspectEvent));
}

}
}

View Code

 

 

 

到这里,基本的配置定义就完成了

======================================

下面开始具体的使用。

 

12.controller入口


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


@RestController
public class FirstController {

@Autowired
BaseService baseService;

@RequestMapping(value = "/aoptest", method = {RequestMethod.GET})
public String myAopTest(){
baseService.saveBaseInfo("方法入参");
return "测试成功";
}

}

View Code

 

 

13.Service 实际被自定义注解切面的方法

切面上,通过自定义注解,将 不同业务对应的枚举定义好,进入切面后,取枚举,做不同业务。


【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义注解_04【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_java_05


@Service
public class BaseServiceImpl implements BaseService {



@Override
@MyAnno(myName = "德玛西亚", myEnums = {MyEnum.ONE,MyEnum.TWO})
public String saveBaseInfo(String param) {
System.out.println("进入被切方法");
return "方法出参";
}

}

View Code

 

 

 

 

三.启动验证

1.PostMan 发起请求

【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_自定义_30

 

 

 

2.异步完成 补充逻辑处理

【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event  实现 监听者模式,实现补充业务逻辑开发_线程池_31