SpringBoot事件的监听与发布
事件监听需要有事件本身,事件发布者,事件监听者三个重要的角色。需要使用事件监听解决业务,一般逃不过异步处理、降低耦合、随机触发等特点。
实现事件监听机制有四种常见的方法:
方法一:向ApplicationContext中添加监听器
方法二:@Component注解将监听器装载入spring容器
方法三:在application配置文件中配置监听器
方法四:@EventListener注解实现事件监听
建立Event
在config文件夹下创建listenerEvent文件夹,接着在listenerEvent文件下创建一个Event.java,继承ApplicationEvent抽象类,定义自己的构造器:
package com.javafamily.familydemo.config.listenerEvent;import org.springframework.context.ApplicationEvent;@SuppressWarnings("serial")public class Event extends ApplicationEvent { public Event(Object source) { super(source); } }12345678910复制代码类型:[java]
方法一:向ApplicationContext中添加监听器
listener需要实现ApplicationListener
在listenerEvent文件夹下创建Listener1.java:
package com.javafamily.familydemo.config.listenerEvent;import lombok.extern.slf4j.Slf4j;import org.springframework.context.ApplicationListener;@Slf4jpublic class Listener1 implements ApplicationListener<Event> { public void onApplicationEvent(Event event) { log.info(Listener1.class.getName() + "监听事件源:" + event.getSource()); } }1234567891011复制代码类型:[java]
在FamilyDemoApplication启动类中获取ConfigurableApplicationContext上下文,装载监听:
package com.javafamily.familydemo;import com.javafamily.familydemo.config.listenerEvent.Listener1;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.web.servlet.ServletComponentScan;import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication@MapperScan(basePackages = {"com.javafamily.familydemo.mapper"})// 扫描原生Servlet位置(监听器是Servlet规范中定义的一种特殊类)@ServletComponentScanpublic class FamilyDemoApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(FamilyDemoApplication.class, args); context.addApplicationListener(new Listener1()); } }1234567891011121314151617181920复制代码类型:[java]
方法二:@Component注解将监听器装载入spring容器
创建Listener2.java:
package com.javafamily.familydemo.config.listenerEvent;import lombok.extern.slf4j.Slf4j;import org.springframework.context.ApplicationListener;import org.springframework.stereotype.Component;@Slf4j// 实现Listener的注册,不需要启动类添加addApplicationListener()@Componentpublic class Listener2 implements ApplicationListener<Event> { public void onApplicationEvent(Event event) { log.info(Listener2.class.getName() + "监听事件源:" + event.getSource()); } }12345678910111213141516复制代码类型:[java]
方法三:在application.properties中配置监听器
创建Listener3.java:
package com.javafamily.familydemo.config.listenerEvent;import lombok.extern.slf4j.Slf4j;import org.springframework.context.ApplicationListener;@Slf4jpublic class Listener3 implements ApplicationListener<Event> { public void onApplicationEvent(Event event) { log.info(Listener3.class.getName() + "监听事件源:" + event.getSource()); } }1234567891011复制代码类型:[java]
在application.yml中进行配置:
context: listener: classes: com.javafamily.familydemo.config.listenerEvent.Listener3123复制代码类型:[java]
方法四:@EventListener注解实现事件监听
创建Listener4.java:
package com.javafamily.familydemo.config.listenerEvent;import lombok.extern.slf4j.Slf4j;import org.springframework.context.event.EventListener;import org.springframework.stereotype.Component;@Slf4j@Componentpublic class Listener4 { @EventListener public void listener(Event event) { log.info(Listener4.class.getName() + "监听事件源:" + event.getSource()); } }1234567891011121314复制代码类型:[java]
监听事件的发布
将ListenerController.java中的代码进行改写:
package com.javafamily.familydemo.controller;import com.javafamily.familydemo.config.listenerEvent.Event;import org.springframework.context.ApplicationContext;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;@RestControllerpublic class ListenerController { @Resource private ApplicationContext applicationContext; @GetMapping("/listener") public String Listener(HttpServletRequest request, HttpSession session) { applicationContext.publishEvent(new Event("测试event")); return "测试Listener"; } }1234567891011121314151617181920212223242526复制代码类型:[java]
运行代码,在浏览器访问http://localhost:8888/listener:
SpringBoot实现事件监听机制四种方法是有顺序的,无论执行多少次都是这个顺序。
应用启动的监听
SpringBoot提供了两个应用启动监听接口,CommandLineRunner、ApplicationRunner,使用他们的好处是可以方便的使用应用启动参数,根据参数不同做不同的初始化操作。
@Component
在config文件夹下创建CommandLineRunnerConfig.java:
package com.javafamily.familydemo.config;import lombok.extern.slf4j.Slf4j;import org.springframework.boot.CommandLineRunner;import org.springframework.stereotype.Component;import java.util.Arrays;@Componentpublic class CommandLineRunnerConfig implements CommandLineRunner { @Override public void run(String... args) { System.out.println("CommandLineRunner传入参数:{}" + Arrays.toString(args)); } }123456789101112131415复制代码类型:[java]
ApplicationRunner把参数放入ApplicationArguments,通过一下三个方法获取参数:
package com.javafamily.familydemo.config;import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.stereotype.Component;import java.util.Arrays;@Componentpublic class ApplicationRunnerConfig implements ApplicationRunner { @Override public void run(ApplicationArguments args) { System.out.println("ApplicationRunner参数名称: {}" + args.getOptionNames()); System.out.println("ApplicationRunner参数值: {}" + args.getOptionValues("id")); System.out.println("ApplicationRunner参数: {}" + Arrays.toString(args.getSourceArgs())); } }1234567891011121314151617复制代码类型:[java]
@Bean
创建BeanRunnerConfig.java,@Order可以设置执行顺序:
package com.javafamily.familydemo.config;import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.boot.CommandLineRunner;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.annotation.Order;import java.util.Arrays;@Configurationpublic class BeanRunnerConfig { @Bean @Order(1) public CommandLineRunner command1() { return new CommandLineRunner() { @Override public void run(String... args) { System.out.println("BeanCommandLineRunner command1()" + Arrays.toString(args)); } }; } @Bean @Order(2) public CommandLineRunner command2() { return new CommandLineRunner() { @Override public void run(String... args) { System.out.println("BeanCommandLineRunner command2()" + Arrays.toString(args)); } }; } @Bean @Order(3) public ApplicationRunner application1() { return new ApplicationRunner() { @Override public void run(ApplicationArguments args) { System.out.println("BeanApplicationRunner application1()" + Arrays.toString(args.getSourceArgs())); } }; } @Bean @Order(4) public ApplicationRunner application2() { return new ApplicationRunner() { @Override public void run(ApplicationArguments args) { System.out.println("BeanApplicationRunner application2()" + Arrays.toString(args.getSourceArgs())); } }; } }123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657复制代码类型:[java]
执行测试之前在EditConfiguration中进行配置:
点击ok,运行程序。
通过打印顺序我们可以看出:ApplicationRunner执行优先级高于CommandLineRunner,Component注解加implementsRunner接口的方式优先级高于以Bean的形式运行的Runner,Order注解只能保证同类的CommandLineRunner或ApplicationRunner的执行顺序,Order注解只能保证同类的CommandLineRunner/ApplicationRunner的执行顺序,不能跨类。