SpringBoot组件
过滤器
使用
- 实现filter接口并实现doFilter方法
- 添加过滤器有两种方式
- 使用@WebFilter+@Component+@order指定顺序
- 或者使用@WebFilter+在主程序类上添加@ServletComponentScan
- 使用@Bean + FilterRegistrationBean
- 使用@WebFilter+@Component+@order指定顺序
- 其实过滤器应该在spring容器之前的,但是使用了注解方式交给容器管理之后他好像和spring的拦截器也没有什么不同了
- 过滤器在项目启动时就会实例化以及初始化,在服务关闭的时候销毁
注解方式
@Slf4j
@Component
// 使用order指定顺序
@Order(3)
@WebFilter(filterName = "studentLogFilter",urlPatterns = {"/student/**"})
public class HttpRequestLogFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("==========================studentLogFilter1 filter================>>>>>>>>>>");
HttpServletRequest request = (HttpServletRequest) servletRequest;
// 获取请求路径
String contextPath = request.getContextPath();
String requestURI = request.getRequestURI();
String queryString = request.getQueryString();
String servletPath = request.getServletPath();
log.info("studentLogFilter contextPath is {},requestURI is {},queryString is {},servletPath is {}",contextPath,requestURI,queryString,servletPath);
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()){
String s = headerNames.nextElement();
String header = request.getHeader(s);
log.info("studentLogFilter head message {} = {}",s,header);
}
// 读取body中的参数 但是SelvetRequest的inputStream只能够读取一次
// 使用ContentCachingRequestWrapper对request进行包装 ContentCachingRequestWrapper能够让inputStream重复读取
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request, 0);
// 读取
byte[] bytes = requestWrapper.getContentAsByteArray();
if(bytes.length > 0){
log.info("studentLogFilter request body is {}",new String(bytes));
}
filterChain.doFilter(requestWrapper,servletResponse);
}
}
@Bean的方式
public class MyConfig {
@Bean
public FilterRegistrationBean<HttpRequestLog3Filter> studentLogFilter3(){
// 现在做出一个选择吧,按照网上一模一样写的 但是不知道为啥不好用
// 1.找出原因(花费时间)
// 2.暂时跳过 反正还有注解版可以使用 √ FilterRegistrationBean<HttpRequestLog3Filter> filter = new FilterRegistrationBean<>();
//设置过滤器实例
filter.setFilter(new HttpRequestLog3Filter());
// 过滤器名称
filter.setName("studentFilter3");
// 是否启用
// filter.setEnabled(true);
// 设置过滤路径
//filter.setUrlPatterns(Collections.singletonList("/student/**"));
// 设置优先级
filter.setOrder(0);
// 增加过滤路径
filter.addUrlPatterns("/**");
return filter;
}
}
@Slf4j
public class HttpRequestLog3Filter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("FilterRegistrationBean 方式添加过滤器 ");
System.out.println("================================================");
filterChain.doFilter(servletRequest,servletResponse);
}
}
监听器
应用生命周期监听器(SpringApplicationRunListener)
- 监听springBoot应用启动的完整流程
public interface SpringApplicationRunListener {
//开始启动
default void starting(ConfigurableBootstrapContext bootstrapContext) {
}
// 环境准备完成
default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
}
// 上下文准备完成 什么是上下文?
default void contextPrepared(ConfigurableApplicationContext context) {
}
// 上下文加载完成
default void contextLoaded(ConfigurableApplicationContext context) {
}
// 应用已启动
default void started(ConfigurableApplicationContext context, @Nullable Duration timeTaken) {
}
// 应用就绪
default void ready(ConfigurableApplicationContext context, @Nullable Duration timeTaken) {
}
// 应用启动失败
default void failed(@Nullable ConfigurableApplicationContext context, Throwable exception) {
}
}
使用方式
- 继承SpringApplicationRunListener实现其中的方法
- 在resource目录下新建META-INF/spring.factories文件
- 在文件中指定自己写的ApplicationRunListener
org.springframework.boot.SpringApplicationRunListener=\ com.cpz.mybatistest.listener.MySpringApplicationRunListener
/**
* spring应用启动监听器
* @author: caorongwei
* 2026/1/16 13:45 */@Slf4j
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
// 这个地方用log打印不出来
System.out.println("++++++++++++Application starting++++++++++");
System.out.println("startTime:==========>>>>" + LocalDateTime.now());
// bootstrapContext 就是spring容器
// 注册自定义Bootstrap组件 不是往IOC容器中注册
bootstrapContext.register(Student.class, BootstrapRegistry.InstanceSupplier.from(() -> {
Student s = new Student();
s.setAddress("阿美莉卡斯坦福大学");
s.setName("生命的礼物");
s.setAge(30);
s.setHeight(189.1);
s.setSex("男");
return s;
}));
}
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
// 环境准备好 这个地方可以指定激活那个环境配置
log.info("====================environmentPrepared======================");
// 这个位置能够获取到Student对象
Student orElse = bootstrapContext.getOrElse(Student.class, null);
log.info("+++++++++++++ Student is {}",orElse);
// 获取系统环境变量
Map<String, Object> systemEnvironment = environment.getSystemEnvironment();
systemEnvironment.forEach((key,value)->{
log.info("============== systemEnvParm is {} = {}",key,value);
});
for (String activeProfile : environment.getActiveProfiles()) {
log.info("=================== activeProfile is {}",activeProfile);
}
//可以在这里修改环境配置 新增激活的配置
//environment.addActiveProfile();
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
// 上下文准备完毕 但是还没开始制造Bean 现在获取Bean会报错
// 也是能够获取IOC容器对象,
// org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext@16f1f8fb has not been refreshed yet
log.info("==========================contextPrepared======================");
//能够注册Bean到IOC容器中
((BeanDefinitionRegistry) context).registerBeanDefinition("student01", BeanDefinitionBuilder.genericBeanDefinition(Student.class).getBeanDefinition());
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
// 这一步也不能够获取Bean 同样会报容器还没有刷新错误
log.info("============================contextLoaded======================");
int beanDefinitionCount = context.getBeanDefinitionCount();
log.info("============== BeanCount is {}",beanDefinitionCount);
}
@Override
public void started(ConfigurableApplicationContext context, @Nullable Duration timeTaken) {
// 应用已经启动,但是还没开始接请求
// 容器已经刷新 可以获取到Bean
log.info("============================started======================");
Student s = context.getBean(Student.class);
log.info("+++++++++++++ Student is {}",s);
}
@Override
public void ready(ConfigurableApplicationContext context, @Nullable Duration timeTaken) {
// 应用就绪 可以接受请求
log.info("========================ready======================");
log.info("===========启动时间:{}秒=============",timeTaken.getSeconds());
log.info("===================Application 启动完成!======================");
}
@Override
public void failed(@Nullable ConfigurableApplicationContext context, Throwable exception) {
log.error("===============Application 启动失败!==============",exception);
}
}
Spring容器监听器(ApplicationListener)
能够监听Spring容器的各个阶段
各种事件
- ApplicationStartingEvent // 应用开始启动
- ApplicationEnvironmentPreparedEvent // 环境准备完成
- ApplicationContextInitializedEvent // 上下文初始化完成
- ApplicationPreparedEvent // 上下文准备完成
- ContextRefreshedEvent // 上下文刷新完成,容器刷新完成
- ApplicationStartedEvent // 应用已启动
- ApplicationReadyEvent // 应用准备就绪(可处理请求)
- ContextClosedEvent // 上下文关闭
- ContextStoppedEvent // 上下文停止
通过事项ApplicationListener接口的方式进行监听
- 实际测试时候如果使用Component注解的方式将监听器注入到容器中,那么这个applicationListener只能监听到容器刷新到容器关闭之间的事件即
- ContextRefreshedEvent
- ApplicationStartedEvent
- ApplicationReadyEvent
- ContextClosedEvent
@Slf4j
@Component
// 也可替换成具体的事件,只监听一种事件
// public class MyApplicationListener implements ApplicationListener<ApplicationStartingEvent>
// 不太对 实际测试
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ApplicationStartingEvent event1){
String name = event1.getSpringApplication().getClass().getName();
log.info("MyApplicationListener: application begin main class is {}",name);
System.out.println("MyApplicationListener: application begin");
}else if(event instanceof ContextRefreshedEvent event1){
int beanDefinitionCount = event1.getApplicationContext().getBeanDefinitionCount();
String[] beanDefinitionNames = event1.getApplicationContext().getBeanDefinitionNames();
Student bean = event1.getApplicationContext().getBean(Student.class);
log.info("MyApplicationListener: IOC refreshed,beanDefinitionCount is {},beanDefinitionNames is {}",beanDefinitionCount,beanDefinitionNames);
log.info("MyApplicationListener: IOC refreshed,get student from IOC student is {}",bean);
}else if(event instanceof ApplicationReadyEvent event1){
log.info("MyApplicationListener:--------- listen ApplicationReadyEvent");
}else if(event instanceof ApplicationEnvironmentPreparedEvent event1){
log.info("MyApplicationListener:listen ApplicationEnvironmentPreparedEvent");
}else if(event instanceof ApplicationContextInitializedEvent event1){
log.info("MyApplicationListener:--------- listen ApplicationContextInitializedEvent");
}else if(event instanceof ApplicationPreparedEvent event1){
log.info("MyApplicationListener:listen ApplicationPreparedEvent");
}else if(event instanceof ApplicationStartedEvent event1){
log.info("MyApplicationListener:--------- listen ApplicationStartedEvent");
}else if(event instanceof ContextClosedEvent event1){
log.info("MyApplicationListener:--------- listen ContextClosedEvent");
}else if(event instanceof ContextStoppedEvent event1){
log.info("MyApplicationListener:--------- listen ContextStoppedEvent");
}
}
}
使用EventListener注解的方式进行监听(推荐)
- 使用异步线程池处理监听事件 @Async("taskExecutor")
- 监听多个事件
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
- 使用SpEL表达式条件监听
@EventListener(condition = "#event.applicationContext.getEnvironment().acceptsProfiles('prod')")
@Slf4j
@Component
public class MyApplicationListener2 {
@EventListener(value = ContextRefreshedEvent.class)
// 使用Order来指定监听器对同一事件的处理顺序
@Order(1)
public void handleContextRefreshedEvent(ContextRefreshedEvent event){
ApplicationContext applicationContext = event.getApplicationContext();
int beanDefinitionCount = applicationContext.getBeanDefinitionCount();
log.info("EventListener listen ContextRefreshedEvent the beanDefinitionCount is {}",beanDefinitionCount);
for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
log.info("EventListener listen ContextRefreshedEvent the beanDefinitionName is {}",beanDefinitionName);
}
Student student = applicationContext.getBean(Student.class);
log.info("EventListener listen ContextRefreshedEvent the student is {}",student);
}
}
Selvet监听器
主要的监听接口
- 也就前两个监听器有点用
- ServletContextListener - 监听Web应用启动/关闭
- ServletRequestListener - 监听HTTP请求开始/结束
- ServletRequestAttributeListener - 监听Request属性变化
- HttpSessionListener - 监听Session创建/销毁
- HttpSessionAttributeListener - 监听Session属性变化
使用方式
- 使用@WebListener + @SelvetComponentScan注解注入到容器
- 使用@Component注解
- 或者像过滤器一样使用@Bean配合ServletListenerRegistrationBean
@Slf4j
@Component
public class MyServletListener implements ServletRequestListener {
private long startTime;
@Override
public void requestDestroyed(ServletRequestEvent sre) {
HttpServletRequest servletRequest = (HttpServletRequest) sre.getServletRequest();
String requestURI = servletRequest.getRequestURI();
log.info("servlet end request request uri is {} endTime is {},耗时 {}",requestURI, System.currentTimeMillis(),System.currentTimeMillis()-this.startTime);
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest servletRequest = (HttpServletRequest)sre.getServletRequest();
this.startTime = System.currentTimeMillis();
log.info("servlet recv request request uri is {} startTime is {}",servletRequest.getRequestURI(), startTime);
}
}
@WebListener
@Slf4j
public class MyServletListener2 implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("ServletContextListener Servlet容器 started!!!");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("ServletContextListener Servlet容器 end!!!");
}
}
自定义事件监听器
- 观察者模式的一种经典实现
自定义事件
- 继承ApplicationEvent类
public class AddStudentEvent extends ApplicationEvent {
private final Student student;
public AddStudentEvent(Object source,Student student) {
super(source);
this.student = student;
}
public Student getStudent() {
return student;
}
}
ApplicationEventPublisher发布自定义事件
- 直接注入即可,SpringBoot默认自动提供
@Autowired
private final ApplicationEventPublisher publisher;
//发布事件
publisher.publishEvent(new AddStudentEvent(this,student));
EventListener监听自定义事件
@EventListener(AddStudentEvent.class)
@Order(1)
public void handleAddStudentEvent(AddStudentEvent event){
Student student = event.getStudent();
log.info("success add a new student student is {}",student);
log.info("发送电子邮件给该学生,祝贺她成功入学!");
}
启动运行器ApplicationRunner
- Application启动后可以运行一些初始化步骤
- 实现ApplicationRunner接口
@Component
@Slf4j
public class MyApplicationRunner implements ApplicationRunner {
@Autowired
private StudentMapper studentMapper;
@Override
public void run(ApplicationArguments args) throws Exception {
// 启动之后测试数据库是否正常
try {
studentMapper.getStudentById(0);
log.info("test mysql connection success!");
}catch (Exception e){
log.error("cant connect to Mysql Server!",e);
}
// 初始化数据。。。。。
}
}
各种事件的顺序
Spring Boot 应用启动监听器执行顺序:
- SpringApplicationRunListener.starting()
- SpringApplicationRunListener.environmentPrepared()
- ApplicationListener
- ApplicationListener
- SpringApplicationRunListener.contextPrepared()
- ApplicationListener
- SpringApplicationRunListener.contextLoaded()
- ApplicationListener
- SpringApplicationRunListener.started()
- ApplicationListener
- ApplicationListener
- SpringApplicationRunListener.ready()
- ApplicationListener
- ApplicationRunner.run() / CommandLineRunner.run()
- @EventListener(ApplicationReadyEvent) 方法

浙公网安备 33010602011771号