SpringBoot源码解析(十):应用上下文AnnotationConfigServletWebServerApplicationContext构造方法

SpringBoot源码系列文章

SpringBoot源码解析(一):SpringApplication构造方法

SpringBoot源码解析(二):引导上下文DefaultBootstrapContext

SpringBoot源码解析(三):启动开始阶段

SpringBoot源码解析(四):解析应用参数args

SpringBoot源码解析(五):准备应用环境

SpringBoot源码解析(六):打印Banner

SpringBoot源码解析(七):应用上下文结构体系

SpringBoot源码解析(八):Bean工厂接口体系

SpringBoot源码解析(九):Bean定义接口体系

SpringBoot源码解析(十):应用上下文AnnotationConfigServletWebServerApplicationContext构造方法


目录

  • 前言
  • 源码入口
  • 一、初始化注解Bean定义读取器
    • 1、BeanDefinitionRegistry(Bean定义注册接口)
    • 2、获取环境对象Environment
    • 3、注册注解配置处理器
      • 3.1、获取默认Bean工厂
      • 3.2、注册后置处理器(注册Bean定义)
  • 二、初始化类路径Bean定义扫描器
    • 1、注册默认注解过滤器
    • 2、自定义包扫描
  • 总结

前言

  在前文中,我们了解了应用上下文Bean工厂以及Bean定义的核心组件功能,接下来,我们将深入探讨应用上下文的构造方法。

SpringBoot版本2.7.18SpringApplication的run方法的执行逻辑如下,本文将详细介绍第6小节:创建应用程序上下文

// SpringApplication类方法
public ConfigurableApplicationContext run(String... args) {
    // 记录应用启动的开始时间
    long startTime = System.nanoTime();

    // 1.创建引导上下文,用于管理应用启动时的依赖和资源
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;

    // 配置无头模式属性,以支持在无图形环境下运行
    // 将系统属性 java.awt.headless 设置为 true
    configureHeadlessProperty();

    // 2.获取Spring应用启动监听器,用于在应用启动的各个阶段执行自定义逻辑
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 启动开始方法(发布开始事件、通知应用监听器ApplicationListener)
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
        // 3.解析应用参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

        // 4.准备应用环境,包括读取配置文件和设置环境变量
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

        // 配置是否忽略 BeanInfo,以加快启动速度
        configureIgnoreBeanInfo(environment);

        // 5.打印启动Banner
        Banner printedBanner = printBanner(environment);

        // 6.创建应用程序上下文
        context = createApplicationContext();
        
        // 设置应用启动的上下文,用于监控和管理启动过程
        context.setApplicationStartup(this.applicationStartup);

        // 7.准备应用上下文,包括加载配置、添加 Bean 等
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

        // 8.刷新上下文,完成 Bean 的加载和依赖注入
        refreshContext(context);

        // 9.刷新后的一些操作,如事件发布等
        afterRefresh(context, applicationArguments);

        // 计算启动应用程序的时间,并记录日志
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
        }

        // 10.通知监听器应用启动完成
        listeners.started(context, timeTakenToStartup);

        // 11.调用应用程序中的 `CommandLineRunner` 或 `ApplicationRunner`,以便执行自定义的启动逻辑
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        // 12.处理启动过程中发生的异常,并通知监听器
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }
    try {
        // 13.计算应用启动完成至准备就绪的时间,并通知监听器
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        listeners.ready(context, timeTakenToReady);
    }
    catch (Throwable ex) {
        // 处理准备就绪过程中发生的异常
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }

    // 返回已启动并准备就绪的应用上下文
    return context;
}

源码入口

  • 这里核心内容就是new AnnotationConfigServletWebServerApplicationContext()
// 6.创建应用程序上下文
context = createApplicationContext();

在这里插入图片描述

  • 无参构造
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext
        implements AnnotationConfigRegistry {

    // 用于读取注解的Bean定义读取器
    private final AnnotatedBeanDefinitionReader reader;

    // 用于扫描类路径并注册Bean定义的扫描器
    private final ClassPathBeanDefinitionScanner scanner;

	...

    // 无参构造函数
    public AnnotationConfigServletWebServerApplicationContext() {
        // 初始化注解Bean定义读取器
        this.reader = new AnnotatedBeanDefinitionReader(this);
        // 初始化类路径Bean定义扫描器
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

	...
}

  AnnotationConfigServletWebServerApplicationContext类图如下,其实就是应用上下文核心接口Application的实现类,那么注解Bean定义读取器类路径Bean定义扫描器构造传入的this即Application。

在这里插入图片描述

一、初始化注解Bean定义读取器

public class AnnotatedBeanDefinitionReader {

    // Bean定义注册表,用于管理和注册Bean定义
    private final BeanDefinitionRegistry registry;

    // 条件评估器,用于判断是否满足某些条件
    private ConditionEvaluator conditionEvaluator;

    // 构造方法,接收BeanDefinitionRegistry作为参数
    public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
        // 调用带有Environment参数的构造方法,环境对象通过注册表自动创建
        this(registry, getOrCreateEnvironment(registry));
    }

    // 构造方法,接收BeanDefinitionRegistry和Environment作为参数
    public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
        // 校验参数,确保注册表和环境不为空
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        Assert.notNull(environment, "Environment must not be null");
        // 初始化Bean定义注册表
        this.registry = registry;
        // 初始化条件评估器
        this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
        // 注册注解配置处理器,用于处理注解配置
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }
    ...
}

1、BeanDefinitionRegistry(Bean定义注册接口)

  这里的Bean定义注册表BeanDefinitionRegistry就是AnnotatedBeanDefinitionReader构造传入的AnnotationConfigServletWebServerApplicationContext

  BeanDefinitionRegistry是Spring容器中用于管理Bean定义的核心接口,支持动态注册移除查询别名管理,常用于扩展和动态操作容器内的 Bean 定义。

// 此接口是 Bean 定义注册的核心,用于动态管理 Bean 定义(注册、移除、查询等)
public interface BeanDefinitionRegistry extends AliasRegistry {

	// 向注册表中注册一个新的 BeanDefinition
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;

	// 移除给定名称的 BeanDefinition
	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	// 返回给定名称的 BeanDefinition
	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	// 检查此注册表是否包含具有给定名称的 BeanDefinition。
	boolean containsBeanDefinition(String beanName);

	// 返回此注册表中定义的所有 Bean 的名称
	String[] getBeanDefinitionNames();
	
	//  返回注册表中定义的 Bean 的数量。
	int getBeanDefinitionCount();

	 // 确定给定的 Bean 名称是否已经在此注册表中使用(即是否有本地 Bean 或别名注册了此名称)
	boolean isBeanNameInUse(String beanName);
}


/**
 * 管理别名的通用接口(提供了注册、删除、查询别名的方法)
 *
 * 别名是一种机制,用于为已有的名称提供额外的标识,
 * 通常用于配置中增加灵活性,例如为同一个 Bean 定义多个名称
 */
public interface AliasRegistry {

	// 为给定的名称注册一个别名
	void registerAlias(String name, String alias);

	// 从注册表中删除指定的别名
	void removeAlias(String alias);

	// 确定给定的名称是否被定义为别名(而不是实际注册的组件名称)
	boolean isAlias(String name);

	// 返回给定名称的所有别名(如果定义了别名)
	String[] getAliases(String name);
}

2、获取环境对象Environment

// AnnotatedBeanDefinitionReader类方法
private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
    // 校验注册表对象不为空
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    // 如果注册表实现了EnvironmentCapable接口,则直接获取其中的Environment
    if (registry instanceof EnvironmentCapable) {
        return ((EnvironmentCapable) registry).getEnvironment();
    }
    
    // 如果注册表不具备Environment,则创建并返回一个标准的Environment对象
    return new StandardEnvironment();
}

  在创建应用上下文之前,SpringBoot源码解析(五):准备应用环境中有详细介绍应用环境Environment的初始化。

在这里插入图片描述

3、注册注解配置处理器

在这里插入图片描述

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
        BeanDefinitionRegistry registry, @Nullable Object source) {

    // 获取默认的ListableBeanFactory对象
    // 这里获取到的就是应用上下文中AnnotationConfigServletWebServerApplicationContext的
    // 父类GenericApplicationContext的构造中new DefaultListableBeanFactory()
    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    if (beanFactory != null) {
        // AnnotationAwareOrderComparator:这是一个带有注解感知的比较器,用来排序Bean的依赖
        // 它比标准的比较器多了对@Order注解的支持,可以确保按照注解指定的优先级排序依赖关系
        if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
            beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
        }
		// ContextAnnotationAutowireCandidateResolver:这个解析器是用于处理基于注解的自动注入(例如@Autowired注解)
		// 它负责在自动注入过程中,决定哪些Bean可以作为自动注入的候选者
        if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
            beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        }
    }

    // 创建一个 LinkedHashSet 用于存储 BeanDefinitionHolder
    // BeanDefinitionHolder: 用于持有 BeanDefinition 及其名称和别名
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

    // 注册 ConfigurationClassPostProcessor,用于处理 @Configuration 注解
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        // 注册后置处理器就是将Bean定义添加到bean工厂beanDefinitionMap缓存中,下面细说
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 注册 AutowiredAnnotationBeanPostProcessor,用于处理 @Autowired 注解
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 如果 JSR-250 支持存在,注册 CommonAnnotationBeanPostProcessor,用于处理 @Resource 等注解
    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 如果 JPA 支持存在,注册 PersistenceAnnotationBeanPostProcessor,用于处理 JPA 相关的注解
    // JPA是一个 Java 标准规范,用于简化对象与关系数据库之间的映射和数据持久化操作
    if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition();
        try {
            def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
                    AnnotationConfigUtils.class.getClassLoader()));
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
        }
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 注册 EventListenerMethodProcessor,用于处理 @EventListener 注解
    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }

    // 注册 DefaultEventListenerFactory 是事件监听器的工厂类,它实际负责创建监听器对象并将其注册到 Spring 的事件发布机制中
    // 当 EventListenerMethodProcessor 找到带有 @EventListener 注解的方法时,它会通过 DefaultEventListenerFactory 创建该方法对应的监听器
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }

    // 返回所有注册的 BeanDefinitionHolder
    return beanDefs;
}

3.1、获取默认Bean工厂

  这里的registry就是注解应用上下文AnnotationConfigServletWebServerApplicationContext,从上面的类图可知GenericApplicationContext是父类之一。

// AnnotationConfigUtils类方法
@Nullable
private static DefaultListableBeanFactory unwrapDefaultListableBeanFactory(BeanDefinitionRegistry registry) {
	if (registry instanceof DefaultListableBeanFactory) {
		return (DefaultListableBeanFactory) registry;
	}
	else if (registry instanceof GenericApplicationContext) {
		return ((GenericApplicationContext) registry).getDefaultListableBeanFactory();
	}
	else {
		return null;
	}
}

在这里插入图片描述

  DefaultListableBeanFactory是Spring框架中的一个核心类,负责管理创建应用上下文中的所有Bean,提供了Bean定义的注册、查找和生命周期管理功能。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

	// ==================== 核心属性 ====================
	
	// 存储所有注册的 BeanDefinition,键为 beanName,值为对应的 BeanDefinition
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
	
	// 记录所有注册的beanName,保持注册顺序(通过配置文件、注解、或约定方式将 Bean 注册到容器中,由 Spring 自动完成)
	private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
	
	...
}

获取到默认Bean工厂以后,设置了两个重要属性

  AnnotationAwareOrderComparator:这是一个带有注解感知的比较器,用来排序Bean的依赖。它比标准的比较器多了对@Order注解的支持,可以确保按照注解指定的优先级排序依赖关系。

  ContextAnnotationAutowireCandidateResolver:这个解析器是用于处理基于注解的自动注入(例如@Autowired注解)。它负责在自动注入过程中,决定哪些Bean可以作为自动注入的候选者。

3.2、注册后置处理器(注册Bean定义)

上面创建了很多RootBeanDefinition,这些Bean定义只做Spring内部使用,用于处理注解配置

  • ConfigurationClassPostProcessor,用于处理@Configuration@Bean@Import@ComponentScan等注解
  • AutowiredAnnotationBeanPostProcessor,用于处理@Autowired@Value注解
  • CommonAnnotationBeanPostProcessor,用于处理@PostConstruct@PreDestroy@Resource注解
  • EventListenerMethodProcessor,用于处理@EventListener注解

创建完RootBeanDefinition后,还需要将其添加到应用上下文缓存

  Bean定义的角色设置为2,表示框架内部的实现类,用户无需关心,平常我们创建的组件默认角色就是0,用于实现具体的业务逻辑。

// AnnotationConfigUtils类方法
private static BeanDefinitionHolder registerPostProcessor(
		BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
	definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	// 注册Bean定义
	registry.registerBeanDefinition(beanName, definition);
	return new BeanDefinitionHolder(definition, beanName);
}

  BeanDefinitionRegistry registry是应用上下文,调用注册Bean定义方法registerBeanDefinition,实际就是调用默认Bean工厂的注册Bean定义的方法,上面有提到默认Bean工厂DefaultListableBeanFactory核心属性beanDefinitionMap就是存储所有注册的BeanDefinition

在这里插入图片描述

二、初始化类路径Bean定义扫描器

1、注册默认注解过滤器

  通过ClassPathBeanDefinitionScanner的构造方法,最终跳转到最后一个构造方法,核心操作是注册默认的注解扫描过滤器

在这里插入图片描述

  注册默认的注解过滤器,确保在组件扫描时,能够识别特定的注解。添加@Component注解的过滤器,允许扫描带有@Component注解的类,@Controller@Service@Repository也会被扫描到。

// ClassPathScanningCandidateComponentProvider类方法
protected void registerDefaultFilters() {
    // 添加 @Component 注解的过滤器,允许扫描带有 @Component 注解的类
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));

	// @Component 是 Spring 框架的通用组件注解(只关心这个就可以)
	// @ManagedBean 是 Java EE 的托管 Bean 注解
	// 而 @Named 是 CDI(Jakarta EE)的标准化注解

    // 获取类加载器
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        // 尝试加载 javax.annotation.ManagedBean 注解类,如果存在,添加为过滤器
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
        logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // 如果 JSR-250 API 未找到,跳过该注解支持
    }
    try {
        // 尝试加载 javax.inject.Named 注解类,如果存在,添加为过滤器
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
        logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // 如果 JSR-330 API 未找到,跳过该注解支持
    }
}

2、自定义包扫描

  此方法将在后续通过传入包路径调用,并返回注册的Bean定义集合。具体源码后续调用时候详细

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // 检查 basePackages 是否为空,如果为空则抛出异常
    Assert.notEmpty(basePackages, "At least one base package must be specified");

    // 用于存储扫描到的 Bean 定义
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();

    // 遍历传入的每个包路径进行扫描
    for (String basePackage : basePackages) {
        // 查找当前包路径下符合条件的候选组件
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

        // 遍历候选组件
        for (BeanDefinition candidate : candidates) {
            // 获取该 Bean 定义的作用域元数据,并设置作用域
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());

            // 生成 Bean 的名称
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

            // 如果候选 Bean 是 AbstractBeanDefinition 类型,进行后处理
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }

            // 如果候选 Bean 是 AnnotatedBeanDefinition 类型,处理注解配置
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }

            // 如果当前 Bean 名称和定义符合要求,则继续处理
            if (checkCandidate(beanName, candidate)) {
                // 创建一个 BeanDefinitionHolder 来包装 Bean 定义和 Bean 名称
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);

                // 根据作用域元数据,可能应用代理模式
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

                // 将处理好的 Bean 定义添加到集合中
                beanDefinitions.add(definitionHolder);

                // 将 Bean 定义注册到容器中
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }

    // 返回所有注册的 Bean 定义
    return beanDefinitions;
}

总结

  本文详细介绍了SpringBoot应用上下文AnnotationConfigServletWebServerApplicationContext的构造方法中初始化两个重要组件:一是注解Bean定义读取器,创建一些特殊Bean定义(Spring内部使用,也叫后置处理器),用于处理@Configuration、@Autowired等注解;二是类路径Bean定义扫描器,用于扫描给定路径下的@Component注解组件,将其转换为Bean定义。

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

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

相关文章

Unity小功能实现:鼠标点击移动物体

1、功能描述 当玩家点击鼠标时&#xff0c;场景中的物体会移动到鼠标点击的位置。这个功能可以用于控制角色移动、放置物体等场景。 2、实现步骤 创建Unity项目&#xff1a;首先&#xff0c;打开Unity并创建一个新的3D项目。 添加3D物体&#xff1a;在场景中创建一个3D物体&am…

游戏引擎学习第127天

仓库:https://gitee.com/mrxiao_com/2d_game_3 为本周设定阶段 我们目前的渲染器已经实现了令人惊讶的优化&#xff0c;经过过去两周的优化工作后&#xff0c;渲染器在1920x1080分辨率下稳定地运行在60帧每秒。这个结果是意料之外的&#xff0c;因为我们没有预计会达到这样的…

Opencv 图像基本操作

1.1 数据读取-图像 opencv读取的格式是BGR而不是RGB import cv2 import matplotlib.pyplot as plt import numpy as np %matplotlib inline # 在Notebook的输出单元格内嵌入绘制的图形&#xff0c;而不在新窗口中显示img cv2.imread(cat.jpg) # cv2.IMREAD_COLOR&#xff1a…

【微知】ssh如何指定免密的2种简单方式?(vim ~/.ssh/authorized_keys、ssh-copy-id)

背景 ssh通过存储公钥到远端服务器&#xff0c;可以完成本端访问远端服务器的时候免密。免密原理是本端使用私钥&#xff0c;远端公钥&#xff0c;远端可以进行鉴权 方法1&#xff1a; vim ~/.ssh/authorized_keys 将本地电脑的pub的key直接copy到远端 ~/.ssh/authorized_ke…

Skywalking介绍,Skywalking 9.4 安装,SpringBoot集成Skywalking

一.Skywalking介绍 Apache SkyWalking是一个开源的分布式追踪与性能监视平台&#xff0c;特别适用于微服务架构、云原生环境以及基于容器&#xff08;如Docker、Kubernetes&#xff09;的应用部署。该项目由吴晟发起&#xff0c;并已加入Apache软件基金会的孵化器&#xff0c;…

卷积神经网络(cnn,He初始化+relu+softmax+交叉熵+卷积核,六)

He初始化relusoftmax交叉熵卷积核&#xff0c;才是cnn&#xff0c;我们推导的公式&#xff1a; **** &#xff08;p【k】-y【k】&#xff09;*drelu(yi[k])*w2[j, k]*drelu(hi[j])*x【i】 只能满足&#xff1a;He初始化relusoftmax交叉熵。 我们参考&#xff1a; cnn突破七…

【区块链 + 智慧政务】 伽罗华域:区块链数据溯源系统 | FISCO BCOS 应用案例

由北京伽罗华域科技有限公司打造的区块链数据溯源系统&#xff0c; 实现了数据从生产、管理到共享的全流程可追溯性和安全审计。系统支持数据的全生命周期管理&#xff0c; 包括数据采集、生产、共享等关键流程&#xff0c; 并通过智能合约自动执行数据的存证、共享与安全审计&…

矩阵基本概念

前言 本文隶属于专栏《机器学习数学通关指南》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见《机器学习数学通关指南》 正文 一句话理解矩阵 矩阵是数据排…

本地部署大语言模型-DeepSeek

DeepSeek 是国内顶尖 AI 团队「深度求索」开发的多模态大模型&#xff0c;具备数学推理、代码生成等深度能力&#xff0c;堪称"AI界的六边形战士"。 Hostease AMD 9950X/96G/3.84T NVMe/1G/5IP/RTX4090 GPU服务器提供多种计费模式。 DeepSeek-R1-32B配置 配置项 规…

xss自动化扫描工具-DALFox

声明&#xff01;本文章所有的工具分享仅仅只是供大家学习交流为主&#xff0c;切勿用于非法用途&#xff0c;如有任何触犯法律的行为&#xff0c;均与本人及团队无关&#xff01;&#xff01;&#xff01; 工具&#xff1a;https://pan.quark.cn/s/3d824b8637f1 目录标题 一、…

P8720 [蓝桥杯 2020 省 B2] 平面切分--set、pair

P8720 [蓝桥杯 2020 省 B2] 平面切分--set、pair 题目 分析一、pair1.1pair与vector的区别1.2 两者使用场景两者组合使用 二、set2.1核心特点2.2set的基本操作2.3 set vs unordered_set示例&#xff1a;统计唯一单词数代码 题目 分析 大佬写的很明白&#xff0c;看这儿 我讲讲…

vue3学习-1(基础)

vue3学习-1&#xff08;基础&#xff09; 1. 开始API 风格选项式 API (Options API)组合式 API (Composition API) 快速创建个应用 2.基础1. 创建个应用2.模板语法3.响应式基础reactive() 的局限性[](https://cn.vuejs.org/guide/essentials/reactivity-fundamentals.html#limi…

eMMC安全简介

1. 引言 术语“信息安全”涵盖多种不同的设计特性。一般而言&#xff0c; 信息安全是指通过实践防止信息遭受未经授权的访问、使用、披露、中断、篡改、检查、记录或销毁。 信息安全的三大核心目标为 机密性&#xff08;Confidentiality&#xff09;、完整性&#xff08;Integr…

SpringBoot新闻推荐系统设计与实现

随着信息时代的快速发展&#xff0c;新闻推荐系统成为用户获取个性化内容的重要工具。本文将介绍一个幽络源的基于SpringBoot开发的新闻推荐系统&#xff0c;该系统功能全面&#xff0c;操作简便&#xff0c;能够满足管理员和用户的多种需求。 管理员模块 管理员模块为系统管…

zookeeper-docker版

Zookeeper-docker版 1 zookeeper概述 1.1 什么是zookeeper Zookeeper是一个分布式的、高性能的、开源的分布式系统的协调&#xff08;Coordination&#xff09;服务&#xff0c;它是一个为分布式应用提供一致性服务的软件。 1.2 zookeeper应用场景 zookeeper是一个经典的分…

Java 调试模式下 Redisson 看门狗失效

一、场景分析 前几天在做分布式锁测试&#xff1a; 在调试模式下&#xff0c;lock.lock() 之后打上断点&#xff0c;想测试一下在当前线程放弃锁之前&#xff0c;别的线程能否获取得到锁。 发现调试模式下&#xff0c;看门狗机制失效了&#xff0c;Redis 上 30 秒后&#xff0…

FFmpeg入门:最简单的音频播放器

FFmpeg入门&#xff1a;最简单的音频播放器 欢迎大家来到FFmpeg入门的第二章&#xff0c;今天只做一个最简单的FFmpeg音频播放器&#xff1b;同样&#xff0c;话不多说&#xff0c;先上流程图 流程图 以上流程和视频播放器的解码过程基本上是一致的&#xff1b; 不同点在于 S…

基于Python Django的人脸识别上课考勤系统(附源码,部署)

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

【小羊肖恩】小羊杯 Round 2 C+K

题目链接&#xff1a;https://ac.nowcoder.com/acm/contest/100672#question C.是毛毛虫吗&#xff1f; 思路&#xff1a; 其实很简单&#xff0c;假设我们要满足题目所给条件&#xff0c;那么这个毛毛虫最坏情况下肯定是一条如下图所示的无向图 右端省略号为对称图形 &…

PyCharm怎么集成DeepSeek

PyCharm怎么集成DeepSeek 在PyCharm中集成DeepSeek等大语言模型(LLM)可以借助一些插件或通过代码调用API的方式实现,以下为你详细介绍两种方法: 方法一:使用JetBrains AI插件(若支持DeepSeek) JetBrains推出了AI插件来集成大语言模型,不过截至2024年7月,官方插件主要…