SpringBoot监听器

SpringBoot监听器

为什么要使用监听器

emmmm,这是我从网上抄来的讲解,很生动,我就喜欢这样的解释,不知道有没有男生和我一样,更喜欢具象的讲解

为什么要使用监听器,举个例子,大家在过红绿灯的时候,每一个司机其实就是一个观察者,那观察的目标是什么呢?观察的目标就是红绿灯,那这个过程中会产生什么事件呢?就是红灯,黄灯,绿灯的事件,当司机收到这些事件之后会做出不同的举动。那如果不使用这种模式,就是我不用红绿灯,我直接找个交警去一个司机一个司机通知,告诉每个司机,你可以走了,你不能走,比较一下大家就可以发现第一种方式效率更高。其实上面的红绿灯的场景就是一个典型的监听器模式,是23中设计模式中的一种

完成监控器都需要啥(简版)

监听器,我得有一个人监听啊,就是司机

事件(其实事件就是目标中发生了什么事,不同事对应不同的事件),就是红绿灯

触发场景(目标执行了什么东西,然后观察者才可以观察到变化,这个其实就是触发场景)

ApplicationEvent

在源码里我们发现里面只有两个构造函数,最重要的是构造函数需要一个是Object类型的参数,不重要的是也有一个时间,其实那个clock.millis()也是一个获取时间的方法而已。到时候我们写自己的事件的时候,就可以直接继承ApplicationEvent。

这,就是我们的事件

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context;

import java.time.Clock;
import java.util.EventObject;

public abstract class ApplicationEvent extends EventObject {
    private static final long serialVersionUID = 7099057708183571937L;
    private final long timestamp;

    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }

    public ApplicationEvent(Object source, Clock clock) {
        super(source);
        this.timestamp = clock.millis();
    }

    public final long getTimestamp() {
        return this.timestamp;
    }
}

ApplicationListener

老规矩,继续看源码

发现也就是一个接口,一点一点解释就是,我这个接口,需要的参数是一个继承了ApplicationEvent了的类E,然后有一个方法onApplicationEvent,用到了这个E的实例,我们写自己的监听器的时候就要实现这个接口了,然后将onApplicationEvent里写上自己的逻辑。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context;

import java.util.EventListener;
import java.util.function.Consumer;

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E var1);

    static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
        return (event) -> {
            consumer.accept(event.getPayload());
        };
    }
}

这,就是我们的监听器

SmartApplicationListener

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context.event;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.lang.Nullable;

public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    //指定支持哪些类型的事件
    boolean supportsEventType(Class<? extends ApplicationEvent> var1);
    //指定支持发生事件所在的类型
    default boolean supportsSourceType(@Nullable Class<?> sourceType) {
        return true;
    }

    default int getOrder() {
        return 2147483647;
    }

    default String getListenerId() {
        return "";
    }
}

ApplicationContext:publishEvent

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context;

import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.lang.Nullable;

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    @Nullable
    String getId();

    String getApplicationName();

    String getDisplayName();

    long getStartupDate();

    @Nullable
    ApplicationContext getParent();

    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

源码里最关键的是继承了ApplicationEventPublisher类,我们来看看ApplicationEventPublisher里有什么

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context;

@FunctionalInterface
public interface ApplicationEventPublisher {
    default void publishEvent(ApplicationEvent event) {
        this.publishEvent((Object)event);
    }

    void publishEvent(Object var1);
}

就只有这么个publishEvent方法,发现也是可以使用ApplicationEvent作为参数的,是不是就连贯起来了,这个方法的用处就是手动发布一个事件,这个时候监听器Listener就可以间听到了。这,就是触发场景

自定义监听器

下面我们就自己写一个监听器的流程来试一下

1、事件里都有啥信息?

package com.newcrud.entity;

import lombok.Data;

@Data
public class MyListenerEntity {
    private Integer id;
    private String name;
}

2、继承ApplicationEvent写一个自己的事件

package com.newcrud.event;

import com.newcrud.entity.MyListenerEntity;
import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {
    private MyListenerEntity myListenerEntity;
    /**
     * 重写构造函数
     *
     * @param source   发生事件的对象
     * @param myListenerEntity 注册用户对象
     */
    public MyEvent(Object source, MyListenerEntity myListenerEntity) {
        super(source);
        this.myListenerEntity=myListenerEntity;
    }
}

3、实现ApplicationListener,将我们自己的事件放到自己的监听器里

package com.newcrud.config;

import com.newcrud.entity.MyListenerEntity;
import com.newcrud.event.MyEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
//使用Compent将这个监听器交给Spring容器来加载
@Slf4j
public class MyListener implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent myEvent) {
        //将事件中的信息获取到
        MyListenerEntity myListenerEntity=myEvent.getMyListenerEntity();
        log.info(myListenerEntity.getName());
        log.info(String.valueOf(myListenerEntity.getId()));
    }
}

当然我们这里只是简单的打印了一些消息,真正的使用过程中一定是各种各样拿到参数后去用各种逻辑做处理。

4、让我们来触发事件吧,其实就是将信息组装到事件里然后用ApplicationContext的publishEvent方法手动发布一下

package com.newcrud.service.impl;

import com.newcrud.entity.MyListenerEntity;
import com.newcrud.event.MyEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class MyListenerImpl {
    @Autowired
    ApplicationContext applicationContext;
    public MyListenerEntity getMyListenerEntity(){
        MyListenerEntity myListenerEntity =new MyListenerEntity();
        myListenerEntity.setName("zhangsan");
        myListenerEntity.setId(1);
        MyEvent myEvent = new MyEvent(this,myListenerEntity);
        // 发布事件
        applicationContext.publishEvent(myEvent);
        log.info("触发器被触发");
        return myListenerEntity;
    }
}

5、测试一下下

package com.newcrud.controller;

import com.newcrud.entity.MyListenerEntity;
import com.newcrud.service.impl.MyListenerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/listener")
public class MyListenerController {
    @Autowired
    MyListenerImpl myListener;
    @GetMapping("my")
    public MyListenerEntity getMyListenerEntity(){
        return  myListener.getMyListenerEntity();
    }
}

检查结果

postman请求:http://localhost:8084/listener/my

接口返回

{
    "id": 1,
    "name": "zhangsan"
}

检查SpringBoot的日志

2021-09-22 16:18:49.815  INFO 19326 --- [nio-8084-exec-1] com.newcrud.config.MyListener            : zhangsan
2021-09-22 16:18:49.815  INFO 19326 --- [nio-8084-exec-1] com.newcrud.config.MyListener            : 1
2021-09-22 16:18:49.815  INFO 19326 --- [nio-8084-exec-1] com.newcrud.service.impl.MyListenerImpl  : 触发器被触发

注册监听器有两种方式

Compemt

就是刚刚我们用的方式

META-INF/spring.factories

在资源目录中的 META-INF/spring.factories 文件中自动注册:

org.springframework.context.ApplicationListener=
cn.javastack.springboot.features.listener.JavastackListener

如果是监听 Spring 应用上下文(ApplicationContext)创建之后的事件,可以直接在监听器上使用 @Component 注解即可,否则需要使用第一种方法的自动注册,因为 ApplicationContext 并未创建,这时的 Bean 是不能被加载的。

SpringBoot自带的监听器和事件

监听器

除了我们自己写的监听器之外,其实SpringBoot已经为我们准备了很多的监听器,比如Servlet的监听器

Servlet 中的监听器分为以下 3 种类型。

1、监听 ServletContext、 Request、 Session 作用域的创建和销毁
• ServletContextlistener:监听 ServeltContext。
• HttpSessionlistener:监听新的 Session 创建事件。
• ServletRequestlistener:监听 ServletRequest 的初始化和销毁。

2. 监听 ServletContext、 Request、 Session 作用域中属性的变化(增加、修改、删除)
• ServletContextAttributelistener:监听 Se「vlet 上下文参数的变化 。
• HttpSessionAttributelistener:监听 HttpSession 参数的变化。
• ServletRequestAttributelistener「:监听 ServletRequest 参数的变化 。

3. 监听 HttpSession 中对象状态的改变(被绑定、解除绑走、钝化、活化)
• HttpSessionBindinglistener:监听 HttpSession,并绑定及解除绑定。
• HttpSessionActivationlistener:监听钝化和活动的 HttpSession 状态改变。

Web 监听器是一种 Servlet 特殊类,它们能帮助开发者监听 Web 中特定的事件,比如 ServletContext、HttpSession 、ServletRequest 的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理,实现监控。

还是来做一个实验,比如说我们要使用HttpSessionListener,那我们就先进入源码看一下

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package javax.servlet.http;

import java.util.EventListener;

public interface HttpSessionListener extends EventListener {
    default void sessionCreated(HttpSessionEvent se) {
    }

    default void sessionDestroyed(HttpSessionEvent se) {
    }
}

发现只有两个方法,一个是初始化的时候执行的,一个是销毁的时候执行的,为了看效果,那我们就改一下

package com.newcrud.config;

import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

@Component
public class MyListener2 implements HttpSessionListener {
    public static Integer count = 0;   //记录在线的用户数量
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("新来了一个");
        count++;
        se.getSession().getServletContext().setAttribute("count",count);
    }
}

再写一个测试类

package com.newcrud.controller;

import com.newcrud.entity.MyListenerEntity;
import com.newcrud.service.impl.MyListenerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController
@RequestMapping("/listener")
public class MyListenerController {
    @Autowired
    MyListenerImpl myListener;
    @GetMapping("my")
    public MyListenerEntity getMyListenerEntity(){
        return  myListener.getMyListenerEntity();
    }
    //下面是新增的
    @GetMapping("much")
    public String getHowMuch(HttpServletRequest httpServletRequest){
        Integer count = (Integer) httpServletRequest.getSession().getServletContext().getAttribute("count");
        return "一共几个人呢:"+count;
    }
}

执行一下接口:http://localhost:8084/listener/much

执行第一次
postman结果:

一共几个人呢:1

SpringBoot日志:

新来了一个

事件

同时也定义了很多很多的事件

容器事件(ApplicationContextEvent):
ContextRefresheEvent:
    ApplicationContext容器初始化或刷新时触发该事件。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用
ContextStartedEvent:
    当spring启动时,或者说是context调用start()方法时,会触发此事件
ContextStoppedEvent:
    当spring停止时,或者说context调用stop()方法时,会触发此事件
ContextClosedEvent:
    当spring关闭时,或者说context调用close()方法时,会触发此事件

Web应用相关(RequestHandledEvent):
ServletRequestHandledEvent:
    每次请求处理结束后,容器上下文都发布了一个ServletRequestHandledEvent事件
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值