手写Srping10(实现容器事件监听)

news2025/1/23 7:16:51

文章目录

  • 目标
  • 设计
  • 项目结构
  • 一、实现
    • 1、定义事件接口--ApplicationEvent
      • 1、定义通用应用上下文事件--ApplicationContextEvent
      • 2、定义刷新和关闭事件--ContextClosedEvent、ContextRefreshedEvent
    • 2、定义事件监听--ApplicationListener
    • 3、定义事件广播器--ApplicationEventMulticaster
      • 1、抽象实现事件广播器--AbstractApplicationEventMulticaster
      • 2、实现事件的广播功能--SimpleApplicationEventMulticaster
    • 3、定义事件发布--ApplicationEventPublisher
      • 1、抽象实现事件发布实现--AbstractApplicationContext
  • 二、测试
    • 1、创建一个自定义事件和监听器
    • 2、创建刷新事件监听
    • 3、创建关闭事件监听
    • 4、配置文件
    • 5、单元测试
  • 总结


目标

在 Spring 中有一个 Event 事件功能,它可以提供事件的定义、发布以及监听事件来完成一些自定义的动作,可以降低系统的耦合

如提交某个申请,可能需要发MQ短信通知审批人员去审批,如启动审批流程等

本章节我们需要以观察者模式的方式,设计和实现 Spring Event 的容器事件和事件监听器功能,最终可以让我们在现有实现的 Spring 框架中可以定义、监听和发布自己的事件信息。


设计

在功能实现上我们需要定义出事件类事件监听事件发布,而这些类的功能需要结合到 Spring 的 AbstractApplicationContext#refresh(),以便于处理事件初始化注册事件监听器的操作。整体设计结构如下图

橙色是本章新增内容
在这里插入图片描述
1、在整个功能实现过程中,仍然需要在面向用户的应用上下文 AbstractApplicationContext 中添加相关事件内容,包括:初始化事件发布者、注册事件监听器、发布容器刷新完成事件

2、使用观察者模式定义事件类、监听类、发布类,同时还需要完成一个广播器的功能,接收到事件推送时进行分析处理符合监听事件接受者感兴趣的事件,也就是使用 isAssignableFrom 进行判断。

3、isAssignableFrom 和 instanceof 相似,不过 isAssignableFrom 是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是Object。如果A.isAssignableFrom(B)结果是true,证明B可以转换成为A,也就是A可以由B转换而来。


项目结构

本章新增、修改的类,可以看到我们在现有的spring代码结构上,增加了事件的定义、事件监听、事件广播、事件发布等功能,对原来的代码改动很少,仅仅是在ApplicationContext中,继承了ApplicationEventPublisher,由AbstractApplicationContext具体实现了事件发布(广播事件),这全部得益于前期的架构设计,才能像现在这样,即使添加新的功能,对原来的代码改动也非常小,更具备扩展性。
在这里插入图片描述

在这里插入图片描述

1、以上整个类关系图以围绕实现 event 事件定义、发布、监听功能实现和把事件的相关内容使用 AbstractApplicationContext#refresh 进行注册和处理操作。

2、在实现的过程中主要以扩展 spring context 包为主,事件的实现也是在这个包下进行扩展的,当然也可以看出来目前所有的实现内容,仍然是以IOC为主。

3、ApplicationContext 容器继承事件发布功能接口 ApplicationEventPublisher,并在实现类中提供事件监听功能。

4、ApplicationEventMulticaster 接口是注册监听器和发布事件的广播器,提供添加、移除发布事件方法。

5、最后是发布容器关闭事件,这个仍然需要扩展到 AbstractApplicationContext#close 方法中,由注册到虚拟机的钩子实现


一、实现

1、定义事件接口–ApplicationEvent

public abstract class ApplicationEvent extends EventObject {

    /**
     * @desc: 创建事件
     **/
    public ApplicationEvent(Object source) {
        super(source);
    }
}

以继承 java.util.EventObject 定义出具备事件功能的抽象类 ApplicationEvent,后续所有事件的类都需要继承这个类。


1、定义通用应用上下文事件–ApplicationContextEvent

public class ApplicationContextEvent extends ApplicationEvent {

    /**
     * @desc: 创建事件
     **/
    public ApplicationContextEvent(Object source) {
        super(source);
    }

    /**
     * @desc: 获取事件引发的应用上下文
     **/
    public final ApplicationContext getApplicationContext(){
        return (ApplicationContext) getSource();
    }
}

ApplicationContextEvent 是定义事件的抽象类,所有的事件包括关闭、刷新,以及用户自己实现的事件,都需要继承这个类。


2、定义刷新和关闭事件–ContextClosedEvent、ContextRefreshedEvent

/**
 * @desc 定义关闭事件
 */
public class ContextClosedEvent extends ApplicationContextEvent {

    /**
     * @desc: 创建事件
     **/
    public ContextClosedEvent(Object source) {
        super(source);
    }

}
/**
 * @desc 定义刷新事件
 */
public class ContextRefreshedEvent extends ApplicationContextEvent {

    /**
     * @desc: 创建事件
     **/
    public ContextRefreshedEvent(Object source) {
        super(source);
    }

}

1、ContextClosedEvent、ContextRefreshedEvent,分别是 Spring 框架自己实现的两个事件类,可以用于监听刷新和关闭动作。


2、定义事件监听–ApplicationListener

/** 事件监听器
 */
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    /**
     * 处理事件
     */
    void onApplicationEvent(E event);

}

定义了一个处理事件,这个接口由使用者实现,添加自定义处理事件的逻辑

程序员想使用,一般需要实现ApplicationListener接口,并且通过指定泛型,指定事件类,如implements ApplicationListener,然后实现onApplicationEvent的实现


3、定义事件广播器–ApplicationEventMulticaster

/**
 * @desc 事件广播器
 */
public interface ApplicationEventMulticaster {

    /**
     * 添加监听器
     **/
    void addApplicationListener(ApplicationListener<?> listener);

    /**
     * 删除监听器
     */
    void removeApplicationListener(ApplicationListener<?> listener);

    /**
     * 将事件广播
     */
    void multicastEvent(ApplicationEvent event);
}

在事件广播器中定义了添加监听删除监听的方法以及一个广播事件的方法(multicastEvent)最终推送事件消息也会经过这个接口方法来处理谁该接收事件。


1、抽象实现事件广播器–AbstractApplicationEventMulticaster

/**
 * @desc 抽象事件广播器
 */
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {

    private BeanFactory beanFactory;

    private final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }


    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
    }

    @Override
    public void removeApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.remove(listener);
    }

    /**
     * @desc: 获取感兴趣事件的监听
     **/
    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
        LinkedHashSet<ApplicationListener> allListeners = new LinkedHashSet<>();
        for (ApplicationListener<ApplicationEvent> applicationListener : applicationListeners) {
            if (supportsEvent(applicationListener,event)) {
                allListeners.add(applicationListener);
            }
        }
        return allListeners;
    }

    /**
     * @desc:监听器是否对该事件感兴趣
     **/
    protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
        Class<? extends ApplicationListener> listenerClass  = applicationListener.getClass();

        // 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化类型,需要判断后获取目标 class
        Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
        Type genericInterface = targetClass.getGenericInterfaces()[0];

        Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
        String className = actualTypeArgument.getTypeName();
        Class<?> eventClassName;

        try {
            eventClassName = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new BeansException("wrong event class name: " + className);
        }
        // 判定此 eventClassName 对象所表示的类或接口与指定的 event.getClass() 参数所表示的类或接口是否相同,或是否是其超类或超接口。
        // isAssignableFrom是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是Object。如果A.isAssignableFrom(B)结果是true,证明B可以转换成为A,也就是A可以由B转换而来。
        return eventClassName.isAssignableFrom(event.getClass());
    }
}

1、AbstractApplicationEventMulticaster 是对事件广播器的公用方法提取,在这个类中可以实现一些基本功能,避免所有直接实现接口放还需要处理细节。

2、除了像 addApplicationListener、removeApplicationListener,这样的通用方法,这里这个类中主要是对 getApplicationListenerssupportsEvent 的处理。

3、getApplicationListeners 方法主要是摘取符合广播事件中的监听处理器,具体过滤动作在 supportsEvent 方法中。

4、在 supportsEvent 方法中,主要包括对Cglib、Simple不同实例化需要获取目标Class,Cglib代理类需要获取父类的Class,普通实例化的不需要。

5、接下来就是通过提取接口和对应的 ParameterizedType 和 eventClassName,方便最后确认是否为子类和父类的关系,以此证明此事件归这个符合的类处理。可以参考代码中的注释


2、实现事件的广播功能–SimpleApplicationEventMulticaster

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster{


    public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
        setBeanFactory(beanFactory);
    }

    @Override
    public void multicastEvent(ApplicationEvent event) {
        for (ApplicationListener applicationListener : getApplicationListeners(event)) {
            applicationListener.onApplicationEvent(event);
        }
    }
}

1、继承AbstractApplicationEventMulticaster,实现了multicastEvent(广播接口)

2.、multicastEvent获取到符合事件的监听集合,


3、定义事件发布–ApplicationEventPublisher

/**
 * @desc 事件发布者
 */
public interface ApplicationEventPublisher {

    /**
     * @desc: 发布事件
     **/
    void publishEvent(ApplicationEvent event);
}

ApplicationEventPublisher 是整个一个事件的发布接口,所有的事件都需要从这个接口发布出去。


1、抽象实现事件发布实现–AbstractApplicationContext

/**
 * 应用上下文抽象类实现
 */
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {


    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

    private ApplicationEventMulticaster applicationEventMulticaster;


    /**
     * @desc: 刷新容器
     **/
    @Override
    public void refresh() throws BeansException {
        // 1、创建BeanFactory,并加载BeanDefintion
        refreshBeanFactory();

        // 2、获取beanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        // 3. 添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

        // 4.在 Bean 实例化之前,执行 BeanFactoryPostProcesso
        invokeBeanFactoryPostProcessors(beanFactory);

        // 5、BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
        registerBeanPostProcessors(beanFactory);

        // 6. 初始化事件发布者
        initApplicationEventMulticaster();

        // 7. 注册事件监听器
        registerListeners();

        // 8. 提前实例化单例Bean对象
        beanFactory.preInstantiateSingletons();

        // 9. 发布容器刷新完成事件
        finishRefresh();
    }




    /**
     * @desc: 初始化事件广播器
     **/
    private void initApplicationEventMulticaster(){
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME,applicationEventMulticaster);
    }

    /**
     * @desc: 注册程序员定义的各种事件监听器
     **/
    private void registerListeners() {
        Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
        for (ApplicationListener listener : applicationListeners) {
            applicationEventMulticaster.addApplicationListener(listener);
        }
    }

    /**
     * @desc: 发布容器刷新完成事件
     **/
    private void finishRefresh() {
        publishEvent(new ContextRefreshedEvent(this));
    }

    /**
     * @desc: 事件广播器,广播事件
     **/
    @Override
    public void publishEvent(ApplicationEvent event) {
        applicationEventMulticaster.multicastEvent(event);
    }


    /**
     * 添加钩子方法close
     */
    @Override
    public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }


    /**
     * 调用关闭方法时,会执行销毁的自定义处理
     */
    @Override
    public void close() {
        publishEvent(new ContextClosedEvent(this));

        getBeanFactory().destroySingletons();
    }

	// ..屏蔽掉getbean、beanPostProcesssor等本章无关代码

}

1、在抽象应用上下文 AbstractApplicationContext#refresh 中,主要新增了 初始化事件发布者、注册事件监听器、发布容器刷新完成事件,三个方法用于处理事件操作。

2、初始化事件发布者(initApplicationEventMulticaster),主要用于实例化一个 SimpleApplicationEventMulticaster,这是一个实现的事件广播器

3、注册事件监听器(registerListeners),通过 getBeansOfType 方法获取到所有从 spring.xml 中加载到的事件配置 Bean 对象

4、发布容器刷新完成事件(finishRefresh),发布了第一个服务器启动完成后的事件,这个事件通过 publishEvent 发布出去,其实也就是调用了 applicationEventMulticaster.multicastEvent(event); 方法。

5、最后是一个 close 方法中,新增加了发布一个容器关闭事件。publishEvent(new ContextClosedEvent(this));


二、测试

1、创建一个自定义事件和监听器

public class CustomEvent extends ApplicationContextEvent {

    private Long id;
    private String message;

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public CustomEvent(Object source, Long id, String message) {
        super(source);
        this.id = id;
        this.message = message;
    }

    // ...get/set
}

创建一个自定义事件,在事件类的构造函数中可以添加自己的想要的入参信息。这个事件类最终会被完成的拿到监听里,所以你添加的属性都会被获得到。

public class CustomEventListener implements ApplicationListener<CustomEvent> {

    @Override
    public void onApplicationEvent(CustomEvent event) {
        System.out.println("收到:" + event.getSource() + "消息;时间:" + new Date());
        System.out.println("消息:" + event.getId() + ":" + event.getMessage());
    }

}

1、这个是一个用于监听 CustomEvent 事件的监听器,这里你可以处理自己想要的操作,比如一些用户注册后发送优惠券和短信通知等。


2、创建刷新事件监听

/**
 * @desc: 刷新监听
 **/
public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("刷新事件:" + this.getClass().getName());
    }

}

因为spring框架涵本身就提供了ContextRefreshedEvent事件,所以这里无需和前面步骤1一样,自己去定义一个刷新事件,所以这里只需要创建关闭的监听,然后自定义处理逻辑


3、创建关闭事件监听

/**
 * @desc: 关闭监听器
 **/
public class ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> {

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        System.out.println("关闭事件:" + this.getClass().getName());
    }

}

同步骤2一致,当然实际需求中,可以根据自己的需求创建需要的事件和监听


4、配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean class="springframework.test.event.ContextRefreshedEventListener"/>

    <bean class="springframework.test.event.CustomEventListener"/>

    <bean class="springframework.test.event.ContextClosedEventListener"/>
</beans>

在 spring.xml 中配置了三个事件监听器,监听刷新、监控自定义事件、监听关闭事件


5、单元测试

public class ApiTest {

    @Test
    public void test_event() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
        applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L, "成功了!"));

        applicationContext.registerShutdownHook();
    }

}

通过使用 applicationContext 新增加的发布事件接口方法,发布一个自定义事件 CustomEvent,并透传了相应的参数信息。

测试结果

刷新事件:springframework.test.event.ContextRefreshedEventListener$$EnhancerByCGLIB$$828882b7
收到:cn.ljc.springframework.context.support.ClassPathXmlApplicationContext@50f8360d消息;时间:Tue Dec 20 23:50:44 CST 2022
消息:1019129009086763:成功了!
关闭事件:springframework.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$9654bf79

Process finished with exit code 0

从测试结果可以看到,我们自己定义的事件和监听以及监听系统的事件信息,都可以在控制台完整的输出出来了。

你也可以尝试增加一些其他事件行为,并调试代码学习观察者模式。


总结

1、spring的事件定义、定义监听、事件发布,其实就是设计模式的具体实现

2、在发布完成后根据匹配策略,监听器就会收到属于自己的事件内容,并做相应的处理动作

3、不熟悉的地方还是需要一点点调试,并且结合源码才能慢慢掌握

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/105389.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

audio_open函数分析

audio_open() 的作用&#xff0c;就如同它的名字那样&#xff0c;就是打开音频设备。流程图如下&#xff1a; SDL 库播放音频数据有两种方式。 1&#xff0c;调用层定时往 SDL 接口塞数据。 2&#xff0c;设置SDL回调函数&#xff0c;让 SDL 来主动执行回调函数来取数据。 第…

ABAP 字符处理

场景1:是否只包含数字 str1 CO 0123456789 涉及关键字&#xff1a;CO&#xff0c;使用公式 str1 CO str2 。表示 str1 中 的每个字符 在 str2 中都能找到 类比&#xff1a;无 场景2&#xff1a;字符串1 是否 包含 字符串2 DATA str1 TYPE STRING VALUE hello world. DATA …

Pytest测试框架搭建需求及实现方案

目录 框架需求及实现方案 框架需求 实现方案 支持接口自动化、Web UI自动化及App自动化 可以批量运行用例并生成测试报告 测试完成发送邮件 提供灵活的运行方式&#xff0c;如按功能模块运行、按脚本运行、按用例等级运行等等 提供运行日志方便定位问题 支持切换环境 …

李沐精读论文:MAE 《Masked Autoencoders Are Scalable Vision Learners》

论文&#xff1a;Masked Autoencoders Are Scalable Vision Learners 别再无聊地吹捧了&#xff0c;一起来动手实现 MAE(Masked Autoencoders Are Scalable Vision Learners) 玩玩吧&#xff01; - 知乎 参考博文&#xff1a;MAE 论文逐段精读【论文精读】 - 哔哩哔哩 神洛华…

QT 学习笔记(十)

文章目录一、绘图1. 理论知识储备2. 画背景图3. 简单绘图4. 手动刷新窗口二、绘图实现代码1. 主窗口头文件 widget.h2. 主窗口头文件 widget.cpp由于每次代码都是在原有程序上修改&#xff0c;因此除了新建项目&#xff0c;不然一般会在学完后统一展示代码。 提示&#xff1a;具…

【Python机器学习】卷积神经网络卷积层、池化层、Flatten层、批标准化层的讲解(图文解释)

卷积神经网络 卷积神经网络&#xff08;convolutional neural network, CNN&#xff09;在提出之初被成功应用于手写字符图像识别&#xff0c;2012年的AlexNet网络在图像分类任务中取得成功&#xff0c;此后&#xff0c;卷积神经网络发展迅速&#xff0c;现在已经被广泛应用于…

怎样在Odoo 16中启用完整的财务功能

Odoo是目前市场上最好的ERP软件之一。Odoo提供两种类型的版本&#xff0c;社区版和企业版。Odoo社区版是由开源软件支持的免费基本版。Odoo社区版本中没有一些模块和功能。但企业版付费版&#xff0c;升级版更适合更高的价值。Odoo企业版具有无限的功能支持和完整的功能。性能和…

「集合底层」深入浅出HashMap底层源码

「集合底层」深入浅出HashMap底层源码 一、HashMap介绍 HashMap底层采用了哈希表&#xff0c;而哈希表是由数组和链表实现的。数组和链表各有自己的特点&#xff1a; 数组&#xff1a;占用空间连续。 寻址容易&#xff0c;查询速度快。但是&#xff0c;增加和删除效率非常低…

倒序打印链表

在做这个题的时候我闹了一个大笑话&#xff0c;我用了反转链表做&#xff0c;哈哈哈哈&#xff0c; 这个题目思路很简单&#xff0c;用到了数组的头插法&#xff0c;注意题目要求返回数组 遍历链表&#xff0c;将链表的val放到数组中 下面来看代码 import java.util.ArrayLis…

flash基础知识

flash基础手册一、flash概念&#xff08;一&#xff09;特性&#xff08;二&#xff09;FLASH的块/扇区/页关系&#xff08;三&#xff09;常用FLASH型号大小&#xff08;四&#xff09;常用FLASH擦写规则&#xff08;五&#xff09;存储器类型参考二、与其他类型存储器件对照&…

PDF文件怎么加密?推荐3种方法给你

在我们的工作学习上&#xff0c;应该有不少人都需要使用到PDF文件格式&#xff0c;毕竟这个格式它兼容性较广&#xff0c;且不易编辑&#xff0c;能较好的保存文件。不过&#xff0c;我们有时为了不让它被其它人随意查看&#xff0c;会给这个文件进行加密的操作。那你们知道如何…

python实现基于TNDADATASET的人体行为识别

首先来看下TNDADATASET&#xff1a; 随便打开一个文件简单看下如下所示&#xff1a; 可以大概推测出来&#xff0c;这里面不同维度的数据集应该是由不同的穿戴式传感器采集得到的&#xff0c;最后一列的class表示的是当前的行为类型。 在我之前的博文里面已经做过了相关的工作…

计算机毕设Python+Vue养老机构管理信息系统(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Sigrity常见问题解决方法(持续更新)

Sigrity常见问题解决方法&#xff08;持续更新&#xff09; 1.使用SpeedEM仿真时&#xff0c;报placement error 错误信息如下&#xff1a; 该问题是因为Mesh设置的过大&#xff0c;导致via和note在同一个mesh网格上存在粘连 解决方法是&#xff1a;将mesh进行auto设置 2…

鲍鱼年龄预测 knn svm 逻辑回归

数据&#xff1a; M,0.455,0.365,0.095,0.514,0.2245,0.101,0.15,15 M,0.35,0.265,0.09,0.2255,0.0995,0.0485,0.07,7 F,0.53,0.42,0.135,0.677,0.2565,0.1415,0.21,9 M,0.44,0.365,0.125,0.516,0.2155,0.114,0.155,10 I,0.33,0.255,0.08,0.205,0.0895,0.0395,0.055,7 I,0.425…

基于pytorch搭建CNN 人像口罩识别检测项目

项目介绍 我们将使用 CNN (卷积神经网络)和机器学习分类器创建一个检测一个人是否戴着口罩的分类器。它将检测一个人是否戴着口罩。 我们将从头开始学习,我将对每一步进行解释。我需要你对机器学习和数据科学有基本的了解。我已经在本地 Windows 10 机器上实现了它,如果你…

深入浅出JVM(七)之执行引擎的解释执行与编译执行

执行引擎 hotspot执行引擎结构图 执行引擎分为解释器、JIT即时编译器以及垃圾收集器 执行引擎通过解释器/即时编译器将字节码指令解释/编译为对应OS上的的机器指令 本篇文章主要围绕解释器与即时编译器&#xff0c;垃圾收集器将在后续文章解析 解释执行与编译执行 Java虚拟机…

方向图与天线增益

目录 一、方向图 二、增益 一、方向图 天线的方向性是指天线向一定方向辐射电磁波的能力。对于接收天线而言&#xff0c;方向性表示天线对不同方向传来的电磁波具有不同的接收能力。天线的方向性通常用方向图来表示。 在数学里&#xff0c;球坐标系是一种利用球坐标表示一个点…

Linux学习-90-Tomcat下载安装(tar压缩包)

17.20 Tomcat下载安装&#xff08;tar压缩包&#xff09; 访问apache官网下载tomcat压缩包。访问以下链接进行下载tomcat-8.5.83版本&#xff0c;高版本的tomcat存在一些问题影响使用&#xff0c;然后使用 Xftp 上传到/usr/local/src目录中或者使用wget命令直接到/usr/local/s…

SpringBoot:模块探究之spring-boot-cli

Spring Boot CLI 是运行 SpringBoot 命令的命令行工具&#xff0c;能够帮助你快速的构建 Spring Boot 项目。只需要编写简单的 groovy 脚本&#xff0c;即可用最少的代码构建并运行一个完整的 Spring Boot 项目。 Spring Boot CLI 为 SpringCloud 提供了 SpringBoot 命令行功能…