从自定义一个作用域开始来了解SpringBean的作用域

你好,这里是codetrend专栏“Spring6全攻略”。

在 Spring 框架中,Bean 的作用域(Scope)定义了 Bean 实例在容器中如何创建、管理和销毁的策略。

Spring 提供了多种 Bean 作用域,每种作用域都有其特定的生命周期和适用场景。

先试试不同的 Bean Scope

下面通过一个简单的 Spring MVC Controller 示例来感受下 Bean 的作用域。

例子代码是这样的:

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;

import java.util.UUID;

@Configuration
public class AppConfig {
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
    public SingletonBean singletonBean() {
        return new SingletonBean();
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public PrototypeBean prototypeBean() {
        return new PrototypeBean();
    }

    @Bean
    @Scope(WebApplicationContext.SCOPE_SESSION)
    public SessionBean sessionBean() {
        return new SessionBean();
    }
}

class SingletonBean {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

class PrototypeBean {
    private String id;

    public PrototypeBean() {
        this.id = UUID.randomUUID().toString();
    }

    public String getId() {
        return id;
    }
}

class SessionBean {
    private String id;

    public SessionBean() {
        this.id = UUID.randomUUID().toString();
    }

    public String getId() {
        return id;
    }
}

controller 代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ScopeController {
    @Autowired
    private SingletonBean singletonBean;

    @Autowired
    private ApplicationContext context;

    @GetMapping("/singleton")
    public String singletonCount() {
        singletonBean.increment();
        return "Singleton Count: " + singletonBean.getCount();
    }

    @GetMapping("/prototype")
    public String prototypeGet() {
        PrototypeBean prototypeBean = context.getBean(PrototypeBean.class);
        return "Prototype ID: " + prototypeBean.getId();
    }

    @GetMapping("/session")
    public String sessionGet() {
        SessionBean prototypeBean = context.getBean(SessionBean.class);
        return "Session ID: " + prototypeBean.getId();
    }
}
  • Singleton(单例)的属性持续增加,也就是说访问到的SingletonBean每次都是同一个对象。访问/singleton接口的返回是这样的:
1
2
3
  • Prototype(原型)的属性每次都是不一样的,也就是说明id每次都是调用构造器新创建的。访问/prototype接口的返回是这样的:

Prototype ID: 3ea5af10-ddce-4a89-ad3c-3f07a764f179
Prototype ID: 7e6e9fe8-c0dc-423e-b282-96b7f8087dac
Prototype ID: 7aca1000-484d-46e8-80f7-d444a5a04f49
  • Session(会话)的属性同一窗口是一样的,开启无痕窗口或者其他浏览器就不一样。访问/session接口的返回是这样的:
Prototype ID: 7aca1000-484d-46e8-80f7-d444a5a04f49
Prototype ID: 7aca1000-484d-46e8-80f7-d444a5a04f49
# 开启新的窗口后
Session ID: bd22d310-29e5-4004-8555-9678c08275f0
Session ID: bd22d310-29e5-4004-8555-9678c08275f0

可以直接把样例代码复制到例子里面验证测试。这样我们就对BeanScope作用域有个直观的感受。

自定义一个 Bean Scope

接下来通过实现一个自定义作用域来感受下Bean的作用域原理。

在 Spring 框架中,除了预定义的几种作用域(如 singleton、prototype 等)外,用户还可以自定义作用域以满足特定的业务需求。

自定义作用域允许控制 Bean 的创建、缓存和销毁逻辑,以适应特定的场景,如基于特定条件的实例化策略、自定义生命周期管理等。

自定义步骤:

  • 定义作用域接口:首先,需要实现org.springframework.beans.factory.config.Scope接口,该接口定义了 Bean 作用域的基本行为。
  • 实现逻辑:在自定义的 Scope 接口实现中,需要覆盖getremoveregisterDestructionCallback方法,分别用于获取 Bean 实例、移除 Bean 实例以及注册销毁回调。
  • 注册作用域:在 Spring 配置中注册的自定义作用域,使其可被容器识别和使用。
  • 使用自定义作用域:在 Bean 定义中通过@Scope注解指定使用自定义的作用域名称。

自定义作用域实现

首先自定义作用域实现,也就是实现接口org.springframework.beans.factory.config.Scope

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import java.util.UUID;

public class CustomScope implements Scope {
    public final static String CUSTOM_SCOPE_NAME = "custom";
    private final Map<String, Object> scopedObjects = new ConcurrentHashMap<>();
    private final Map<String, Runnable> destructionCallbacks = new ConcurrentHashMap<>();

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Object scopedObject = scopedObjects.get(name);
        if (scopedObject == null) {
            scopedObject = objectFactory.getObject();
            scopedObjects.put(name, scopedObject);
        }
        return scopedObject;
    }

    @Override
    public Object remove(String name) {
        scopedObjects.remove(name);
        Runnable callback = destructionCallbacks.remove(name);
        if (callback != null) {
            callback.run();
        }
        return null;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        destructionCallbacks.put(name, callback);
    }

    @Override
    public Object resolveContextualObject(String key) {
        // 可以根据需要实现上下文对象解析逻辑
        return null;
    }

    @Override
    public String getConversationId() {
        // 返回一个唯一的标识,用于区分作用域上下文
        return UUID.randomUUID().toString();
    }
}

可以看到Scope接口其实是对Bean的全生命周期进行管理,包括获取get、缓存和销毁remove和销毁回调等逻辑。这也是作用域的核心原理。

Spring6怎么实现的Scope

这里以org.springframework.web.context.request.RequestScope 为例子来理解Spring6怎么实现BeanScope的。

得益于Spring框架的抽象和封装,这个类的实现代码并没有多少。

  • RequestScope extends AbstractRequestAttributesScope 核心实现在这个类 AbstractRequestAttributesScope

  • get获取对象方法,其中对象的存储放在了ThreadLocal中,也就是RequestContextHolder这个类的核心。

/**
 * 根据名称获取对象,如果当前请求属性中没有该对象,则使用对象工厂创建一个对象,并将其设置到请求属性中
 * 然后再次获取该对象,以便进行隐式会话属性更新。作为额外的好处,我们还允许在获取属性级别进行潜在的装饰。
 * 如果再次获取到的对象不为空(预期情况),则只使用该对象。如果它同时消失了,我们则返回本地创建的实例。
 */
public Object get(String name, ObjectFactory<?> objectFactory) {
    // 获取当前请求的属性
    RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
    // 根据名称和作用域获取对象
    Object scopedObject = attributes.getAttribute(name, getScope());
    if (scopedObject == null) {
        // 使用对象工厂创建对象
        scopedObject = objectFactory.getObject();
        // 将创建的对象设置到请求属性中
        attributes.setAttribute(name, scopedObject, getScope());
        // 再次获取对象,进行隐式会话属性更新
        // 并允许进行潜在的装饰
        Object retrievedObject = attributes.getAttribute(name, getScope());
        if (retrievedObject!= null) {
            // 只使用再次获取到的对象(如果仍然存在,这是预期情况)
            // 如果它同时消失了,我们则返回本地创建的实例
            scopedObject = retrievedObject;
        }
    }
    // 返回获取到的对象
    return scopedObject;
}
  • remove 方法也是差不多的。借助工具类RequestContextHolder将缓存在ThreadLocal中的对象移除。
/**
 * 移除指定名称的对象,如果当前请求属性中存在该对象,则将其从请求属性中移除并返回该对象;否则返回 null
 */
public Object remove(String name) {
    // 获取当前请求的属性
    RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
    // 根据名称和作用域获取对象
    Object scopedObject = attributes.getAttribute(name, getScope());
    if (scopedObject!= null) {
        // 将该对象从请求属性中移除
        attributes.removeAttribute(name, getScope());
        // 返回移除的对象
        return scopedObject;
    } else {
        // 返回 null
        return null;
    }
}

注册自定义作用域

注册作用域,需要通过BeanFactory的registerScope方法进行注册。

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class ScopeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerScope(CustomScope.CUSTOM_SCOPE_NAME, new CustomScope());
    }
}

验证自定义作用域效果

将Bean注册到Spring容器中,并使用自定义作用域。

public class MyScopeBean {
    private String id;

    public MyScopeBean() {
        this.id = UUID.randomUUID().toString();
    }

    public String getId() {
        return id;
    }
}

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class AppScopeConfig {
    @Bean
    @Scope(CustomScope.CUSTOM_SCOPE_NAME)
    public MyScopeBean myBean() {
        return new MyScopeBean();
    }
}

新建一个Controller,访问/customScope接口,返回自定义作用域的Bean实例。

@RestController
public class CustomScopeController {
    @Autowired
    private ApplicationContext context;
    @GetMapping("/customScope")
    public String customScope() {
        MyScopeBean prototypeBean = context.getBean(MyScopeBean.class);
        return "Prototype ID: " + prototypeBean.getId();
    }
}

访问的结果输出如下:

Session ID: bd22d310-29e5-4004-8555-9678c08275f0
Session ID: bd22d310-29e5-4004-8555-9678c08275f0
Session ID: bd22d310-29e5-4004-8555-9678c08275f0

因为对象全局缓存到了一个MapscopedObjects,所以可以看到这个自定义作用域效果和单例模式基本一致的。

Bean Scope 的分类

Scope描述
singleton(Default) 将单个 bean 定义作用域限定为 Spring IoC 容器中的单个对象实例。
prototype将单个 bean 定义作用域限定为任意数量的对象实例。
request将单个 bean 定义作用域限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有自己的一个基于单个 bean 定义创建的 bean 实例。仅在 Web-aware Spring ApplicationContext 上下文中有效。
session将单个 bean 定义作用域限定为 HTTP Session 的生命周期。仅在 Web-aware Spring ApplicationContext 上下文中有效。
application将单个 bean 定义作用域限定为 ServletContext 的生命周期。仅在 Web-aware Spring ApplicationContext 上下文中有效。
websocket将单个 bean 定义作用域限定为 WebSocket 的生命周期。仅在 Web-aware Spring ApplicationContext 上下文中有效。

其中singletonprototype是比较常用的数据。

Bean Scope 的使用

可以通过在Spring的配置文件(如XML配置文件或Java注解)中指定@Scope注解或<bean>元素的scope属性来定义Bean的Scope。

其中@Scope注解可以是自定义的值或者如下常量:

  • ConfigurableBeanFactory.SCOPE_PROTOTYPE
  • ConfigurableBeanFactory.SCOPE_SINGLETON
  • org.springframework.web.context.WebApplicationContext.SCOPE_REQUEST
  • org.springframework.web.context.WebApplicationContext.SCOPE_SESSION

其中ConfigurableBeanFactory.SCOPE_PROTOTYPE是默认值。

例如:

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class MyPrototypeBean {
    // Bean内容
}

或者使用XML配置:

<bean id="myBean" class="com.example.MyBean" scope="prototype">
    <!-- Bean的其他配置 -->
</bean>

选择合适的Bean Scope取决于应用程序的需求。

为什么设计 Bean Scope

Spring 框架设计 Bean 作用域(Scope)的原因主要是为了提供灵活性和资源管理能力,以适应不同应用场景的需求。

不同的 Bean 作用域会影响 Bean 的生命周期、创建方式和在容器中的共享程度,从而影响应用的性能、内存占用和并发处理能力。

以下是 Spring 提供 Bean 作用域设计背后的主要原因:

  • 资源优化:通过作用域设计,Spring 能够根据业务场景高效管理 Bean 的创建与销毁。例如,单例(Singleton)模式可以减少频繁创建实例的开销,原型(Prototype)模式则确保每次请求都得到新的实例,避免了共享状态问题。
  • 并发处理:对于 Web 应用,特定作用域如请求(Request)和会话(Session)使得每个用户请求或会话都有独立的 Bean 实例,解决了并发用户数据隔离的问题,提高了应用的线程安全。
  • 生命周期管理:不同的作用域允许开发者控制 Bean 的生命周期,比如通过自定义作用域实现复杂的生命周期管理逻辑。Spring 容器在 Bean 的创建、初始化、销毁等关键时刻调用生命周期回调方法,增加了灵活性。
  • 可测试性:通过作用域的设计,特别是原型模式,可以更容易地创建独立的测试环境,因为每次测试都能得到全新的实例,减少了测试间状态干扰。
  • 扩展性:Spring 允许开发者自定义作用域,为特定的业务需求或架构设计提供定制化的 Bean 管理方式,增强了框架的扩展性和适应性。
  • 内存管理:合理使用作用域可以减少内存消耗,例如,原型模式避免了单例 Bean 累积大量状态导致的内存泄漏风险,而请求作用域则确保请求结束后自动清理资源。

单例 bean 里面注入了原型 bean

当单例 Bean 中注入原型(Prototype)Bean 时,会出现一个问题:

  • 单例 Bean 在整个应用生命周期中只创建一次。
  • 而原型 Bean 本应每次请求时创建新实例。
  • 但直接注入到单例 Bean 中时,实际上只会注入一次原型 Bean 的实例。
  • 后续对该原型 Bean 的使用都将复用首次注入的同一个实例,这可能并不符合预期。

以下demo可以复现这种情况。

SpringBean的配置:

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class FaultAppConfig {
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public PrototypeInjectBean prototypeInjectBean() {
        return new PrototypeInjectBean();
    }
}

单例SpringBean:

import java.util.UUID;

public class PrototypeInjectBean {
    private String id;

    public PrototypeInjectBean() {
        this.id = UUID.randomUUID().toString();
    }

    public String getId() {
        return id;
    }
}

测试代码如下:

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class PrototypeFaultController {

    final private PrototypeInjectBean prototypeInjectBean;

    protected PrototypeFaultController(PrototypeInjectBean prototypeInjectBean) {
        this.prototypeInjectBean = prototypeInjectBean;
    }

    /**
     * 原型作用域失效,每次返回同一个id
     * @return
     */
    @GetMapping("/prototypeDemo1")
    public String prototypeDemo1() {
        return "Prototype ID: " + prototypeInjectBean.getId();
    }

}

在不重启应用或者垃圾回收的情况下,访问接口 /prototypeDemo1 原型 Bean 的id值始终是相同的。

Prototype ID: 11893ed8-608c-452b-b2e6-82bc70b5cf97
Prototype ID: 11893ed8-608c-452b-b2e6-82bc70b5cf97
Prototype ID: 11893ed8-608c-452b-b2e6-82bc70b5cf97

那这种常用的使用场景遇到了该怎么解决呢?别急,Spring早已经给出了几种解决办法。

通过完善上面的测试代码给出3中解决方法。

修改完善后的代码如下:


import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public abstract class PrototypeFaultController {

    @Autowired
    private ApplicationContext context;
    @Autowired
    private ObjectProvider<PrototypeInjectBean> prototypeBeanProvider;

    final private PrototypeInjectBean prototypeInjectBean;

    protected PrototypeFaultController(PrototypeInjectBean prototypeInjectBean) {
        this.prototypeInjectBean = prototypeInjectBean;
    }

    /**
     * 原型作用域失效,每次返回同一个id
     * @return
     */
    @GetMapping("/prototypeDemo1")
    public String prototypeDemo1() {
        return "Prototype ID: " + prototypeInjectBean.getId();
    }

    /**
     * 使用实例工厂方法注入获取原型Bean,每次返回不同id
     * @return
     */
    @GetMapping("/prototypeDemo2")
    public String prototypeDemo2() {
        PrototypeInjectBean prototypeBean = context.getBean(PrototypeInjectBean.class);
        return "Prototype ID: " + prototypeBean.getId();
    }

    /**
     * Spring 提供了`ObjectProvider`接口(继承自`Provider`接口),它允许延迟查找和实例化 Bean,非常适合在单例 Bean 中按需获取原型 Bean 的新实例。
     * @return
     */
    @GetMapping("/prototypeDemo4")
    public String prototypeDemo4() {
        return "Prototype ID: " + prototypeBeanProvider.getObject().getId();
    }

    /**
     * 使用`@Lookup`注解获取原型Bean,每次返回不同id
     * @return
     */
    @GetMapping("/prototypeDemo5")
    public String prototypeDemo5() {
        return "Prototype ID: " + getPrototypeBean().getId();
    }
    @Lookup
    public abstract PrototypeInjectBean getPrototypeBean();

}
  • 解决办法1: Spring 提供了ObjectProvider接口(继承自Provider接口),它允许延迟查找和实例化 Bean,非常适合在单例 Bean 中按需获取原型 Bean 的新实例。

通过访问接口/prototypeDemo4可以发现每次返回的id值是不同的。

  • 解决办法2: 可以通过定义一个工厂方法来创建原型 Bean 的实例,然后在单例 Bean 中注入这个工厂方法,每次需要时调用工厂方法获取新实例。

通过访问接口/prototypeDemo2可以发现每次返回的id值是不同的。

  • 解决办法3: 通过@Lookup注解,@Lookup注解是Spring框架中的一个特殊注解,用于在Spring容器中查找另一个Bean,并将其注入到当前Bean中。注意使用@Lookup注解的方法必须是抽象的(abstract)。

通过访问接口/prototypeDemo5可以发现每次返回的id值是不同的。

关于作者

来自一线全栈程序员nine的探索与实践,持续迭代中。

欢迎关注或者点个小红心~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

区块链交易所开发

在当今数字化时代&#xff0c;区块链技术以其独特的去中心化、安全性和透明性&#xff0c;正在逐步改变我们的生活。其中&#xff0c;区块链交易所作为连接区块链技术与广大投资者的桥梁&#xff0c;其开发与发展备受关注。本文将从技术进步与市场需求两个维度&#xff0c;探讨…

抖音小店达人佣金应该怎么结算呢?给达人设置多少佣金合适?

大家好&#xff0c;我是电商小V 咱们在运营抖音小店的时候一定会遇到被达人催促结算佣金的情况&#xff0c;咱们给达人合作的时候都会遇到过新手达人&#xff0c;就是给咱们直播带货之后催促咱们赶紧结算商品的佣金&#xff0c; 但是咱们都需要知道一点&#xff0c;那就是小店的…

VTK —— 三、简单操作 - 示例2 - 计算从3D点到直线的距离(附完整源码)

代码效果 本代码编译运行均在如下链接文章生成的库执行成功&#xff0c;若无VTK库则请先参考如下链接编译vtk源码&#xff1a; VTK —— 一、Windows10下编译VTK源码&#xff0c;并用Vs2017代码测试&#xff08;附编译流程、附编译好的库、vtk测试源码&#xff09; 教程描述 本…

跟TED演讲学英文:How to govern AI — even if it‘s hard to predict by Helen Toner

How to govern AI — even if it’s hard to predict Link: https://www.ted.com/talks/helen_toner_how_to_govern_ai_even_if_it_s_hard_to_predict? Speaker: Helen Toner Date: April 2024 文章目录 How to govern AI — even if its hard to predictIntroductionVocabu…

Hadamard Product(点乘)、Matmul Product(矩阵相乘)和Concat Operation(拼接操作)在神经网络中的应用

Hadamard乘积&#xff08;Hadamard Product&#xff09;&#xff0c;矩阵乘法&#xff08;Matmul Product&#xff09;和拼接操作&#xff08;Concatenation Operation&#xff09;在神经网络中的使用情况如下&#xff1a; Hadamard Product点乘、内积&#xff1a; Hadamard乘…

编程入门(六)【Linux系统基础操作一】

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 &#x1f525;前言&#x1f680;Linux操作系统介绍与环境准备Linux操作系统介…

Kafka源码分析(五) - Server端 - 基于时间轮的延时组件

系列文章目录 Kafka源码分析-目录 一. 背景 Kafka内部涉及大量的"延时"操作&#xff0c;比如收到PRODUCE请求后可为副本等待一个timeout的时间后再响应客户端。 那我们讨论一个问题&#xff1a;Kafka为什么自己实现了一个延时任务组件&#xff0c;而不直接使用ja…

《从Paxos到Zookeeper》——第五、六章:经典应用场景

目录 第五章 使用Zookeeper 5.1 服务端部署与运行 5.2 客户端相关 5.2.1 客户端运行 5.2.2 客户端命令 5.3 Java客户端API 5.4 开源客户端 第六章 经典应用场景 6.1 典型应用场景及实现 6.1.1 数据发布/订阅&#xff08;全局配置中心&#xff09; 6.1.2 负载均衡&#xff08;Lo…

ChatGPT Web Midjourney一键集成最新版

准备工具 服务器一台 推荐使用浪浪云服务器 稳定 安全 有保障 chatgpt api 推荐好用白嫖的api 项目演示 项目部署 浏览器访问casaos 添加软件原添加 https://gitee.com/langlangy_1/CasaOS-AppStore-LangLangy/raw/master/chatmjd.zip 安装此软件 等待安装 安装后再桌面设置…

什么是模版方法模式,有哪些应用?

模板方法模式是一种行为设计模式&#xff0c;他的主要作用就是复用代码。在很多时候&#xff0c;我们的代码中可能会有-些公共的部分并且还有一些定制的部分&#xff0c;那么公共这部分就可以定义在一个父类中&#xff0c;然后将定制的部分实现在子类中。这样子类可以根据需要扩…

TRIZ理论助力充电桩产业跨越技术瓶颈,实现产业升级!

随着新能源汽车市场的迅猛发展和电动汽车保有量的不断增加&#xff0c;充电桩作为电动汽车的“能量补给站”&#xff0c;其重要性日益凸显。然而&#xff0c;充电桩产业在发展过程中也面临着诸多技术瓶颈&#xff0c;如何突破这些瓶颈&#xff0c;推动充电桩产业升级成为行业亟…

《Video Mamba Suite》论文笔记(2)Mamba对于多模态交互的作用

原文翻译 4.2 Mamba for Cross-Modal Interaction Tasks and datasets.除了单模态任务外&#xff0c;我们还评估了 Mamba 在跨模态交互方面的性能。我们首先使用视频时间定位 (Video Temporal Grounding) 任务进行评估。所涉及的数据集包含 QvHighlight [44] 和 Charade-STA …

Vue阶段练习:初始化渲染、获取焦点、记账清单

阶段练习主要承接Vue 生命周期-CSDN博客 &#xff0c;学习完该部分内容后&#xff0c;进行自我检测&#xff0c;每个练习主要分为效果显示、需求分析、静态代码、完整代码、总结 四个部分&#xff0c;效果显示和准备代码已给出&#xff0c;我们需要完成“完整代码”部分。 练习…

交直流充电桩测试系统解决方案,你了解多少?

交直流充电桩测试系统是电动汽车充电设施的重要组成部分&#xff0c;它对充电桩的性能、安全性和可靠性进行全方位的检测和评估。随着电动汽车的普及&#xff0c;充电桩测试系统的需求也在不断增加。本文将对交直流充电桩测试系统的解决方案进行简要介绍。 1. 系统组成 交直流…

微信小程序如何使用svg矢量图标

微信小程序如何使用自定义SVG矢量图标 在微信小程序中&#xff0c;经常会用到小图标来装饰界面&#xff0c;我们常用的方法就是引用第三方的图标&#xff0c;但会存在收费或者找不到合适的图标&#xff0c;这时候我建议可以自行编写svg图标代码&#xff0c;就可以随心所欲的使…

纯干货,源代码防泄密之环境加密与文档加密的区别

环境加密和文档加密是两种不同的数据保护方法&#xff0c;下面用SDC沙盒及文档加密系统作对比&#xff0c;它们在设计理念、管控对象、安全性、性能以及适用场景等方面有所区别&#xff1a; 来百度APP畅享高清图片 1、设计理念&#xff1a; 环境加密&#xff08;如SDC沙盒&am…

JavaScript继承的方法和优缺点

原型链继承 让一个构造函数的原型是另一个类型的实例&#xff0c;那么这个构造函数new出来的实例就具有该实例的属性。 优点&#xff1a; 写法方便简洁&#xff0c;容易理解。 缺点&#xff1a; 在父类型构造函数中定义的引用类型值的实例属性&#xff0c;会在子类型原型上…

华中科技大学雷达站部署

一&#xff1a;项目地址 GitHub - HUSTLYRM/HUST_Radar_2023: 华中科技大学狼牙战队 RoboMaster 2023赛季 雷达站 二&#xff1a;安装依赖 2.1创建虚拟环境 首先是程序是基于python3.8完成&#xff0c;所以创建虚拟环境的时候&#xff0c;选择3.8的虚拟环境 conda create -…

【算法刷题日志】吸氧羊的StarryCoding之旅 - 贡献法计算

题目链接&#xff1a;https://www.starrycoding.com/problem/3 题目描述 吸氧羊终于注册了一个StarryCoding账号&#xff01;&#xff08;她很开心&#xff09; 但是吸氧羊忘记了它的密码&#xff0c;她想起你是计算机大师&#xff0c;于是就来请教你。 她虽然不记得密码了…

nacos开启登录开关启动报错“Unable to start embedded Tomcat”

nacos 版本&#xff1a;2.3.2 2.2.2版本之前的Nacos默认控制台&#xff0c;无论服务端是否开启鉴权&#xff0c;都会存在一个登录页&#xff1b;在之后的版本关闭了默认登录页面&#xff0c;无需登录直接进入控制台操作。在这里我们可以在官网可以看到相关介绍 而我现在所用的…
最新文章