使用 ApplicationEvent 和 Listener 快速实现业务解耦(观察者模式)

ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。

通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。
 

Spring 提供了以下的标准事件

序号 Spring 内置事件 & 描述
1 ContextRefreshedEvent ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。
2 ContextStartedEvent当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。
3 ContextStoppedEvent当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。
4 ContextClosedEvent当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。
5 RequestHandledEvent这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。

案例实战

用户实体对象

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private long id;
    private String userName;
    private String password;
    private String email;
    private String address;
}

创建注册事件

public class RegisterEvent extends ApplicationEvent {
    @Getter
    @Setter
    private User user;

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

    public RegisterEvent(Object source, User user) {
        super(source);
        this.user = user;
    }

}

监听事件

方式1-实现 ApplicationListener接口

@Component
@Slf4j
public class RegisterListener implements ApplicationListener<RegisterEvent> {
    /**
     * 实现监听
     *
     * @param event 事件
     */
    @Override
    public void onApplicationEvent(RegisterEvent event) {
        User user = event.getUser();

        log.info("注册用户信息=[{}]", JSON.toJSONString(user));
    }
}

方式2-基于EventListener注解实现

@Component
@Slf4j
public class Listeners {

    @EventListener
    public void sendEmail(RegisterEvent registerEvent) {
        User user = registerEvent.getUser();

        log.info("邮件通知-注册用户信息=[{}]", JSON.toJSONString(user));
    }

    @EventListener
    public void shortMsg(RegisterEvent registerEvent) {
        User user = registerEvent.getUser();

        log.info("短信通知-注册用户信息=[{}]", JSON.toJSONString(user));
    }

}

方式3-实现SmartApplicationListener接口

@Component
@Slf4j
public class SmartListener implements SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        // 只有 RegisterEvent 监听类型才会执行下面逻辑
        return eventType == RegisterEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> sourceType) {
        // 只有在 UserService 内发布的 RegisterEvent 事件时才会执行下面逻辑
        return sourceType == UserService.class;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        // 转换事件类型
        RegisterEvent registerEvent = (RegisterEvent) event;

        //获取注册用户对象信息
        User user = registerEvent.getUser();

        log.info("Smart-注册用户信息=[{}]", JSON.toJSONString(user));
    }

    /**
     * 数值越小证明优先级越高, 执行顺序越靠前
     *
     * @return rlt
     */
    @Override
    public int getOrder() {
        return 10;
    }
}

方式4-使用@Async实现异步监听

@Component
@Slf4j
public class AsyncRegisterListener implements ApplicationListener<RegisterEvent> {
    /**
     * 实现监听
     *
     * @param event 事件
     */
    @Override
    @Async
    public void onApplicationEvent(RegisterEvent event) {
        try {
            log.info("模拟耗时的业务处理, 休眠10秒");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        User user = event.getUser();

        log.info("异步通知-注册用户信息=[{}]", JSON.toJSONString(user));
    }
}

异步线程池配置

@Configuration
@EnableAsync
public class ListenerAsyncConfiguration implements AsyncConfigurer {

    /**
     * 获取异步线程池执行对象
     *
     * @return executor
     */
    @Override
    public Executor getAsyncExecutor() {
        // 使用Spring内置线程池任务对象
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();

        // 设置线程池参数
        taskExecutor.setCorePoolSize(10);
        taskExecutor.setMaxPoolSize(20);
        taskExecutor.setQueueCapacity(25);
        taskExecutor.setThreadNamePrefix("notifyThread-");

        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }

}

Springboot 使用 Async (内置线程池)执行异步任务

发布事件

@Service
@Slf4j
public class UserService {
    private final ApplicationContext applicationContext;

    @Autowired
    public UserService(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public void register(User user){
        // 业务处理
        log.info("准备进行业务处理...");

        // 持久化用户注册数据
        log.info("准备持久化用户注册数据...");

        // 发布用户注册事件
        applicationContext.publishEvent(new RegisterEvent(this, user));
    }
}

接口调用

@RestController
@Slf4j
public class MyController {
	@Autowired
    private UserService userService;

    @RequestMapping("register")
    public Response register() {
        User user = new User(1, "Jaemon", "123456", "[email protected]", "pt");
        userService.register(user);
        return Response.success();
    }
}

程序运行输出

2019-12-09 15:46:16.031 [Answer-AI-L] [jaemon-service]  INFO 16332 -- [nio-8888-exec-1] com.jaemon.service.UserService  :[30] 准备进行业务处理...
2019-12-09 15:46:16.032 [Answer-AI-L] [jaemon-service]  INFO 16332 -- [nio-8888-exec-1] com.jaemon.service.UserService  :[33] 准备持久化用户注册数据...
2019-12-09 15:46:16.132 [Answer-AI-L] [jaemon-service]  INFO 16332 -- [nio-8888-exec-1] com.jaemon.client.Listeners     :[25] 邮件通知-注册用户信息=[{"address":"pt","email":"[email protected]","id":1,"password":"123456","userName":"Jaemon"}]
2019-12-09 15:46:16.132 [Answer-AI-L] [jaemon-service]  INFO 16332 -- [nio-8888-exec-1] com.jaemon.client.Listeners     :[33] 短信通知-注册用户信息=[{"address":"pt","email":"[email protected]","id":1,"password":"123456","userName":"Jaemon"}]
2019-12-09 15:46:16.132 [Answer-AI-L] [jaemon-service]  INFO 16332 -- [nio-8888-exec-1] c.jaemon.client.SmartListener   :[42] Smart-注册用户信息=[{"address":"pt","email":"[email protected]","id":1,"password":"123456","userName":"Jaemon"}]
2019-12-09 15:46:16.135 [Answer-AI-L] [jaemon-service]  INFO 16332 -- [nio-8888-exec-1] c.j.client.RegisterListener   :[29] 注册用户信息=[{"address":"pt","email":"[email protected]","id":1,"password":"123456","userName":"Jaemon"}]
2019-12-09 15:46:16.135 [Answer-AI-L] [jaemon-service]  INFO 16332 -- [ notifyThread-1] c.j.client.AsyncRegisterListener       :[30] 模拟耗时的业务处理, 休眠102019-12-09 15:46:26.135 [Answer-AI-L] [jaemon-service]  INFO 16332 -- [ notifyThread-1] c.j.client.AsyncRegisterListener       :[39] 异步通知-注册用户信息=[{"address":"pt","email":"[email protected]","id":1,"password":"123456","userName":"Jaemon"}]

参考网址

发布了152 篇原创文章 · 获赞 27 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/u010979642/article/details/103458843