Spring 不仅为我们提供了IOC
, AOP
功能外,还在这个基础上提供了许多的功能,我们用的最多的可能就是 Spring MVC
了吧,但是让我们来看下spring-context
包,其中包含了缓存、调度、校验功能等等
这里主要想介绍一下Spring提供的观察者模式实现(事件发布监听)及异步方法执行,这些功能也都是基于AOP实现的
Spring 事件
观察者模式大家都了解,它可以解耦各个功能,但是自己实现的话比较麻烦,Spring为我们提供了一种事件发布机制,可以按需要发布事件,之后由监听此事件的类或方法来执行各自对应的功能,代码互相不影响,以后修改订单后续的逻辑时不会影响到订单创建,有点类似于使用MQ的感觉~
比如在配置中心apollo项目中,在portal创建了app后会发送app创建事件,监听此事件的逻辑处将此消息同步到各个环境的adminsevice中,大家有兴趣可以看下相关代码
现在我们来看看具体如何使用:假设一个下单场景,订单创建成功后可能有一些后续逻辑要处理,但是和创建订单本身没有关系,此时就可以在创建订单完成后,发送一个消息,又相应部分的代码进行监听处理,避免代码耦合到一起
首先创建对应的事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class OrderCreatedEvent extends ApplicationEvent { private String orderSn;
public OrderCreatedEvent(Object source) { super(source); }
public OrderCreatedEvent(Object source, String orderSn) { super(source); this.orderSn = orderSn; }
public String getOrderSn() { return this.orderSn; } }
|
现在还需要一个事件发布者和监听者,创建一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Service public class OrderService {
private final ApplicationEventPublisher applicationEventPublisher;
public OrderService(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; }
public void createOrder(String orderSn) {
applicationEventPublisher.publishEvent(new OrderCreatedEvent(this, orderSn)); } }
|
1 2 3 4 5 6 7 8 9
| @Component public class OrderEventListener {
@EventListener public void orderCreatedListener(OrderCreatedEvent orderCreatedEvent) { System.out.println("listen orderSn:" + orderCreatedEvent.getOrderSn() + " created"); } }
|
因为这里只是一个简单的例子,所以就用配置文件+main方法来执行了
1 2 3 4 5 6 7 8 9 10 11 12
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.zavier.spring" />
</beans>
|
1 2 3 4 5 6 7 8 9 10
| public class Main { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); OrderService orderService = applicationContext.getBean(OrderService.class);
orderService.createOrder("20191130123");
applicationContext.close(); } }
|
这时我们看下控制台,会打印出如下内容,证明监听者确实收到了消息
1
| listen orderSn:20191130123 created
|
简单的事件发布就完成了,其中的其他复杂逻辑由Spring替我们处理了
这里我们要注意一点:发布和监听后处理的逻辑是在一个线程中执行的,不是异步执行
异步方法
有时候我们为了提高响应速度,有些方法可以异步去执行,一般情况下我们可能是手动将方法调用提交到线程池中去执行,但是Spring 为我们提供了简化的写法,在开启了异步情况下,不用修改代码,只使用注解即可完成此功能
下面来看下具体实现(例子中提供了全局统一的线程池),我们接着上面的例子
添加如下配置(此处@slf4j使用了lombok的注解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Slf4j @Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer {
@Override public Executor getAsyncExecutor() { ExecutorService executorService = Executors.newFixedThreadPool(5); return executorService; }
@Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable throwable, Method method, Object... objects) { log.error("async execute error, method:{}, param:{}", method.getName(), JSON.toJSONString(objects), throwable); } }; } }
|
这时只需要在要异步执行的方法上添加@Async
注解即可异步执行, 如
1 2 3 4 5 6 7 8 9 10 11
| @Slf4j @Component public class OrderEventListener { @Async @EventListener public void orderCreatedListener(OrderCreatedEvent orderCreatedEvent) { log.info("orderCreatedListener threadId:{}", Thread.currentThread().getId()); log.info("listen orderSn:" + orderCreatedEvent.getOrderSn() + " created"); } }
|
为了验证结果,可以在orderService发送处和事件监听代码中添加日志,打印当前所在的线程,会发现它们并不是同一个线程,大家可以自行打印看下
注意:如果方法需要返回值的时候,需要让方法返回Future
注解实现可参考AsyncExecutionInterceptor
类, 同时异步方法也可使用CompletableFuture异步编排实现