当前位置: 首页 > news >正文

第五十三章 Spring之假如让你来写Boot——环境篇

Spring源码阅读目录

第一部分——IOC篇

第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇

第二部分——AOP篇

第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
第十三章 Spring之假如让你来写AOP——AOP联盟篇
第十四章 Spring之假如让你来写AOP——雏形篇
第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP——Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP——Advice(通知)上篇
第十八章 Spring之假如让你来写AOP——Advice(通知)下篇
第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP——Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP——Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP——融入IOC容器篇
第二十四章 Spring之源码阅读——AOP篇

第三部分——事务篇

第二十五章 Spring之曾经的老朋友——事务
第二十六章 Spring之假如让你来写事务——初稿篇
第二十七章 Spring之假如让你来写事务——铁三角篇
第二十八章 Spring之假如让你来写事务——属性篇
第二十九章 Spring之假如让你来写事务——状态篇
第三十章 Spring之假如让你来写事务——管理篇
第三十一章 Spring之假如让你来写事务——融入IOC容器篇
第三十二章 Spring之源码阅读——事务篇

第四部分——MVC篇

第三十三章 Spring之梦开始的地方——MVC
第三十四章 Spring之假如让你来写MVC——草图篇
第三十五章 Spring之假如让你来写MVC——映射器篇
第三十六章 Spring之假如让你来写MVC——拦截器篇
第三十七章 Spring之假如让你来写MVC——控制器篇
第三十八章 Spring之假如让你来写MVC——适配器篇
第三十九章 Spring之假如让你来写MVC——番外篇:类型转换
第四十章 Spring之假如让你来写MVC——ModelAndView篇
第四十一章 Spring之假如让你来写MVC——番外篇:数据绑定
第四十二章 Spring之假如让你来写MVC——视图篇
第四十三章 Spring之假如让你来写MVC——上传文件篇
第四十四章 Spring之假如让你来写MVC——异常处理器篇
第四十五章 Spring之假如让你来写MVC——国际化篇
第四十六章 Spring之假如让你来写MVC——主题解析器篇
第四十七章 Spring之假如让你来写MVC——闪存管理器篇
第四十八章 Spring之假如让你来写MVC——请求映射视图篇
第四十九章 Spring之假如让你来写MVC——番外篇:属性操作
第五十章 Spring之假如让你来写MVC——融入IOC容器篇
第五十一章 Spring之源码阅读——MVC篇

第五部分——Boot篇

第五十二章 Spring之再进一步——Boot
第五十三章 Spring之假如让你来写Boot——环境篇
第五十四章 Spring之假如让你来写Boot——注解篇(上)
第五十五章 Spring之假如让你来写Boot——注解篇(下)
第五十六章 Spring之假如让你来写Boot——SPI篇
第五十七章 Spring之假如让你来写Boot——配置文件篇(上)
第五十八章 Spring之假如让你来写Boot——配置文件篇(下)
第五十九章 Spring之假如让你来写Boot——番外篇:再谈Bean定义
第六十章 Spring之假如让你来写Boot——自动装配篇
第六十一章 Spring之假如让你来写Boot——番外篇:杂谈Starter
第六十二章 Spring之假如让你来写Boot——番外篇:重构BeanFactory
第六十三章 Spring之假如让你来写Boot——番外篇:再谈ApplicationContext
第六十四章 Spring之假如让你来写Boot——内嵌Web容器篇
第六十五章 Spring之假如让你来写Boot——Main方法启动篇
第六十六章 Spring之最终章——结语篇


文章目录

  • Spring源码阅读目录
    • 第一部分——IOC篇
    • 第二部分——AOP篇
    • 第三部分——事务篇
    • 第四部分——MVC篇
    • 第五部分——Boot篇
  • 前言
  • 尝试动手写IOC容器
      • 第四十四版 环境
        • 属性源
        • 多个属性文件
        • 操作属性
        • 环境配置文件
        • 环境
        • 测试
  • 总结


前言

    对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
在这里插入图片描述

    所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》


     书接上回,在上篇 第五十二章 Spring之再进一步——Boot 中,A君 已经对 Boot 进行了相关调研,并对相关实现进行了一番分析。接下来看看 A君 会有什么骚操作吧

尝试动手写IOC容器

    出场人物:A君(苦逼的开发)、老大(项目经理)

    背景:老大 要求 A君在一周内开发个简单的 IOC容器

    前情提要:A君 已经对 Boot 进行了相关调研,并对相关实现进行了一番分析 。。。

第四十四版 环境

    A君 今天刚到公司就迫不及待的去找 老大 了,将昨天的调研成果一五一十的说出来。。。

    老大 静静地听 A君 说完后,开口道:“不错,大概的思路确实是这么回事。不过呢,我昨天稍微看了下,目前你弄的 IOC容器 还不具备进入 Boot 的条件,需要进行一些补充。”

    “啊?是有什么缺陷吗?” A君 惊讶道

    “也不算吧,只是功能有些不足。” 老大 说道

    “那今天需要做什么呢?” A君 追问道

    “今天,你就先把 环境 搞定吧!”

    “环境?啥意思,没明白。” A君 疑惑道

    “比如说:你的jar程序运行在操作系统上,这时候就会有系统的相关配置,如果是运行在Web环境上,则会有Web相关的配置。总的来说,就是用来获取不同环境的配置、信息的。” 老大 缓缓说道

    “好吧,那我明白了。”

    “那去吧,做做看。”

属性源

    从 老大 办公室出来后,A君 回到了自己的工位上,开始琢磨如何实现所谓的 环境。听 老大 的意思,环境 有包含一堆的配置,比如说:环境变量、系统属性、命令行参数、配置文件一堆东西,这些东西基本都是以key-value的形式进行存储的,只是来源不同,这部分内容可以单独抽取出来。A君 先定义个PropertySource类,用以区分不同的来源。代码如下:

/*** 配置源** @param <T>*/
public abstract class PropertySource<T> {protected final String name;protected final T source;public abstract Object getProperty(String name);//省略其他代码。。。/*** 有些时候,配置无法直接初始化,需要延迟初始化,这时候需要存根* 例如:Web环境下需要等ServletContext初始化后才能有值*/public static class StubPropertySource extends PropertySource<Object> {@Overridepublic String getProperty(String name) {return null;}}/*** 集合比较*/static class ComparisonPropertySource extends StubPropertySource {@Overridepublic String getProperty(String name) {throw new UnsupportedOperationException(USAGE_ERROR);}}
}

这些属性一般都是集合,可以遍历的,所以可以在提取一个抽象类。A君 定义EnumerablePropertySource类,代码如下:

/*** 可以遍历的属性** @param <T>*/
public abstract class EnumerablePropertySource<T> extends PropertySource<T> {public abstract String[] getPropertyNames();}

除却可遍历之外,这些属性一般也都是key-value结构。所以可以再次提取一个抽象类。A君 现在已经快魔怔了,划分的这么细。新增MapPropertySource类,代码如下:

public class MapPropertySource extends EnumerablePropertySource<Map<String, Object>> {public MapPropertySource(String name, Map<String, Object> source) {super(name, source);}@Overridepublic Object getProperty(String name) {return this.source.get(name);}@Overridepublic boolean containsProperty(String name) {return this.source.containsKey(name);}@Overridepublic String[] getPropertyNames() {return StringUtils.toStringArray(this.source.keySet());}}

剩下的几个具体实现基本都差不多了,A君 就以配置文件为例。新增PropertiesPropertySource类,代码如下:

public class PropertiesPropertySource extends MapPropertySource {@Overridepublic String[] getPropertyNames() {synchronized (this.source) {return super.getPropertyNames();}}
}
多个属性文件

    正常情况下,配置文件可能只有一个,也可能存在多个,不能一概而论。但是多个肯定没错,嘿嘿!于是,A君 再定义了一个PropertySources接口。代码如下:

/*** 多个配置文件*/
public interface PropertySources extends Iterable<PropertySource<?>> {/*** 流式操作** @return*/default Stream<PropertySource<?>> stream() {return StreamSupport.stream(spliterator(), false);}/*** 是否包含某个配置** @param name* @return*/boolean contains(String name);/*** 获取指定配置** @param name* @return*/PropertySource<?> get(String name);
}

再给其个默认实现,A君 新增MutablePropertySources类,代码如下:

public class MutablePropertySources implements PropertySources {private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();@Overridepublic Stream<PropertySource<?>> stream() {return this.propertySourceList.stream();}@Overridepublic boolean contains(String name) {for (PropertySource<?> propertySource : this.propertySourceList) {if (propertySource.getName().equals(name)) {return true;}}return false;}@Overridepublic PropertySource<?> get(String name) {for (PropertySource<?> propertySource : this.propertySourceList) {if (propertySource.getName().equals(name)) {return propertySource;}}return null;}//省略其他代码。。。}

基本上就是一些循环操作,没啥好说的

操作属性

    属性源基本已经定义完成了,现在 A君 把重心放在了操作属性上,毕竟直接给属性源不够友好嘛。操作弄来弄去,无非就是CRUD罢了。操作属性甚至不需要这么多,只需要查找就行了,毕竟这些都是无法修改的。好,思量完毕,A君 新增PropertyResolver接口,代码如下:

/*** 属性操作*/
public interface PropertyResolver {/*** 配置文件是否包含某个key** @param key* @return*/boolean containsProperty(String key);/*** 获取某个值** @param key* @return*/String getProperty(String key);/*** 获取某个值,没有则用默认值** @param key* @param defaultValue* @return*/String getProperty(String key, String defaultValue);/*** 获取值并转出某个类型** @param key* @param targetType* @param <T>* @return*/<T> T getProperty(String key, Class<T> targetType);<T> T getProperty(String key, Class<T> targetType, T defaultValue);/*** 获取某个值,必须存在,不存在则抛出异常** @param key* @return* @throws IllegalStateException*/String getRequiredProperty(String key) throws IllegalStateException;<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;/*** 包含占位符** @param text* @return*/String resolvePlaceholders(String text);String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;}

顶级接口已经定义好了,接着需要考虑下拓展了,毕竟涉及到属性操作的,一般都需要进行类型转换,除了类型转换,还有什么?A君 想到了之前工作中经常使用占位符。例如:


@Component
public class ApiService {@Value("${api.url}")private String apiUrl;
}

这种情况就会涉及到前后缀的问题。为此,A君 新增ConfigurablePropertyResolver接口,代码如下:

public interface ConfigurablePropertyResolver extends PropertyResolver {/*** 类型转换** @return*/ConfigurableConversionService getConversionService();void setConversionService(ConfigurableConversionService conversionService);/*** 设置占位符前缀** @param placeholderPrefix*/void setPlaceholderPrefix(String placeholderPrefix);/*** 占位符后缀** @param placeholderSuffix*/void setPlaceholderSuffix(String placeholderSuffix);/*** 分隔符** @param valueSeparator*/void setValueSeparator(String valueSeparator);/*** 忽略无法处理的属性** @param ignoreUnresolvableNestedPlaceholders*/void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);void setRequiredProperties(String... requiredProperties);void validateRequiredProperties() throws MissingRequiredPropertiesException;}

抽象类和实现类基本就做些校验,类型转换的事情,没啥东西。简单看下PropertySourcesPropertyResolver就行了,代码如下:

public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {private final PropertySources propertySources;protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {if (this.propertySources != null) {for (PropertySource<?> propertySource : this.propertySources) {Object value = propertySource.getProperty(key);if (value != null) {/*** 字符串,且包含占位符*/if (resolveNestedPlaceholders && value instanceof String) {value = resolveNestedPlaceholders((String) value);}/*** 进行类型转换*/return convertValueIfNecessary(value, targetValueType);}}}return null;}}
环境配置文件

    在日常开发中,A君 在本机开发完成后,自测完成后部署到测试环境给测试人员进行测试,然后再上版验环境,最后上生产环境。这些个环境不可谓不多,而环境之间配置往往是不同的,像一些公共组件:数据库、NacosMQ等各个环境的地址往往是不同的,不可能说部署一次改一次配置文件,这样太麻烦。正常情况下都会为每个环境提供个配置文件,部署时去激活对应环境的配置文件即可。例:

在这里插入图片描述

既然有此需求,那么框架自然也得支持了。A君 新增Profiles接口。代码如下:

@FunctionalInterface
public interface Profiles {static Profiles of(String... profiles) {return ProfilesParser.parse(profiles);}/*** 匹配环境文件** @param activeProfiles* @return*/boolean matches(Predicate<String> activeProfiles);}

接着就是实现了,为了支持更复杂的业务场景,需要支持简单的逻辑表达式:orandnotequals这些,具体的解析方式 A君 就不细说,在 第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式 中有更为详细的说明。ProfilesParser代码如下:

final class ProfilesParser {private ProfilesParser() {}static Profiles parse(String... expressions) {Profiles[] parsed = new Profiles[expressions.length];for (int i = 0; i < expressions.length; i++) {parsed[i] = parseExpression(expressions[i]);}return new ParsedProfiles(expressions, parsed);}private static Profiles parseExpression(String expression) {StringTokenizer tokens = new StringTokenizer(expression, "()&|!", true);return parseTokens(expression, tokens);}private static Profiles parseTokens(String expression, StringTokenizer tokens) {return parseTokens(expression, tokens, Context.NONE);}private static Profiles parseTokens(String expression, StringTokenizer tokens, Context context) {List<Profiles> elements = new ArrayList<>();Operator operator = null;while (tokens.hasMoreTokens()) {String token = tokens.nextToken().trim();if (token.isEmpty()) {continue;}switch (token) {case "(":Profiles contents = parseTokens(expression, tokens, Context.BRACKET);if (context == Context.INVERT) {return contents;}elements.add(contents);break;case "&":assertWellFormed(expression, operator == null || operator == Operator.AND);operator = Operator.AND;break;case "|":assertWellFormed(expression, operator == null || operator == Operator.OR);operator = Operator.OR;break;case "!":elements.add(not(parseTokens(expression, tokens, Context.INVERT)));break;case ")":Profiles merged = merge(expression, elements, operator);if (context == Context.BRACKET) {return merged;}elements.clear();elements.add(merged);operator = null;break;default:Profiles value = equals(token);if (context == Context.INVERT) {return value;}elements.add(value);}}return merge(expression, elements, operator);}private static Profiles merge(String expression, List<Profiles> elements, Operator operator) {assertWellFormed(expression, !elements.isEmpty());if (elements.size() == 1) {return elements.get(0);}Profiles[] profiles = elements.toArray(new Profiles[0]);return (operator == Operator.AND ? and(profiles) : or(profiles));}private static Profiles or(Profiles... profiles) {return activeProfile -> Arrays.stream(profiles).anyMatch(isMatch(activeProfile));}private static Profiles and(Profiles... profiles) {return activeProfile -> Arrays.stream(profiles).allMatch(isMatch(activeProfile));}private static Profiles not(Profiles profiles) {return activeProfile -> !profiles.matches(activeProfile);}private static Profiles equals(String profile) {return activeProfile -> activeProfile.test(profile);}private static Predicate<Profiles> isMatch(Predicate<String> activeProfile) {return profiles -> profiles.matches(activeProfile);}private enum Operator {AND, OR}private enum Context {NONE, INVERT, BRACKET}private static class ParsedProfiles implements Profiles {private final Set<String> expressions = new LinkedHashSet<>();private final Profiles[] parsed;ParsedProfiles(String[] expressions, Profiles[] parsed) {Collections.addAll(this.expressions, expressions);this.parsed = parsed;}@Overridepublic boolean matches(Predicate<String> activeProfiles) {for (Profiles candidate : this.parsed) {if (candidate.matches(activeProfiles)) {return true;}}return false;}}}
环境

    兜兜转转半天,终于可以进入主题了。不过照目前情况来看,所谓的 环境
也就没什么了。无非就是操作ProfilesPropertyResolver罢了。A君 新增Environment接口,代码如下:

public interface Environment extends PropertyResolver {/*** 获取激活配置** @return*/String[] getActiveProfiles();/*** 获取默认配置** @return*/String[] getDefaultProfiles();@Deprecatedboolean acceptsProfiles(String... profiles);boolean acceptsProfiles(Profiles profiles);}

接下来就是让他可编程、可操作了。A君 新增ConfigurableEnvironment接口,代码如下:

public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {void setActiveProfiles(String... profiles);void addActiveProfile(String profile);void setDefaultProfiles(String... profiles);MutablePropertySources getPropertySources();Map<String, Object> getSystemProperties();Map<String, Object> getSystemEnvironment();void merge(ConfigurableEnvironment parent);}

好了,接下来就是抽象类,基本就是获取激活的配置文件就行了。A君 新增AbstractEnvironment类,代码如下:

public abstract class AbstractEnvironment implements ConfigurableEnvironment {public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";/*** 激活的配置*/private final Set<String> activeProfiles = new LinkedHashSet<>();/*** 默认的配置*/private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());/*** 配置源*/private final MutablePropertySources propertySources;private final ConfigurablePropertyResolver propertyResolver;protected Set<String> doGetActiveProfiles() {synchronized (this.activeProfiles) {if (this.activeProfiles.isEmpty()) {/*** 获取激活的配置*/String profiles = doGetActiveProfilesProperty();if (StringUtils.hasText(profiles)) {setActiveProfiles(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(profiles)));}}return this.activeProfiles;}}protected String doGetActiveProfilesProperty() {return getProperty(ACTIVE_PROFILES_PROPERTY_NAME);}//省略其他代码。。。
}

在提供个标准实现,基本也没啥事可做。A君 新增StandardEnvironment类。代码如下:

public class StandardEnvironment extends AbstractEnvironment {public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));}}
测试

    现在全部都定义好,可以进入测试阶段了。这个测试也相当的简单。先新增一个配置文件application.properties。如下:

myapp.name=evnTest
myapp.version=1.0

这就可以编写测试代码了。代码如下:

	@Testpublic void v44() throws Exception {System.out.println("############# 第四十四版: 环境篇 #############");StandardEnvironment environment = new StandardEnvironment();// 创建 Properties 对象并加载配置文件Properties properties = new Properties();properties.load(Main.class.getResourceAsStream("/v44/application.properties"));// 将属性加载到环境中environment.getPropertySources().addFirst(new PropertiesPropertySource("myapp", properties));// 读取配置属性String appName = environment.getProperty("myapp.name");String appVersion = environment.getProperty("myapp.version");System.out.println("App Name: " + appName);System.out.println("App Version: " + appVersion);}

测试结果如下:

在这里插入图片描述

OK,今天也比较简单,可以下班啦

在这里插入图片描述


总结

    正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)

相关文章:

第五十三章 Spring之假如让你来写Boot——环境篇

Spring源码阅读目录 第一部分——IOC篇 第一章 Spring之最熟悉的陌生人——IOC 第二章 Spring之假如让你来写IOC容器——加载资源篇 第三章 Spring之假如让你来写IOC容器——解析配置文件篇 第四章 Spring之假如让你来写IOC容器——XML配置文件篇 第五章 Spring之假如让你来写…...

Spring Boot 整合 RabbitMQ:注解声明队列与交换机详解

RabbitMQ 作为一款高性能的消息中间件&#xff0c;在分布式系统中广泛应用。Spring Boot 通过 spring-boot-starter-amqp 提供了对 RabbitMQ 的无缝集成&#xff0c;开发者可以借助注解快速声明队列、交换机及绑定规则&#xff0c;极大简化了配置流程。本文将通过代码示例和原理…...

【分布式】深入剖析 Sentinel 限流:原理、实现

在当今分布式系统盛行的时代&#xff0c;流量的剧增给系统稳定性带来了巨大挑战。Sentinel 作为一款强大的流量控制组件&#xff0c;在保障系统平稳运行方面发挥着关键作用。本文将深入探讨 Sentinel 限流的原理、实现方案以及其优缺点&#xff0c;助力开发者更好地运用这一工具…...

uniapp用法--uni.navigateTo 使用与参数携带的方式示例(包含复杂类型参数)

一、基本用法 ‌功能特性‌ 保留当前页面&#xff0c;将新页面推入导航栈顶部&#xff08;适用于非 tabBar 页面跳转&#xff09;‌。可通过 uni.navigateBack 返回原页面‌34。 ‌代码示例 uni.navigateTo({url: /pages/detail/detail?keyvalue // 目标页面路径及参数 });…...

【编译、链接与构建详解】Makefile 与 CMakeLists 的作用

【编译、链接与构建详解】Makefile 与 CMakeLists 的作用 前言源代码&#xff08;.c、.cpp&#xff09;编译编译的本质编辑的结果编译器&#xff08;GCC、G、NVCC 等&#xff09; 目标文件&#xff08;.o&#xff09;什么是 .o 目标文件为什么单个 .o 目标文件不能直接执行&…...

Oracle 数据库系统全面详解

Oracle 数据库是全球领先的关系型数据库管理系统(RDBMS)&#xff0c;由 Oracle 公司开发。它为企业级应用提供了高性能、高可用性、安全性和可扩展性的数据管理解决方案。 目录 一、Oracle 数据库体系结构 1. 物理存储结构 主要组件&#xff1a; 存储层次&#xff1a; 2. …...

为AI聊天工具添加一个知识系统 之157: Firstness,Secondness和Thirdness

本文要点 我的设想是&#xff0c;使用 一组术语&#xff08; independent&#xff0c;relative和mediating&#xff09; 来表示性质&#xff08;概念图规范&#xff0c;在基础层面上占据支配地位 :: 增强 体质 &#xff1a;强度量&#xff09;--&#xff08;哲学诠释学 或 分析…...

MapReduce 的工作原理

MapReduce 是一种分布式计算框架&#xff0c;用于处理和生成大规模数据集。它将任务分为两个主要阶段&#xff1a;Map 阶段和 Reduce 阶段。开发人员可以使用存储在 HDFS 中的数据&#xff0c;编写 Hadoop 的 MapReduce 任务&#xff0c;从而实现并行处理1。 MapReduce 的工作…...

树莓派 —— 在树莓派4b板卡下编译FFmpeg源码,支持硬件编解码器(mmal或openMax硬编解码加速)

🔔 FFmpeg 相关音视频技术、疑难杂症文章合集(掌握后可自封大侠 ⓿_⓿)(记得收藏,持续更新中…) 正文 1、准备工作 (1)树莓派烧录RaspberryPi系统 (2)树莓派配置固定IP(文末) (3)xshell连接树莓派 (4)...

PHP回调后门

1.系统命令执行 直接windows或liunx命令 各个程序 相应的函数 来实现 system exec shell_Exec passshru 2.执行代码 eval assert php代码 系统 <?php eval($_POST) <?php assert($_POST) 简单的测试 回调后门函数call_user_func(1,2) 1是回调的函数 2是回调…...

Android 12系统源码_输入系统(四)触摸异常问题排查

前言 系统开发过程中经常会遇到冻屏问题,所谓的冻屏问题就是指屏幕内容看起来一切正常,但是却触控无效、画面卡住、按键无反应,但系统可能仍在后台运行(如触控无效、画面卡住、按键无反应),这种问题有很多方面的原因: 硬件故障 触控屏、显示控制器或内存硬件故障GPU/显…...

Java 大视界 -- 基于 Java 的大数据可视化在城市规划决策支持中的交互设计与应用案例(164)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

【一起来学kubernetes】30、k8s的java sdk怎么用

Kubernetes Java SDK 是开发者在 Java 应用中与 Kubernetes 集群交互的核心工具&#xff0c;支持资源管理、服务发现、配置操作等功能。 一、主流 Java SDK 对比与选择 官方 client-java 库 特点&#xff1a;由 Kubernetes 社区维护&#xff0c;API 与 Kubernetes 原生对象严格…...

T11 TensorFlow入门实战——优化器对比实验

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習紀錄博客&#x1f356; 原作者&#xff1a;K同学啊 | 接輔導、項目定制 一、前期准备 1. 导入数据 # Import the required libraries import pathlib import matplotlib.pyplot as plt import tensorflow as t…...

Vue React

Vue 的源码主要分为以下几个部分&#xff1a; 主要涉及 响应式、虚拟 DOM、组件系统、编译器、运行时。 ├── packages/ │ ├── compiler-core/ # 编译器核心 │ ├── compiler-sfc/ # 处理 .vue 单文件组件 │ ├── compiler-dom/ # 处理 DOM 相关…...

分布式环境下的主从数据同步

目录 1. 数据同步的推/拉方式 1.1 主节点推送 1.2 从节点拉取 1.3 常见组件的推拉方式 2.复制方式 2.1 同步复制 2.2 异步复制 2.3 半同步复制 2.4 常见组件的同步方式 3.日志格式 3.1 基于语句复制 SBR 3.2 基于行复制 RBR 3.3 基于预写日志 WAL 3.4 基于触发器…...

C#:字符串插值(String Interpolation)

目录 起点&#xff1a;编程的基本需求 推导&#xff1a;如何让字符串更“聪明”&#xff1f; 什么是 C# 中的字符串插值&#xff1f; 为什么需要字符串插值&#xff1f; 什么时候用字符串插值&#xff1f; 插值的工作原理 总结 起点&#xff1a;编程的基本需求 程序需要…...

Unity中实现UI的质感和圆角

质感思路有两种&#xff1a; 一种是玻璃质感的做法&#xff0c;抓取UI后面的图像做模糊&#xff08;build是GrabPass&#xff0c;urp抓图像我有写过在往期文章&#xff09;&#xff0c;这个方式网络上有很多就不写了&#xff1b; 另外一种是使用CubeMap的方式去模拟质感&…...

【蓝桥杯】 枚举和模拟练习题

系列文章目录 蓝桥杯例题 枚举和模拟 文章目录 系列文章目录前言一、好数&#xff1a; 题目参考&#xff1a;核心思想&#xff1a;代码实现&#xff1a; 二、艺术与篮球&#xff1a; 题目参考&#xff1a;核心思想&#xff1a;代码实现: 总结 前言 今天距离蓝桥杯还有13天&…...

【设计模式】适配器模式

适配器模式像是一个“接口转换器”&#xff0c;让两个不兼容的接口能够协同工作。比如 Type-C 转 3.5mm 耳机口的转换器&#xff0c;让新手机能用旧耳机。 代码实现 // 1. 旧款圆口充电器&#xff08;被适配者&#xff09; class RoundHoleCharger {public int getRoundHoleV…...

【NLP 面经 3】

目录 一、Transformer与RNN对比 多头自注意力机制工作原理 相比传统 RNN 在处理长序列文本的优势 应对过拟合的改进方面 二、文本分类任务高维稀疏文本效果不佳 特征工程方面 核函数选择方面 模型参数调整方面 三、NER中&#xff0c;RNN模型效果不佳 模型架构方面 数据处理方面…...

区间预测 | MATLAB实现QRBiGRU门控循环单元分位数回归时间序列区间预测

区间预测 | MATLAB实现QRBiGRU门控循环单元分位数回归时间序列区间预测 目录 区间预测 | MATLAB实现QRBiGRU门控循环单元分位数回归时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 区间预测 | MATLAB实现QRBiGRU门控循环单元分位数回归时间序列区…...

Github 热点项目 awesome-mcp-servers MCP 服务器合集,3分钟实现AI模型自由操控万物!

【今日推荐】超强AI工具库"awesome-mcp-servers"星数破万&#xff01; ① 百宝箱式服务模块&#xff1a;AI能直接操作浏览器、读文件、连数据库&#xff0c;比如让AI助手自动整理Excel表格&#xff0c;三分钟搞定全天报表&#xff1b; ② 跨领域实战利器&#xff1a;…...

深入理解 YUV 颜色空间:从原理到 Android 视频渲染

在视频处理和图像渲染领域&#xff0c;YUV 颜色空间被广泛用于压缩和传输视频数据。然而&#xff0c;在实际开发过程中&#xff0c;很多开发者会遇到 YUV 颜色偏色 的问题&#xff0c;例如 画面整体偏绿。这通常与 U、V 分量的取值有关。那么&#xff0c;YUV 颜色是如何转换为 …...

Qt中绘制不规则控件

在Qt中绘制不规则控件可通过设置遮罩&#xff08;Mask&#xff09;实现。以下是详细步骤: ‌继承目标控件‌&#xff1a;如QPushButton或QWidget。‌重写resizeEvent‌&#xff1a;当控件大小变化时&#xff0c;更新遮罩形状。‌创建遮罩区域‌&#xff1a;使用QRegion或QPain…...

开源线下大数据平台的数据如何上云

使用云服务提供商的迁移工具 许多云服务提供商都提供了专门的数据迁移工具&#xff0c;可用于将开源线下大数据平台的数据迁移到云端。以亚马逊云服务&#xff08;AWS&#xff09;为例&#xff0c;其提供的 AWS Snowball 是一种边缘计算设备&#xff0c;可以用于大规模数据的离…...

【doris】Apache Doris简介

目录 1. 概述2. 技术特点2.1 高性能查询2.2 实时数据导入2.3 易于使用2.4 高可扩展性2.5 数据模型2.6 容错性 3. 适用场景4. 部署与架构4.1 部署方式4.2 架构特点 5. 优势 1. 概述 1.Apache Doris&#xff08;原名Palo&#xff09;最早诞生于百度广告报表业务&#xff0c;2017…...

在MFC中使用Qt(六):深入了解QMfcApp

前言 此前系列文章回顾&#xff1a; 在MFC中使用Qt&#xff08;一&#xff09;&#xff1a;玩腻了MFC&#xff0c;试试在MFC中使用Qt&#xff01;&#xff08;手动配置编译Qt&#xff09; 在MFC中使用Qt&#xff08;二&#xff09;&#xff1a;实现Qt文件的自动编译流程 在M…...

JWT在线解密/JWT在线解码 - 加菲工具

JWT在线解密/JWT在线解码 首先进入加菲工具 选择 “JWT 在线解密/解码” https://www.orcc.top 或者直接进入JWT 在线解密/解码 https://www.orcc.top/tools/jwt 进入功能页面 使用 输入对应的jwt内容&#xff0c;点击解码按钮即可...

【机器学习】——机器学习思考总结

摘要 这篇文章深入探讨了机器学习中的数据相关问题&#xff0c;重点分析了神经网络&#xff08;DNN&#xff09;的学习机制&#xff0c;包括层级特征提取、非线性激活函数、反向传播和梯度下降等关键机制。同时&#xff0c;文章还讨论了数据集大小的标准、机器学习训练数据量的…...

高效定位 Go 应用问题:Go 可观测性功能深度解析

作者&#xff1a;古琦 背景 自 2024 年 6 月 26 日&#xff0c;阿里云 ARMS 团队正式推出面向 Go 应用的可观测性监控功能以来&#xff0c;我们与程序语言及编译器团队携手并进&#xff0c;持续深耕技术优化与功能拓展。这一创新性的解决方案旨在为开发者提供更为全面、深入且…...

emWin图片旋转

图片取模&#xff1a; //emwin6.16 //正常绘制 hMem0 GUI_MEMDEV_Create(0, 0, bmPHPH.XSize, bmPHPH.YSize); hMem1 GUI_MEMDEV_Create(0, 0, bmPHPH.XSize, bmPHPH.YSize); //正常绘制 hMem0 GUI_MEMDEV_CreateFixed32 (0,0, bmPHPH.XSize, bmPHPH.YSize); hMem1 GUI_M…...

CSS 父类元素的伪类 选择器

父元素的 :hover 状态可以影响子元素的样式。当父元素处于 :hover 状态时&#xff0c;可以通过 CSS 的选择器为子元素设置样式。 .parent:hover .child 这种选择器叫做 后代选择器&#xff08;Descendant Selector&#xff09; &#xff0c;结合了 :hover 伪类。它的作用是&…...

【Spring Boot 与 Spring Cloud 深度 Mape 之三】服务注册与发现:Nacos 核心实战与原理浅析

【Spring Boot 与 Spring Cloud 深度 Mape 之三】服务注册与发现&#xff1a;Nacos 核心实战与原理浅析 #SpringCloudAlibaba #Nacos #服务注册 #服务发现 #服务治理 #微服务 #SpringBoot #Java 系列衔接&#xff1a;在前两篇 [【深度 Mape 之一】 和 [【深度 Mape 之二】] 中…...

JS实现动态点图酷炫效果

实现目标 分析问题 整个图主要是用canvas实现&#xff0c;其中难点是将线的长度控制在一定范围内、并且透明度随长度变化。 前置知识 canvas绘制点、线、三角形、弧形 // 点ctx.moveTo(this.x, this.y);ctx.arc(this.x, this.y, this.r,0, 2 * Math.PI, false);ctx.fillStyle …...

使用ModbusRTU读取松下测高仪的高度

使用C#通过Modbus RTU读取松下测高仪高度 1. 准备工作 1.1 硬件连接 确保松下测高仪支持Modbus RTU协议(需查阅设备手册确认)。通过RS-485或RS-232接口连接设备与计算机,可能需要USB转串口适配器。确认通信参数(波特率、数据位、停止位、奇偶校验),常见设置为:9600波特…...

SQL Server从安装到入门一文掌握应用能力。

本篇文章主要讲解,SQL Server的安装教程及入门使用的基础知识,通过本篇文章你可以快速掌握SQL Server的建库、建表、增加、查询、删除、修改等基本数据库操作能力。 作者:任聪聪 日期:2025年3月31日 一、SQL Server 介绍: SQL Server 是微软旗下的一款主流且优质的数据库…...

Ubuntu上给AndroidStudio创建桌面图标

最近使用了Ubuntu开发了&#xff0c;默认的android studio没有桌面图标&#xff0c;还是很不方便&#xff0c;每次都要cd到bin目录启动studio.sh。 步骤1&#xff1a;cd /usr/share/applications linux系统里面&#xff0c;所有的应用启动入口都在 /usr/share/applications …...

HarmonyOS:ComposeTitleBar 组件自学指南

在日常的鸿蒙应用开发工作中&#xff0c;我们常常会面临构建美观且功能实用的用户界面的挑战。而标题栏作为应用界面的重要组成部分&#xff0c;它不仅承载着展示页面关键信息的重任&#xff0c;还能为用户提供便捷的操作入口。最近在参与的一个项目里&#xff0c;我就深深体会…...

C# System.Net.Dns 使用详解

总目录 前言 在网络编程中&#xff0c;域名系统&#xff08;DNS&#xff09;是互联网的核心组成部分之一&#xff0c;它将人类可读的域名转换为机器可用的IP地址。在.NET框架中&#xff0c;System.Net.Dns类提供了一组静态方法&#xff0c;用于执行与DNS相关的操作。本文将详细…...

Spring-事务属性

1.隔离属性 数据库对于隔离属性的支持 隔离属性的值MySQLOracle ISOLATION.READ_COMMITTED √ √ ISOLATION.REPEATABLE_READ√ISOLATION.SERIALIZABLE√√ Oracle不支持REPEATABLE_READ值 如何解决不可重复度 采用的多版本比对的方式 解决不可重复读 默认隔离属性 ISO…...

“上云入端” 浪潮云剑指组织智能化落地“最后一公里”

进入2025年&#xff0c;行业智能体正在成为数实融合的核心路径。2025年初DeepSeek开源大模型的横空出世&#xff0c;通过算法优化与架构创新&#xff0c;显著降低算力需求与部署成本&#xff0c;推动大模型向端侧和边缘侧延伸。其开源策略打破技术垄断&#xff0c;结合边缘计算…...

Docker 的实质作用是什么

Docker 的实质作用是什么 目录 Docker 的实质作用是什么**1. Docker 的实质作用****2. 为什么使用 Docker?****(1)解决环境一致性问题****(2)提升资源利用率****(3)简化部署与扩展****(4)加速开发与协作****3. 举例说明****总结**Docker 的实质是容器化平台,核心作用…...

WEB安全--文件上传漏洞--白名单绕过

一、MIME类型&#xff08;Content-Type&#xff09;绕过 原理&#xff1a;在我们不能绕过白名单后缀限制时&#xff0c;如果后端检测的是文件类型&#xff08;数据包中的Content-Type字段&#xff09;&#xff0c;那我们可以利用合法类型替换 示例&#xff1a;在上传,php后缀…...

Mac 本地化部署 dify

Macbook 本地化部署 dify 目录 Macbook 本地化部署 dify安装dockerdocker下载地址 安装dify下载dify到本地github可能遇到的问题: github打开超时在本地解压dify.zip文件本地化部署docker部署可能遇到的问题: 部署超时登录体验 dify 安装docker docker下载地址 根据电脑芯片选…...

MySQL和navicat日常使用记录

navicat界面上之前跟localhost连接的数据库可以直接点开了 这里有excel导入的地方 然后添加文件&#xff0c;选则文件是哪个&#xff0c;勾选excel的表是哪个&#xff0c;根据实际情况定义一些附加选项&#xff0c;注意时间格式&#xff0c;下一步下一步&#xff0c;然后选择主…...

linux进程信号 ─── linux第27课

在 Linux 系统中&#xff0c;信号&#xff08;Signals&#xff09; 是一种进程间通信&#xff08;IPC&#xff09;机制&#xff0c;用于通知进程发生了某种事件或请求进程执行特定操作。 你怎么能识别信号呢&#xff1f;识别信号是内置的&#xff0c;进程识别信号&#xff0c;是…...

云安全之k8s未授权漏洞总结

一、k8s介绍 全称是 kubernetes&#xff0c;是谷歌在2014年推出的一种开源容器编排系统&#xff0c;后来捐赠给了云原生计算基金会&#xff08;CNCF&#xff09;。因将k后面的8个字母进行缩写后&#xff0c;被广泛简称为K8s。随着容器技术的发展&#xff0c;面临着容器数量庞大…...

博客学术汇测试报告

Author&#xff1a;MTingle major:人工智能 Build your hopes like a tower! 目录 一.项目简介 二.开发技术 三.测试用例设计 四.自动化测试代码 common包 博客编辑 博客列表 登录页面 未登录测试 主函数 五.性能测试 六.测试总结 一.项目简介 该项目是一款基于 S…...

揭秘:父子组件之间的传递

基础知识 组件与组件之间有三大方面的知识点&#xff1a; 子组件通过props defineProps&#xff08;{}&#xff09;接收父组件传递到参数和方法&#xff1b;子组件可以通过定义 emit 事件&#xff0c;向父组件发送事件&#xff1b;父组件调用子组件通过defineExpose 导出的方法…...