[Springboot 源码系列] 浅析自动配置原理

文章目录

    • 自动配置类原理
    • `AopAutoConfigurartion`
    • 条件装配的底层原理
      • `@Conditional`
      • `@ConditionalOnXxx`

自动配置类原理


public class AutoConfApplication {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean("config", Config.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(">>>" + name);
        }
    }

    @Configuration
    static class Config {

    }

    static class Auto {

        @Bean
        public Bean1 bean1() {
            return new Bean1("自动配置");
        }

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }

    }

    @Data
    @AllArgsConstructor
    static class Bean1 {
        private String name;
    }

    static class Bean2 { }
}

>>>org.springframework.context.annotation.ConfigurationClassPostProcessor
>>>config

可以看出 Auto没有加 @Configuration不会被 Spring容器管理,所以不会加载。此时我们进行以下改造

    @Configuration
    @Import({Auto.class})
    static class Config {

    }

上述代码的打印结果为:

>>>org.springframework.context.annotation.ConfigurationClassPostProcessor
>>>config
>>>com.example.auto_conf.AutoConfApplication$Auto
>>>bean1
>>>bean2

可以看到模拟自动配置的类正常起作用(bean1, bean2 被加载到容器)。

除了使用 @Import注解意外,可以使用导入选择器 ImportSelector,重写 selectImports() 方法,返回需要自动装配的 Bean 的全限定类名数组批量导入:


public class AutoConfApplication {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean("config", Config.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(">>>" + name);
        }
    }

    @Configuration
    @Import({MyImportSelector.class})
    static class Config {

    }

    static class MyImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{Auto.class.getName()};
        }
    }

    static class Auto {

        @Bean
        public Bean1 bean1() {
            return new Bean1("自动配置");
        }

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }

    }

    @Data
    @AllArgsConstructor
    static class Bean1 {
        private String name;
    }

    static class Bean2 { }
}

打印 以下结果,说明 ImportSelector生效。

>>>org.springframework.context.annotation.ConfigurationClassPostProcessor
>>>config
>>>com.example.auto_conf.AutoConfApplication$Auto
>>>bean1
>>>bean2

但这样的方式相比最初的方式并没有本质区别,甚至更麻烦,还多了一个类。如果 selectImports() 方法返回的全限定类名可以从文件中读取,就更方便了。
所以,在当前项目的类路径下创建 META-INF/spring.factories 文件,约定一个 key,对应的 value 即为需要指定装配的 Bean:

package com.example.auto_conf;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class AutoConfApplication {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean("config", Config.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(">>>" + name);
        }
    }

    @Configuration
    @Import({MyImportSelector.class})
    static class Config {

    }

    static class MyImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
            return names.toArray(new String[0]);
        }
    }

    static class Auto {

        @Bean
        public Bean1 bean1() {
            return new Bean1("自动配置");
        }


    }
    static class Auto1 {


        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }

    }

    @Data
    @AllArgsConstructor
    static class Bean1 {
        private String name;
    }

    static class Bean2 { }
}

// spring.factories
# 内部类使用$
com.example.auto_conf.AutoConfApplication$MyImportSelector=\
    com.example.auto_conf.AutoConfApplication.Auto, \
    com.example.auto_conf.AutoConfApplication.Auto1            

打印结果

>>>org.springframework.context.annotation.ConfigurationClassPostProcessor
>>>config
>>>com.example.auto_conf.AutoConfApplication$Auto
>>>bean1
>>>com.example.auto_conf.AutoConfApplication$Auto1
>>>bean2

SpringFactoriesLoader.loadFactoryNames() 不仅只扫描当前项目类型路径下的 META-INF/spring.factories 文件,而是会扫描包括 Jar 包里类路径下的 META-INF/spring.factories 文件。

针对 SpringBoot 来说,自动装配的 Bean 使用如下语句加载:

SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, null);

image.png

AopAutoConfigurartion

常用工具类

 // 注册常用的后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());

示例代码

package com.example.auto_conf;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;

import java.util.List;

public class AopAutoConfApplication {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();

        // 注册常用的后处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean("config", Config.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(">>>" + name);
        }

    }

    @Configuration
    @Import({MyImportSelector.class})
    static class Config {

    }

    static class MyImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{AopAutoConfiguration.class.getName()};
        }
    }

}

打印以下结果

>>>org.springframework.context.annotation.internalConfigurationAnnotationProcessor
>>>org.springframework.context.annotation.internalAutowiredAnnotationProcessor
>>>org.springframework.context.annotation.internalCommonAnnotationProcessor
>>>org.springframework.context.event.internalEventListenerProcessor
>>>org.springframework.context.event.internalEventListenerFactory
>>>org.springframework.context.annotation.ConfigurationClassPostProcessor
>>>config
>>>com.example.auto_conf.AopAutoConfApplication

代码添加命令行参数

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    StandardEnvironment env = new StandardEnvironment();
    env.getPropertySources().addLast(
        new SimpleCommandLinePropertySource("--spring.aop.auto=false")
    );
    context.setEnvironment(env);
}

AopAutoConfiguration类源码

AopAutoConfiguration```java

@AutoConfiguration
// 存在spring.aop=true 或者不存在时生效
@ConditionalOnProperty(prefix = “spring.aop”,name = {“auto”}, havingValue = “true”,matchIfMissing = true)
public class AopAutoConfiguration {
public AopAutoConfiguration() {
}

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnMissingClass({"org.aspectj.weaver.Advice"})
@ConditionalOnProperty(prefix = "spring.aop",name = {"proxy-target-class"},havingValue = "true",matchIfMissing = true
)
static class ClassProxyingConfiguration {
    ClassProxyingConfiguration() {
    }

    @Bean
    static BeanFactoryPostProcessor forceAutoProxyCreatorToUseClassProxying() {
        return (beanFactory) -> {
            if (beanFactory instanceof BeanDefinitionRegistry registry) {
                AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }

        };
    }
}

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({Advice.class})
static class AspectJAutoProxyingConfiguration {
    AspectJAutoProxyingConfiguration() {
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @EnableAspectJAutoProxy(
        proxyTargetClass = true
    )
    @ConditionalOnProperty(prefix = "spring.aop",name = {"proxy-target-class"},havingValue = "true",
        matchIfMissing = true
    )
    static class CglibAutoProxyConfiguration {
        CglibAutoProxyConfiguration() {
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @EnableAspectJAutoProxy(
        proxyTargetClass = false
    )
    @ConditionalOnProperty(prefix = "spring.aop",name = {"proxy-target-class"},havingValue = "false"
    )
    static class JdkDynamicAutoProxyConfiguration {
        JdkDynamicAutoProxyConfiguration() {
        }
    }
}

}

> 从源码中可以看出,`Spring`默认使用 `Cglib`只有 `spring.aop.proxy-target-class=false`的时候才会使用 `Jdk`代理。

## 数据库相关自动配置
代码准备
```xml
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.0</version>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
    </dependencies>

未使用配置文件,而是使用 StandardEnvironment 设置了一些数据库连接信息。

package com.example.auto_conf;

import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.type.AnnotationMetadata;

public class DbAutoConfApplication {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();

        StandardEnvironment env = new StandardEnvironment();
        env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
            "--spring.datasource.url=jdbc:mysql://localhost:3306/advanced_spring",
            "--spring.datasource.username=root",
            "--spring.datasource.password=123456"
        ));
        context.setEnvironment(env);
        // 注册常用的后处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean("config", Config.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            String resourceDescription = context.getBeanDefinition(name).getResourceDescription();
            if (resourceDescription != null){
                System.out.println(name + " 来源: \n" + resourceDescription);
                System.out.println("-----------");
            }

        }


    }

    @Configuration
    @Import({DataSourceAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class,
        MybatisAutoConfiguration.class,
        TransactionAutoConfiguration.class
    })
    static class Config {

    }

}

打印

jdbcConnectionDetailsHikariBeanPostProcessor 来源: 
org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari
-----------
dataSource 来源: 
org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari
-----------
jdbcConnectionDetails 来源: 
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceConfiguration
-----------
hikariPoolDataSourceMetadataProvider 来源: 
org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration
-----------
transactionManager 来源: 
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration$JdbcTransactionManagerConfiguration
-----------
sqlSessionFactory 来源: 
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
-----------
sqlSessionTemplate 来源: 
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
-----------
org.springframework.transaction.config.internalTransactionAdvisor 来源: 
class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]
-----------
transactionAttributeSource 来源: 
class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]
-----------
transactionInterceptor 来源: 
class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]
-----------
org.springframework.transaction.config.internalTransactionalEventListenerFactory 来源: 
class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]
-----------
transactionTemplate 来源: 
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$TransactionTemplateConfiguration
-----------

可以看到 dataSource 来源: org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari

条件装配的底层原理

@Conditional

SpringBoot 的自动配置中,经常看到 @Conditional 注解的使用,使用该注解可以按条件加载配置类。
@Conditional 注解并不具备条件判断功能,而是通过指定的 Class 列表来进行判断,指定的 Class 需要实现 Condition 接口。


public class ConditionalApplication {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean("config", Config.class);
        context.refresh();

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }

    }

    @Configuration
    static class Config {
        @Bean
        @Conditional(MyConditional.class)
        public Bean1 bean1() {
            return new Bean1();
        }
        @Bean
        @Conditional(MyConditional2.class)
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class H {}

    static class MyConditional implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return ClassUtils.isPresent("com.example.auto_conf.ConditionalApplication.H", null);
        }
    }

    static class MyConditional2 implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return !ClassUtils.isPresent("com.example.auto_conf.ConditionalApplication.H", null);
        }
    }
    static class Bean1 { }

    static class Bean2 { }
}

输出: 把 H类注释掉时,bean2注册, 否则 bean1注册。

@ConditionalOnXxx

SpringBootBean存在才生效的源码

package org.springframework.boot.autoconfigure.condition;

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
	...
}

模仿 SpringBoot 的组合注解进行条件注册 bean

public class ConditionalApplication {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean("config", Config.class);
        context.refresh();

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }

    }

    @Configuration
    static class Config {
        @Bean
        @ConditionalOnClass(className = "com.example.auto_conf.ConditionalApplication.H")
        public Bean1 bean1() {
            return new Bean1();
        }
        @Bean
        @ConditionalOnClass(exist = false, className = "com.example.auto_conf.ConditionalApplication.H")
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class H {}

    static class MyConditional implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());
            boolean exist = (boolean) attributes.get("exist");
            String className = attributes.get("className").toString();
            boolean present = ClassUtils.isPresent(className, null);
            return exist == present;
        }
    }


    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Conditional(MyConditional.class)
    @interface ConditionalOnClass {
        boolean exist() default true;

        String className();
    }

    static class Bean1 { }

    static class Bean2 { }
}

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

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

相关文章

Git命令之本地分支与远程分支支关联/解除关联

目录 1.应用场景2.关联远程仓库中存在的分支3.解除本地分支与远程分支的关联 1.应用场景 在实际的工作生活中&#xff0c;往往需要将本地的分支和远程分支关联&#xff0c;这样我们就可以使用git pull命令来更新拉取最新的代码&#xff0c;并使用git push命令将自己本地的修改…

设备远程监控系统:实时对工厂设备状态进行监控

随着工业4.0时代的到来&#xff0c;工业物联网平台在工业领域的应用越来越广泛。设备远程监控系统作为工业物联网的重要组成部分&#xff0c;能够实时对工厂设备状态进行监控&#xff0c;提高生产效率和管理水平。本文将详细介绍工厂设备数据的采集方式以及设备远程监控系统的应…

企业寄件运费贵?企业如何将企业快递费用降至最低?

按照上述情况&#xff0c;如果使用闪侠惠递商家寄件服务&#xff0c;相较于原本的费用&#xff0c;会节省很大一部分的成本费用的&#xff0c;显而易见&#xff0c;这是非常便宜的。 闪侠惠递的寄件服务支持圆通、德邦、京东、顺丰等多家主流快递公司&#xff0c;并可以对比多…

Git-瑞吉外卖

什么是GIt? 分布式版本控制工具&#xff0c;用来管理源代码文件。分布式主要体现在两种仓库&#xff08;本地仓库、远程仓库&#xff09;。 git的作用&#xff1f; 代码回溯、版本切换&#xff08;切换不同框架&#xff09;、多人协作、远程备份 基本命令&…

虚拟化逻辑架构:KVM虚拟机通过OVS端口组实现网络连接

目录 一、实验 1.CentOS 7 安装 OpenVSwitch(构建RPM安装包&#xff09; 2.KVM虚拟机通过OVS端口组实现网络连接 二、问题 1.安装openvswitch-2.5.10报错 2.virt-install未找到命令 3.如何删除自定义网络 4.开机如何自动启动自定义网络 一、实验 1.CentOS 7 安装 Open…

双指针算法(一)

目录 移动零 复写零 快乐数 盛水最多的容器 双指针与单调性结合 有效三角形的个数 查找总价格为目标值的两个商品 两数之和 Ⅱ - 输入有序数组 双指针算法是通过定义两个指针不断单向移动来解决问题的一种算法。但双指针算法&#xff0c;是一个抽象的思想概念&#xf…

【C语言】超详解strncpystrncatstrncmpstrerrorperror的使⽤和模拟实现

&#x1f308;write in front :&#x1f50d;个人主页 &#xff1a; 啊森要自信的主页 ✏️真正相信奇迹的家伙&#xff0c;本身和奇迹一样了不起啊&#xff01; 欢迎大家关注&#x1f50d;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;>希望看完我的文章对你有小小的帮助&am…

智能优化算法应用:基于原子搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于原子搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于原子搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.原子搜索算法4.实验参数设定5.算法结果6.…

12.4~12.14概率论复习与相应理解(学习、复习、备考概率论,这一篇就够了)

未分配的题目 概率计算&#xff08;一些转换公式与全概率公式&#xff09;与实际概率 &#xff0c;贝叶斯 一些转换公式 相关性质计算 常规&#xff0c;公式的COV与P 复习相关公式 计算出新表达式的均值&#xff0c;方差&#xff0c;再套正态分布的公式 COV的运算性质 如…

FastAPI访问/docs接口文档显示空白、js/css无法加载

如图&#xff1a; 原因是FastAPI的接口文档默认使用https://cdn.jsdelivr.net/npm/swagger-ui-dist5.9.0/swagger-ui.css 和https://cdn.jsdelivr.net/npm/swagger-ui-dist5.9.0/swagger-ui-bundle.js 来渲染页面&#xff0c;而这两个URL是外网的CDN&#xff0c;在国内响应超…

五、Java核心数组篇

1.数组 概念&#xff1a; ​ 指的是一种容器&#xff0c;可以同来存储同种数据类型的多个值。 ​ 但是数组容器在存储数据的时候&#xff0c;需要结合隐式转换考虑。 比如&#xff1a; ​ 定义了一个int类型的数组。那么boolean。double类型的数据是不能存到这个数组中的&…

【RocketMQ】顺序消费消息实现原理分析

一、顺序消息概述 1.1、什么是顺序消息 顺序消息是指对于一个指定的 Topic &#xff0c;消息严格按照先进先出&#xff08;FIFO&#xff09;的原则进行消息发布和消费&#xff0c;即先发布的消息先消费&#xff0c;后发布的消息后消费。 1.2、顺序消息的类型 全局顺序消息 …

经典深度学习算法【1】:K-近邻算法(KNN)概述

最简单最初级的分类器是将全部的训练数据所对应的类别都记录下来&#xff0c;当测试对象的属性和某个训练对象的属性完全匹配时&#xff0c;便可以对其进行分类。但是怎么可能所有测试对象都会找到与之完全匹配的训练对象呢&#xff0c;其次就是存在一个测试对象同时与多个训练…

JVM虚拟机系统性学习-JVM调优之GC日志分析

JVM 调优 首先&#xff0c;为什么要 JVM 调优呢&#xff1f; JVM 调优的目的就是为了让应用程序使用最小的硬件消耗来承载更大的吞吐量 什么情况下需要 JVM 调优呢&#xff1f; 系统吞吐量下降&#xff0c;或系统延迟较高出现 OOMFull GC 频繁GC 停顿时间过长&#xff08;超…

UE虚幻引擎中程序无需运行也可调试

首先先新建一个蓝图类&#xff0c;在蓝图类中创建一个Custom event 事件&#xff0c;然后在右侧细节面板中搜索call in editor&#xff0c;编译保存之后&#xff0c;将该蓝图类拖拽到关卡场景中&#xff0c;在细节面板中即可看到该事件的按钮。

PE文件格式-PE文件头部

文章目录 PE文件头基本概念简介地址对齐 PE结构概述PE文件头部DOS MZ 头PE头&#xff08;NT头&#xff09;PE头标识 Signature标准PE头 IMAGE_FILE_HEADERMachineNumberOfSectionsTimeDateStampPointToSymbolTableNumberOfSymbolSizeOfOptionalHeaderCharacteristics 扩展PE头 …

亚信科技AntDB数据库——深入了解AntDB-M元数据锁的相关概念

AntDB-M在架构上分为两层&#xff0c;服务层和存储引擎层。元数据的并发管理集中在服务层&#xff0c;数据的存储访问在存储引擎层。为了保证DDL操作与DML操作之间的一致性&#xff0c;引入了元数据锁&#xff08;MDL&#xff09;。 AntDB-M提供了丰富的元数据锁功能&#xff…

【Hadoop】执行start-dfs.sh启动hadoop集群时,datenode没有启动怎么办

执行start-dfs.sh后&#xff0c;datenode没有启动&#xff0c;很大一部分原因是因为在第一次格式化dfs后又重新执行了格式化命令&#xff08;hdfs namenode -format)&#xff0c;这时主节点namenode的clusterID会重新生成&#xff0c;而从节点datanode的clusterID 保持不变。 在…

认识缓存,一文读懂Cookie,Session缓存机制。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)十四:系统设置模块相关功能实现

一、本章内容 本章使用已实现的公共组件实现系统管理中的系统设置模块相关功能,包括菜单管理、角色管理、日志管理、用户管理、系统配置、数据字典等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 3.1 B站视频地址: