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的执行顺序,不能跨类。

(0)

相关推荐