【Spring框架】Spring监听器的简介和基本使用

news2025/2/1 15:39:22

目录

一、观察者模式

1.1 模型介绍

1.2 观察者模式Demo

1.2.1 观察者实体

1.2.2 主题实体

1.2.3 测试代码

二、Spring监听器的介绍

2.1 事件(ApplicationEvent)

2.1.1 Spring内置事件

2.1.2 Spring内置事件

2.2 事件监听器(ApplicationListener)

2.2.1 事件监听器-基于接口

2.2.1.1 SmartApplicationListener

2.2.1.2 GenericApplicationListener

2.2.2 事件监听器-基于注解

2.3 事件发布者(ApplicationEventPublisher)

2.4 事件广播器(ApplicationEventMulticaster)

三、Spring监听器的使用

3.1 新建监听器

3.1.1 通过实现ApplicationListener接口创建监听器

3.1.2 使用@EventListener注解创建监听器

3.2 内置的事件类型

3.3 自定义事件与监听器Demo

3.3.1 构建两个自定义事件

3.3.2 构建监听

3.3.3 发布事件


一、观察者模式

Spring的监听器用到了观察者模式,工厂模式(EventListenerFactory),适配器模式(ApplicationListenerMethodAdapter),而其中最重要的就是基于观察者模式,它能实现事件与事件监听者直接的解耦。所以在讲解监听器之前,我们先讲一下观察者模式。

1.1 模型介绍

观察者模式(Observer Pattern)是一种行为设计模式,它用于在对象之间建立一对多的依赖关系。在该模式中,当一个对象的状态发生变化时,它会自动通知其依赖对象(称为观察者),使它们能够自动更新。

观察者模式的工作原理如下:

  1. 主题对象维护一个观察者列表,并提供方法用于添加和删除观察者。
  2. 当主题的状态发生变化时(监听到某种事件发生),它会遍历观察者列表,并调用每个观察者的通知方法。
  3. 观察者接收到通知后,根据通知进行相应的更新操作。

以我的理解,就是一个主题会去监听一种事件,和这个事件相关的观察者都会被加到这个主题对象中。当主题对象监听到这个事件发生了,就会去通知自己维护的所有观察者,这些观察者就回去执行相应的逻辑。也就是一种主题对应一种事件,要观察这个事件的观察者都添加到这个主题中,由主题去监听这个事件是否发生,事件发生后主题回去通知所有的观察者,观察者们再去执行相应的逻辑

观察者模式角色:

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
  • Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
  • ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

1.2 观察者模式Demo

一个观察者模式demo包括以下部分:

  • 观察者实体
  • 主题实体

1.2.1 观察者实体

所以我们先写个观察者接口

interface Observer {
    void update();
}

再构建两个观察者实现类

// 具体观察者A
class ConcreteObserverA implements Observer {
    @Override
    public void update() {
        System.out.println("ConcreteObserverA收到更新通知");
    }
}

// 具体观察者B
class ConcreteObserverB implements Observer {
    @Override
    public void update() {
        System.out.println("ConcreteObserverB收到更新通知");
    }
}

1.2.2 主题实体

然后定义主题接口

interface Subject {
    // 注册新的观察者
    void registerObserver(Observer observer);
    // 删除指定观察者
    void removeObserver(Observer observer);
    // 通知全部观察者
    void notifyObservers();
}

构建一个具体主题

// 具体主题
class ConcreteSubject implements Subject {
    // 一个主题维护多个观察者
    private List<Observer> observers = new ArrayList<>();

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }

    public void doSomething() {
        System.out.println("主题执行某些操作...");
        // 执行操作后通知观察者
        notifyObservers(); 
    }
}

1.2.3 测试代码

public class ObserverPatternDemo {
    public static void main(String[] args) {
        // 创建主题和观察者
        ConcreteSubject subject = new ConcreteSubject();
        Observer observerA = new ConcreteObserverA();
        Observer observerB = new ConcreteObserverB();
        // 注册观察者
        subject.registerObserver(observerA);
        subject.registerObserver(observerB);
        // 执行主题的操作,触发通知
        subject.doSomething();
    }
}

最后可以看到结果:

主题执行某些操作…

ConcreteObserverA收到更新通知

ConcreteObserverB收到更新通知

二、Spring监听器的介绍

Spring事件体系包括三个组件:

  1. 事件广播器(事件多播器、事件发布器),可以理解为就是applicationContext。
  2. 事件ApplicationEvent。
  3. 事件监听器ApplicationListener。

实现Spring事件机制主要有4个类:

  • ApplicationEvent:事件,每个实现类表示一类事件,可携带数据。
  • ApplicationListener:事件监听器,用于接收事件处理时间。
  • ApplicationEventMulticaster:事件管理者,用于事件监听器的注册和事件的广播。
  • ApplicationEventPublisher:事件发布者,委托ApplicationEventMulticaster完成事件发布。

2.1 事件(ApplicationEvent

事件(ApplicationEvent) 是特定事件监听器被触发的原因。

2.1.1 Spring内置事件

内置事件中由系统内部进行发布,只需自己注入监听器即可。

Spring容器启动的时候在不同阶段会发布不同事件,我们可以根据自己的需求进行监听。(阿里的nacos服务自动注册原理就是监听到web容器初始化完成事件,完成自动注册发现)。例如Spring容器启动完之后,会发布一个ContextRefreshedEvent事件,我们可以实现自己的监听器进行监听。

@Component
// 设置用于监听ContextRefreshedEvent事件的监听器
public class MyListener2 implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("-----------------:"+event.getSource().getClass());
    }
}

可以使用Spring内置的事件来对注册到容器中的Bean进行拓展,重点看内置的ContextRefreshedEvent和ContextClosedEvent这两个事件,一个是在刚完成容器初始化的时候被发布的事件,一个是关闭容器时发布的事件。我们只要去编写监听这两个事件的监听器,来自定义实现一些逻辑,就能做到对注册到容器中的Bean进行拓展了。

内置的事件类型:

Event

说明

ContextRefreshedEvent

当容器被实例化或refreshed时发布。如调用refresh()方法,此处的实例化是指所有的bean都已被加载,后置处理器都被激活,所有单例bean都已被实例化,所有的容器对象都已准备好可使用。如果容器支持热重载,则refresh可以被触发多次(XmlWebApplicatonContext支持热刷新,而 GenericApplicationContext则不支持)

ContextStartedEvent

当容器启动时发布,即调用start()方法,已启用意味着所有的Lifecycle bean都已显式接收到了start 信号

ContextStoppedEvent

当容器停止时发布,即调用stop()方法,即所有的Lifecycle bean都已显式接收到了stop信号,关闭的容器可以通过start()方法重启

ContextClosedEven36

当容器关闭时发布,即调用close方法,关闭意味着所有的单例bean都已被销毁。关闭的容器不能被重启 或refresh

RequestHandledEvent

这只在使用spring的DispatcherServlet时有效,当一个请求被处理完成时发布

内置事件类型的继承体系:

2.1.2 Spring内置事件

事件类需要继承ApplicationEvent抽象类,代码如下:

public class BigEvent extends ApplicationEvent {
    private String name;
    public BigEvent(Object source, String name) {
        super(source);
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
}

这里为了简单测试,所以写的很简单。

事件类其实就是一种很简单的pojo,除了需要继承ApplicationEvent也没什么了,这个类有一个构造方法需要调用super()方法。

2.2 事件监听器(ApplicationListener

事件监听器(ApplicationListener)对应于观察者模式中的观察者。作用为处理事件广播器发布的事件。

在Spring中监听器的实现主要有一下重要组件:

  • ApplicationListener接口:事件监听者,观察者;
  • ApplicationEvent抽象类:Spring 事件,记录事件源、事件内容、时间等数据;
  • @EventListener注解:除了实现ApplicationListener接口注册监听器,也可以使用注解的方式;
  • ApplicationEventPublisher接口:发布事件;

2.2.1 事件监听器-基于接口

Spring提供了继承于java.util.EventListener接口的应用监听器接口, ApplicationListener,此接口源码:

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);
}

使用接口实现监听器:

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);
}

事件监听器需要实现ApplicationListener接口,这是个泛型接口,泛型类类型就是事件类型,其次监听器是需要spring容器托管的bean,所以这里加了@component,只有一个方法,就是onApplicationEvent(),是事件发生后要触发的操作。

Spring还提供了对ApplicationListener接口的实现:SmartApplicationListener接口和GenericApplicationListener接口。

2.2.1.1 SmartApplicationListener

提供了监听器对泛型事件的支持,spring3.0 添加的。

源码:

public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    boolean supportsEventType(Class<? extends ApplicationEvent> var1);
    boolean supportsSourceType(Class<?> var1);
}

2.2.1.2 GenericApplicationListener

增强对泛型事件的支持(支持泛型方式不同与SmartApplicationListener),spring4.2 添加的。

源码:

public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    boolean supportsEventType(ResolvableType var1);
    boolean supportsSourceType(Class<?> var1);
}

2.2.2 事件监听器-基于注解

@Component
public class OrderEventListener {
    @EventListener(OrderEvent.class)
    public void onApplicationEvent(OrderEvent event) {
        if(event.getName().equals("减库存")){
            System.out.println("减库存.......");
        }
    }
}

注解是加到onApplicationEvent()方法上的(方法名可以自定义,这个方法就是监听器监听到事件发生后触发的执行逻辑)。

这里要注意,加注解的方法不能是private修饰的方法,否则会报错。

@EventListener的错误尝试:

道了@EventListener的原理,我们其实可以做一些猜测,如下:

methodA是正常的用法;

methodB方法的修饰符是private;

methodC则是监听的ContextRefreshedEvent,但下面方法的入参却是ContextClosedEvent;

后两者都有问题:

可以看到,编译器直接黄底提示了methodB的@EventListener注解,其实从前面我们已经猜到,因为最后我们的调用是由监听器ApplicationListenerMethodAdapter对象直接调用的方法ABC,所以方法必须可被其他对象调用,即public。

而后者会在执行广播响应事件时报参数非法异常也是意料之中。

2.3 事件发布者(ApplicationEventPublisher

事件发布者,职责为发布事件。

spring的ApplicationContext 本来就实现了ApplicationEventPublisher接口,因此应用上下文本来就是

一个事件发布者,在AbstractApplicationContext中实现了事件发布的业务。

上下文对象调用publishEvent()方法,将事件对象传入即可将该事件发布。

applicationContext.publishEvent(new HelloEvent(this,"lgb"));

事件发布之后就会被已经注册到Spring容器中对应的事件监听器监听到,会触发监听器onApplicationEvent()操作。

2.4 事件广播器(ApplicationEventMulticaster

Spring事件机制是观察者模式的一种实现,但是除了发布者和监听者者两个角色之外,还有一个EventMultiCaster的角色负责把事件转发给监听者。

事件发布器(ApplicationEventMulticaster)又叫事件发布器、事件多播器。对应于观察者模式中的被观察者/主题, 负责通知观察者。对外提供发布事件和增删事件监听器的接口,维护事件和事件监听器之间的映射关系,并在事件发生时负责通知相关监听器。

其工作流程如下:

EventMultiCaster注册着所有的Listener,在上面发布事件的代码中发布者调用applicationEventPublisher.publishEvent(msg); 是会将事件发送给了EventMultiCaster,然后EventMultiCaster根据事件类型决定转发给那个Listener。

讲完了Spring事件体系中的三个组件,我们就明白了他们之间的关系和运作流程。下面我们再详细讲一下如何使用它。

三、Spring监听器的使用

3.1 新建监听器

3.1.1 通过实现ApplicationListener接口创建监听器

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class MyContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("应用程序上下文已刷新");
        // 在这里可以执行一些初始化操作
    }
}
  • 我们创建了一个名为MyContextRefreshedListener的监听器,它实现了ApplicationListener接口,并且传入的泛型是ContextRefreshedEvent,这意味着这个监听器监听的事件类型是ContextRefreshedEvent。MyContextRefreshedListener重写了 onApplicationEvent 方法。因为Spring会在容器refresh()时发布ContextRefreshedEvent事件,所以onApplicationEvent方法在应用程序上下文被刷新时触发。
  • 使用 @Component 注解将该监听器声明为一个Spring管理的组件,这样Spring会自动将其纳入到应用程序上下文中,并在适当的时候触发监听。

3.1.2 使用@EventListener注解创建监听器

除了手动写个类外,我们也可以找个现成的类,该类不需要继承或实现任何其他类,只需要在它的某个方法上加上 @EventListener 注解就可以实现监听器的功能,如下:

@Component
public class MyListener {
    @EventListener(ContextRefreshedEvent.class)
    public void methodA(ContextRefreshedEvent event) {
        System.out.println("应用程序上下文已刷新");
        // 在这里可以执行一些初始化操作
    }
}
  • 在一个现有的类的某个方法上,加上@EventListener(ContextRefreshedEvent.class),Spring会在加载这个类时,为其创建一个监听器,这个监听器监听的事件类型是ContextRefreshedEvent,当此事件发生时,将触发执行该方法methodA。
  • 使用 @Component 注解将该类声明为一个Spring管理的组件,这样Spring会自动将其纳入到应用程序上下文中,并在适当的时候触发监听。
  • 我们可以在这个类中写上多个方法,每个方法通过注解监听着不同的事件类型,这样我们就仅需使用一个类,却构建了多个监听器。

上述两种方法的效果是一样的。那么最后,我们就完成了Spring中一个内置监听器的简单示例:当启动一个基于Spring的应用程序时,当应用程序上下文被刷新时,ContextRefreshedEvent事件将被触发,然后MyContextRefreshedListener监听器的onApplicationEvent方法将被调用。

3.2 内置的事件类型

我们在demo中使用了一个 ContextRefreshedEvent 的事件,这个事件是Spring内置的事件,除了该事件,Spring还内置了一些其他的事件类型,分别在以下情况下触发:

  • ContextRefreshedEvent:

        当应用程序上下文被刷新时触发。这个事件在ApplicationContext初始化或刷新时被发布,适用于执行初始化操作和启动后的后续处理。例如,初始化缓存、预加载数据等。

  • ContextStartedEvent:

当应用程序上下文启动时触发。这个事件在调用ApplicationContext的start()方法时被发布,适用于在应用程序启动时执行特定的操作。例如,启动定时任务、启动异步消息处理等。

  • ContextStoppedEvent:

当应用程序上下文停止时触发。这个事件在调用ApplicationContext的stop()方法时被发布,适用于在应用程序停止时执行清理操作。例如,停止定时任务、关闭数据库连接等。

  • ContextClosedEvent:

当应用程序上下文关闭时触发。这个事件在调用ApplicationContext的close()方法时被发布,适用于在应用程序关闭前执行最后的清理工作。例如,释放资源、保存日志等。

  • RequestHandledEvent:

在Web应用程序中,当一个HTTP请求处理完成后触发。这个事件在Spring的DispatcherServlet处理完请求后被发布,适用于记录请求日志、处理统计数据等。

  • ApplicationEvent:

这是一个抽象的基类,可以用于定义自定义的应用程序事件。你可以创建自定义事件类,继承自ApplicationEvent,并定义适合你的应用场景的事件类型。

这些内置监听器是由Spring框架自动发布的。不需要程序员去发布。

3.3 自定义事件与监听器Demo

在学习完上面的内容后,我们现在可以手动写个Spring的事件,以及对应的监听器的demo了

3.3.1 构建两个自定义事件

Spring通过继承ApplicationEvent抽象类来创建自定义事件。下面我们来建立一个继承自ApplicationEvent的自定义事件类。

// 事件A
public class CustomEventA extends ApplicationEvent {
    private String message;
    public CustomEventA(Object source, String message) {
        super(source);
        this.message = message;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
}

// 事件B
public class CustomEventB extends ApplicationEvent {
    private String message;
    public CustomEventB(Object source, String message) {
        super(source);
        this.message = message;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
}

3.3.2 构建监听

我们选用@EventListener注解来实现监听器。

@Component
public class MyListener {
    @EventListener(CustomEventA.class)
    public void methodA(CustomEventA event) {
        System.out.println("========我监听到事件A了:" + event.getMessage());
        // 在这里可以执行一些其他操作
    }

    @EventListener(CustomEventB.class)
    public void methodB(CustomEventB event) {
        System.out.println("========我监听到事件B了:" + event.getMessage());
        // 在这里可以执行一些其他操作
    }
}

3.3.3 发布事件

@Component
// 这里将这个CustomEvent事件发布器也设置成一个事件监听器了,去监听ContextRefreshedEvent事件,当这个时间发生之后就会自动去发布CustomEvent事件
public class CustomEventPublisher implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    
    // 利用容器刷新好的消息作为触发,去发布两条自定义的事件
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 创建事件
        CustomEventA eventA = new CustomEventA(applicationContext , "我是AAAA");
        CustomEventB eventB = new CustomEventB(applicationContext , "我是BBBB");
        // 发布事件
        applicationContext.publishEvent(eventA);
        applicationContext.publishEvent(eventB);
    }
}

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

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

相关文章

阿里云安全中心需要购买吗?功能及价格告诉你值不值!

阿里云云安全中心有必要购买吗&#xff1f;云安全中心经常提示云服务器高危漏洞&#xff0c;需要购买云安全中心吗&#xff1f;无论是云服务器上是网站还是其他应用&#xff0c;难免会存在漏洞&#xff0c;有漏洞是一定要修复的&#xff0c;云安全中心不仅可以修复漏洞还可以防…

【C++STL基础入门】list的增、删

文章目录 前言一、list迭代器1.1 list迭代器的定义 二、list增2.1 头添加2.2 尾添加2.3 中间添加 三、list删3.1 尾删除3.2 头删除3.3 删除指定元素3.4 clear()函数3.5 remove()函数3.6 unique()函数 总结 前言 在C中&#xff0c;STL&#xff08;Standard Template Library&am…

ALBERT-更小更少但并不快

BERT模型的压缩大致可以分为&#xff1a;1. 参数剪枝&#xff1b;2. 知识蒸馏&#xff1b;3. 参数共享&#xff1b;4. 低秩分解。 其中&#xff0c;对于剪枝&#xff0c;比较简单&#xff0c;但是容易误操作降低精读&#xff1b; 对于知识蒸馏&#xff0c;之前我写个一系列的…

【完美世界】战王之殇特别篇定档,11月3日播,云曦受辱石昊杀红眼了

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析完美世界国漫资讯。 完美世界第132集已经播出了&#xff0c;相信很多人都去看了。但是不知道大家注意到这一集的片尾没有。如果没有快进或者直接跳过的话&#xff0c;那么应该知道&#xff0c;官方给大家送惊喜了。 这个…

高校教务系统登录页面JS分析——四川大学

高校教务系统密码加密逻辑及JS逆向 本文将介绍高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文&#xff0c;你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文仅供交流学习&#xff0c;勿用于非法用途。 一、密码加…

进程同步互斥问题

互斥-临界区前后分别PV操作&#xff1b;同步-前V后P 一、生产者费者问题 1.过程&#xff1a; 生产者生产产品——>缓冲区(供两者共享使用)——>消费者取出产品使用&#xff1b;但缓冲区有容量要求&#xff0c;会导致等待与唤醒。缓冲区是临界资源&#xff0c;各进程必…

Jetpack:007-各种各样的Button

文章目录 1. 概念介绍2. 使用方法2.1 Button2.2 IconButton2.3 ElevatedButton2.4 OutlinedButton2.5 TextButton2.6 FloatingActionButton 3. 示例代码4. 内容总结 我们在上一章回中介绍了Jetpack中输入框相关的内容&#xff0c;本章回中将要介绍 Button。闲话休提&#xff0…

Linux系统编程02

makefile的原理 问题需求 之前我们讲过C语言代码主要是经过编译和链接两个步骤生成目标文件&#xff0c;但是在编译的时候我们可能需要进行多条指令的输入&#xff0c;要对main函数所在的文件进行汇编&#xff0c;再将我们定义的函数文件进行汇编&#xff0c;分别形成*.o文件&a…

网课、会议投屏自动截屏软件推荐 —— 使用定时自动截屏软件,自动截屏网课、会议投屏,方便回顾、整理

在当前的远程学习和工作环境下&#xff0c;我们经常需要参加网课和会议&#xff0c;而这些内容通常都是通过投屏的方式呈现给我们的。为了更好地记录和回顾这些内容&#xff0c;我们可以使用定时截屏软件来保存这些投屏截屏。 定时截屏软件功能 定时截屏软件可以帮助我们定时…

ModSecurity开源WAF防火墙和控制面板安装教程

ModSecurity开源WAF防火墙和控制面板安装教程 CyberPanel带有两个版本&#xff0c;一个是CyberPanel&#xff0c;另一个是CyberPanel Ent。CyberPanel附带OpenLiteSpeed&#xff0c;不限数量域名完全免费。CyberPanel Ent附带LiteSpeed Web Server Enterprise&#xff0c;只免…

2023年中国门把手产量、销量及市场规模分析[图]

门把手行业是指专门从事门把手的设计、制造、销售和安装等相关业务的行业。门把手是门窗装饰硬件的一种&#xff0c;用于开启和关闭门窗&#xff0c;同时也具有装饰和美化门窗的作用。 门把手行业分类 资料来源&#xff1a;共研产业咨询&#xff08;共研网&#xff09; 随着消…

leetCode 72. 编辑距离 动态规划 + 滚动数组 + 优化空间

72. 编辑距离 - 力扣&#xff08;LeetCode&#xff09; 给你两个单词 word1 和 word2&#xff0c; 请返回将 word1 转换成 word2 所使用的最少操作数 。你可以对一个单词进行如下三种操作&#xff1a; 插入一个字符删除一个字符替换一个字符 编辑距离的应用场景&#xff1a;…

200多万开发者才开发91款应用,国产手机操作系统离开安卓活不了?

随着某国产手机操作系统强调将不再兼容安卓&#xff0c;他们沸腾了&#xff0c;表示将真正独立自主发展&#xff0c;然而业界人士却指出号称拥有220万开发者的该款操作系统至今仅开发了91款应用&#xff0c;彻底撕下了它的遮羞布。 应用对一个智能操作系统有多重要&#xff0c;…

Go 代码块与作用域,变量遮蔽问题详解

Go 代码块与作用域详解 文章目录 Go 代码块与作用域详解一、引入二、代码块 (Block)2.1 代码块介绍2.2 显式代码块2.3 隐式代码块2.4 空代码块2.5 支持嵌套代码块 三、作用域 (Scope)3.1 作用域介绍3.2 作用域划定原则3.3 标识符的作用域范围3.3.1 预定义标识符作用域3.3.2 包代…

互联网Java工程师面试题·Java 并发编程篇·第八弹

目录 33、Java 死锁以及如何避免&#xff1f; 34、死锁的原因 35、怎么唤醒一个阻塞的线程 36、不可变对象对多线程有什么帮助 37、什么是多线程的上下文切换 38、如果你提交任务时&#xff0c;线程池队列已满&#xff0c;这时会发生什么这里区分一下&#xff1a; 39、J…

探索JDK8新特性,Stream 流:构建流的多种方式

当我们处理集合数据时&#xff0c;往往需要对其进行各种操作&#xff0c;如过滤、映射、排序、归约等。在 Java 8 中引入的 Stream 流为我们提供了一种更加简洁和灵活的方式来处理数据。上述情况都是流对集合进行操作的&#xff0c;但是对于流的创建操作还是不太了解&#xff0…

解密并发编程的时间之谜:揭开Happens-Before的神秘面纱

优质博文&#xff1a;IT-BLOG-CN 一、简介 为什么需要happens-before原则&#xff1a; 主要是因为Java内存模型 &#xff0c; 为了提高CPU效率&#xff0c;通过工作内存Cache代替了主内存。修改这个临界资源会更新work memory但并不一定立刻刷到主存中。通常JMM会将编写的代码…

docker保存镜像出错

报错&#xff1a;open .docker_temp_801673807: Access is denied. 查询后发现是因为C盘权限问题导致失败&#xff0c;修改保存路径&#xff1a;docker save -o D:\nginx.tar nginx:latest后成功。

解决docker使用pandarallel报错OSError: [Errno 28] No space left on device

参考&#xff1a;https://github.com/nalepae/pandarallel/issues/127 在使用pandarallel报错OSError: [Errno 28] No space left on device&#xff0c;根据上述issue发现确实默认使用的MEMORY_FS_ROOT为 /dev/shm&#xff0c;而在docker环境下这个目录大小只有64M&#xff0…

​嵌入式VS纯软件

嵌入式VS纯软件 嵌入式系统开发与纯软件开发之间存在关键差异&#xff0c;涵盖了硬件依赖性、资源限制、实时性要求、安全性和维护等多个方面。最近很多小伙伴找我&#xff0c;说想要一些嵌入式资料&#xff0c;然后我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#x…