深入认识ClassLoader - 一次投产失败的复盘

news/2025/10/21 20:13:58/文章来源:https://www.cnblogs.com/imadc/p/19156192

问题背景

投产日,同事负责的项目新版本发布,版本包是SpringBoot v2.7.18的一个FatJarjava -jar启动报错停止了,输出的异常日志如下:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [com/alibaba/druid/spring/boot/autoconfigure/DruidDataSourceAutoConfigure.class]: Invocation of init method failed; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class...省略Caused by: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver classat org.springframework.boot.autoconfigure.jdbc.DataSourceProperties.determineDriverClassName(DataSourceProperties.java:186)at org.springframework.boot.autoconfigure.jdbc.DataSourceProperties.determineUsername(DataSourceProperties.java:280)at com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper.afterPropertiesSet(DruidDataSourceWrapper.java:40)...省略

版本回退,正好我也在旁边,记录下一起排查解决的过程。

定位与解决问题

分析错误日志

拉了版本分支代码,从下往上看输出的错误日志,发现是DruidDataSourceWrapper这个类中40行出错,看下这个类以及出错的位置:

@ConfigurationProperties("spring.datasource.druid")
class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean {@Autowiredprivate DataSourceProperties basicProperties;@Overridepublic void afterPropertiesSet() throws Exception {//if not found prefix 'spring.datasource.druid' jdbc properties ,'spring.datasource' prefix jdbc properties will be used.if (super.getUsername() == null) {// 关键行:这一行出错,basicProperties.determineUsername()这个方法会出现异常super.setUsername(basicProperties.determineUsername());}if (super.getPassword() == null) {super.setPassword(basicProperties.determinePassword());}if (super.getUrl() == null) {super.setUrl(basicProperties.determineUrl());}if (super.getDriverClassName() == null) {super.setDriverClassName(basicProperties.getDriverClassName());}}...

DruidDataSourceWrapper归属于druid-spring-boot-starter这个依赖,是 alibaba druid 数据库连接池的一个 starter。

结合错误日志看下basicProperties.determineUsername()这个方法里面出错的位置:

public String determineUsername() {if (StringUtils.hasText(this.username)) {return this.username;}// 关键行:调用determineDriverClassName()这个方法出错if (EmbeddedDatabaseConnection.isEmbedded(determineDriverClassName(), determineUrl())) {return "sa";}return null;
}

再次结合错误日志看下determineDriverClassName()这个方法里面出错的位置:

public String determineDriverClassName() {if (StringUtils.hasText(this.driverClassName)) {Assert.state(driverClassIsLoadable(), () -> "Cannot load driver class: " + this.driverClassName);return this.driverClassName;}String driverClassName = null;if (StringUtils.hasText(this.url)) {driverClassName = DatabaseDriver.fromJdbcUrl(this.url).getDriverClassName();}if (!StringUtils.hasText(driverClassName)) {driverClassName = this.embeddedDatabaseConnection.getDriverClassName();}if (!StringUtils.hasText(driverClassName)) {// 关键行:在这边抛出的异常throw new DataSourceBeanCreationException("Failed to determine a suitable driver class", this,this.embeddedDatabaseConnection);}return driverClassName;
}

定位到了出错的位置,分析这块代码抛出异常的原因,意思就是如果spring.datasource.druid.username这个配置的值为空,那么读取spring.datasource.username这个配置,如果还是空,尝试从spring.datasource.url配置信息中解析jdbc驱动类,解析不出来就抛出DataSourceBeanCreationException异常。

版本变动点

是配置信息有问题?

问了下这个项目的配置原本是放在配置文件中的,公共配置放在了application.yml中,不同环境的配置采用application-{profile}.yml放置,如下:

application.yml
application-dev.yml
...
application-pro.yml

application.yml中使用占位符借助 maven 打包时添加-P参数设置激活的profile

spring:profiles:# envactive: @env@

项目 pom 文件中多个 profile 配置如下(这是本次版本的一个变动点):

<profiles><!-- DEV 开发环境--><profile><id>dev</id><properties><env>DEV</env>...</properties></profile>...<!-- PRO 生产环境--><profile><id>pro</id><properties><env>PRO</env>...</properties></profile>
</profiles>

maven 打生产包,spring.profiles.active的值被设置成了PRO,也就是生产环境将使用application-PRO.yml这个配置文件。

这个版本的另一个变动点是接入了 apollo 配置中心,但是没有删除不同环境的配置文件,配置文件application.yml中增加了 apollo 相关的配置:

app:id: app-xxx-web
apollo:bootstrap:namespaces: applicationenabled: trueeagerLoad:enabled: true
分析 SpringBoot 的配置加载流程
触发时机

SpringBoot 应用启动时在 SpringApplication prepareEnvironment方法中发布ApplicationEnvironmentPreparedEvent事件,EnvironmentPostProcessorApplicationListener 中监听了这个事件触发配置信息读取,不同来源的配置信息有专门实现了EnvironmentPostProcessor接口的类进行处理,这些类实现postProcessEnvironment方法,apollo-client使用的是v1.9.0版本,其包含一个META-INF/spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
org.springframework.boot.env.EnvironmentPostProcessor=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer

com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer 会被扫描到,然后执行其postProcessEnvironment方法,多个EnvironmentPostProcessor的执行顺序由其内部的order属性决定,越小的越靠前,ApolloApplicationContextInitializerorder为0,属于是靠后的:

SpringBoot 中,后加载的属性源可以覆盖先加载的属性源定义的值,参考:属性源的优先级顺序,因此 apollo 中的配置会覆盖配置文件中的配置。

难道是 apollo 中的配置写错了?

看了下 apollo 中没有spring.datasource.url这个配置,数据库的连接信息是写在spring.datasource.druid这个前缀开头下面的,apollo 中有两个名为application的命名空间,一个格式是properties,另一个格式是yml,这些配置是写在yml格式命名空间下的,properties格式命名空间下的配置为空。

spring:# druid pooldatasource:druid:url: jdbc:mysql://...:3306/...?useUnicode=true&characterEncoding=UTF-8&useSSL=false&...username: ...password: ...driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource...

idea 启动参数指定 apollo 配置,启动项目,本地 apollo 的缓存文件夹config-cache下是有配置文件存在的,不过只有一个文件app-xx-web+default+application.properties,里面是空的。

yml格式命名空间下的配置呢?

看了下 apollo 的文档,原来yml格式命名空间下的配置在客户端使用需要填写带后缀的完整名字。

注1:yaml/yml格式的namespace从1.3.0版本开始支持和Spring整合,注入时需要填写带后缀的完整名字,比如application.yml

注2:非properties、非yaml/yml格式(如xml,json等)的namespace暂不支持和Spring整合。

配置文件application.yml中修改apollo的配置,将namespacesapplication修改为application.yml

app:id: app-xxx-web
apollo:bootstrap:namespaces: application.ymlenabled: trueeagerLoad:enabled: true

本地调试启动ok,apollo 中的配置可以正常拉取,项目启动成功。

生产环境 apollo 中的配置没有生效的话,可application-{profile}.yml文件还在,应该还是能读取配置文件中的配置完成启动的吧?

额,不对, maven 打生产包,spring.profiles.active的值被设置成了PRO,但classpath下生产环境配置文件名称为 application-pro.yml,大小写不一致,能正常加载吗?

application.yml配置文件中的app.apollo.bootstrap.namespaces配置还原,在 maven 的 Profiles 中勾选 dev ,spring.profiles.active的值被设置成了DEV,idea 中正常启动项目,说明 application-dev.yml这个配置文件被读取了。

拿生产包在本地java -jar启动,apollo 的配置服务器指定为dev环境,和生产环境报一样的错误:

java -Dapp.id=app-xxx-web -Dapollo.meta=http://10.100.x.x:8072 -jar app-xxx-web.jar

难道是 CICD 打包的问题?

没有加载的配置文件

本地打了一个包,启动也是报一样的错误,奇怪了,idea 里面启动和打成 FatJar 之后启动的行为还不一样。

idea 里面启动,spring.profiles.active 的值是大写的 DEVapplication-dev.yml中的配置是能正常读取的,打成FatJar之后,spring.profiles.active的值是大写的 PROapplication-pro.yml中的配置却不能正常读取。

apollo 的 app.id 这个配置是放在application.yml中的,启动后本地 apollo 的配置缓存文件夹 config-cache 下是有配置的,说明 application.yml 是生效的,只是不同环境 application-{profile}.yml 文件中的配置没有生效。

得着重看看 SpringBoot 中读取配置文件的逻辑了。

配置文件的加载流程

上面分析到,EnvironmentPostProcessorApplicationListener 中监听了ApplicationEnvironmentPreparedEvent事件做配置信息读取动作,不同来源的配置信息有专门实现了EnvironmentPostProcessor接口的类进行处理,配置文件的处理类是哪一个?

debug 看了下,是 ConfigDataEnvironmentPostProcessor,其 postProcessEnvironment 方法里面进行处理,然后调用了ConfigDataEnvironment类中的 processAndApply 方法,其内部会调用processWithProfiles方法:

private ConfigDataEnvironmentContributors processWithProfiles(ConfigDataEnvironmentContributors contributors,ConfigDataImporter importer, ConfigDataActivationContext activationContext) {this.logger.trace("Processing config data environment contributors with profile activation context");// 在这~~~contributors = contributors.withProcessedImports(importer, activationContext);registerBootstrapBinder(contributors, activationContext, ALLOW_INACTIVE_BINDING);return contributors;
}

此时的contributorsConfigDataEnvironmentContributors,继续跟踪 withProcessedImports 方法,里面会调用是ConfigDataImporter的 resolveAndLoad 方法:

/*** Resolve and load the given list of locations, filtering any that have been* previously loaded.* @param activationContext the activation context* @param locationResolverContext the location resolver context* @param loaderContext the loader context* @param locations the locations to resolve* @return a map of the loaded locations and data*/
Map<ConfigDataResolutionResult, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,List<ConfigDataLocation> locations) {try {// 关键行:定位出使用的环境profileProfiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;// 关键行:根据profile列出需要查找的配置文件列表List<ConfigDataResolutionResult> resolved = resolve(locationResolverContext, profiles, locations);return load(loaderContext, resolved);}catch (IOException ex) {throw new IllegalStateException("IO error on loading imports from " + locations, ex);}
}

因为我本地 debug 的时候 profile 指定的 dev,所以spring.profiles.active的值被设置成了DEV

继续断点,跟踪到了StandardConfigDataLocationResolver类,其 getProfileSpecificReferences 方法中根据 profile 列出需要读取的配置文件路径列表:

继续断点到了获取配置文件资源的位置,是 resolveNonPattern 方法:

private List<StandardConfigDataResource> resolveNonPattern(StandardConfigDataReference reference) {// 关键行:通过统一的 ResourceLoader 接口获取资源Resource resource = this.resourceLoader.getResource(reference.getResourceLocation());// 关键行:调用 Resource.exists() 方法,如果文件存在,则继续在后面读取,否则忽略if (!resource.exists() && reference.isSkippable()) {logSkippingResource(reference);return Collections.emptyList();}return Collections.singletonList(createConfigResourceLocation(reference, resource));
}

因为 application-DEV.yml 是放在classpath下,在这加一个条件断点,只关注application-DEV.yml

reference.getResourceLocation().equals("classpath:/application-DEV.yml");

判断文件是否存在这个语句执行的结果是存在的:

可见spring.profiles.active的值被设置成了DEV,本地在 idea 中 debug 项目代码也能正常加载 application-dev.yml ,会不会是打成 jar 包之后就不行呢?

远程调试生产包

idea 支持** Remote JVM Debug** ,我想要观测下生产版本 jar 包启动的时候,spring.profiles.active的值被设置成了PRO,这块代码判断 classpath:/application-PRO.yml 文件是否存在的结果。

在生产 jar 包目录下打开命令行窗口,执行以下命令,其中 suspend 需要设置成 y,代表回车执行命令需要等到 idea 连接到这个 5005 调试端口之后才继续执行程序:

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -Dapollo.meta=http://10.100.x.xx:8072 -jar app-xxx-web.jar --logging.level.root=TRACE

因为是生产包,所以需要改下条件断点为:

reference.getResourceLocation().equals("classpath:/application-PRO.yml");

Remote JVM Debug 启动后 , 判断 classpath:/application-PRO.yml 文件是否存在的结果为false,和 debug 项目代码不一样了:

改成 classpath:/application-pro.yml 呢?在断点处执行以下命令,判断结果为 true了:

解决方案

分析到这,问题点和解决方案已经出来了:

  1. 项目 pom 文件中 profile 设置的 env 参数值本该小写但是用了大写
  2. 全面使用 apollo,去掉不同环境的配置文件,修正 apollo 命名空间配置为: apollo.bootstrap.namespaces = application.yml

可为什么测试环境没有出现这个问题呢,原来测试环境的启动脚本中指定了spring.profiles.active的值且是小写,生产环境启动脚本却没有指定。

🙂

深入认识 ClassLoader

不一样的 ClassLoader

版本能正常投产了,但是同一份代码不同的启动方式却有不同的表现,这着实让我费解,想着空余花点时间来弄明白这其中的原理。

resolveNonPattern 这个方法里面调用了resource.exists()方法判断配置文件是否存在,resource 是 spring-core 提供的 org.springframework.core.io.ClassPathResource 类:

/*** {@link Resource} implementation for class path resources. Uses either a* given {@link ClassLoader} or a given {@link Class} for loading resources.** <p>Supports resolution as {@code java.io.File} if the class path* resource resides in the file system, but not for resources in a JAR.* Always supports resolution as {@code java.net.URL}.** ...*/
public class ClassPathResource extends AbstractFileResolvingResource {private final String path;@Nullableprivate ClassLoader classLoader;@Nullableprivate Class<?> clazz;/*** This implementation checks for the resolution of a resource URL.* @see ClassLoader#getResource(String)* @see Class#getResource(String)*/@Overridepublic boolean exists() {return (resolveURL() != null);}/*** Resolves a URL for the underlying class path resource.* @return the resolved URL, or {@code null} if not resolvable*/@Nullableprotected URL resolveURL() {try {if (this.clazz != null) {return this.clazz.getResource(this.path);}else if (this.classLoader != null) {// 关键行:委托给具体的 ClassLoaderreturn this.classLoader.getResource(this.path);}else {return ClassLoader.getSystemResource(this.path);}}catch (IllegalArgumentException ex) {// Should not happen according to the JDK's contract:// see https://github.com/openjdk/jdk/pull/2662return null;}}...
}

2.7.18 版本的 SpringBoot 使用的 spring-core 版本为 5.3.31,exists() 方法会调用本类中的 resolveURL() 方法,debug 看了下,不管是 idea 启动还是打成 jar 之后启动,resolveURL 方法中都是通过return this.classLoader.getResource(this.path)返回配置文件的 URL 的,区别在于:

  • idea 中启动时,classLoaderLauncher$AppClassLoaderexists方法中return (resolveURL() != null)的返回值为 true
  • 打成 jar 之后启动,classLoaderLaunchedURLClassLoaderexists方法中return (resolveURL() != null)的返回值为 false
Launcher$AppClassLoader 与 LaunchedURLClassLoader 的差异
类加载器的架构差异

Launcher$AppClassLoader 是 JDK 标准的三层类加载器架构中的系统类加载器:

// JDK 类加载器层次结构
Bootstrap ClassLoader (C++实现,加载JRE核心类)↓
Extension ClassLoader (加载JRE扩展包)↓
AppClassLoader (系统类加载器,加载-classpath指定路径)

在 idea 环境中运行时,应用类路径由 idea 动态构建,通常包含:

项目编译输出目录(如target/classes
所有依赖的 JAR 文件
idea 特定的资源目录

此时的资源查找基于文件系统,ClassLoader.getResource()方法会遍历类路径中的每个条目,在文件系统上直接查找对应的资源文件。

LaunchedURLClassLoader 是 SpringBoot 为 FatJar 设计的特殊类加载器:

// SpringBoot FatJar 类加载架构
LaunchedURLClassLoader↓
URLClassLoader↓
ClassLoader (父类加载器)

其特殊之处在于能够处理"嵌套的JAR"(nested JARs)——即 FatJar 中内嵌的其他 JAR 文件。

资源解析机制的对比

AppClassLoader 的资源解析流程:

// 简化版的资源查找逻辑
public URL getResource(String name) {URL url;// 首先委托父加载器查找if (parent != null) {url = parent.getResource(name);} else {url = getBootstrapResource(name);}if (url == null) {// 在自身的类路径中查找url = findResource(name);}return url;
}
// 在文件系统中的查找
URL findResource(String name) {for (File classpathEntry : classpath) {File resourceFile = new File(classpathEntry, name);if (resourceFile.exists()) {return resourceFile.toURI().toURL();}}return null;
}

关键特性:

基于文件系统路径直接查找
受操作系统文件系统大小写规则影响(Windows 不敏感,Linux 敏感)
支持通配符和模式匹配

LaunchedURLClassLoader 的资源解析流程:

// SpringBoot 自定义的资源查找
public URL findResource(String name) {// 1. 首先尝试从已索引的资源中查找URL url = findResourceFromIndex(name);if (url != null) {return url; }// 2. 在嵌套的JAR文件中查找for (JarFile jar : nestedJars) {JarEntry entry = jar.getJarEntry(name);if (entry != null) {try {// 创建特殊的URL指向JAR内的资源return createJarUrl(jar, entry);} catch (IOException e) {// 处理异常}}}// 3. 回退到标准的URLClassLoader查找return super.findResource(name);
}

关键特性:

基于 JAR 文件条目的精确匹配
严格的大小写敏感性(ZIP/JAR实现的实际要求)
需要预先构建资源索引以提高性能

FatJar 中的资源定位机制

SpringBoot FatJar 的特殊结构:

app.jar
├── META-INF/
├── BOOT-INF/
│   ├── classes/          # 应用类文件
│   │   └── application.yml
│   └── lib/              # 依赖库
│       ├── spring-core.jar
│       └── druid.jar
└── org/springframework/boot/loader/├── Launcher.class└── LaunchedURLClassLoader.class

资源解析的核心挑战:

  1. JAR 规范的限制
    • JAR 文件本质上是 ZIP 文件
    • 严格的大小写敏感性(ZIP/JAR实现的实际要求),application-PRO.ymlapplication-pro.yml

SpringBoot 的优化策略

// SpringBoot 在构建时创建资源索引
private Map<String, List<String>> createResourceIndex() {Map<String, List<String>> index = new HashMap<>();for (JarFile jar : getAllJars()) {Enumeration<JarEntry> entries = jar.entries();while (entries.hasMoreElements()) {JarEntry entry = entries.nextElement();if (!entry.isDirectory()) {String path = entry.getName();// 将路径转换为标准形式String normalized = normalizePath(path);index.computeIfAbsent(normalized, k -> new ArrayList<>()).add(path);}}}return index;
}
  1. 类加载器的初始化差异
    • idea 环境:类路径包含具体的目录和文件
    • FatJar 环境:类路径指向 JAR 文件内部的嵌套结构
操作系统的影响

开发环境(Windows):

// 文件系统级别的大小写处理
File file = new File("application-PRO.yml");
System.out.println(file.exists()); 
// Windows: 如果存在application-pro.yml,可能返回true(不敏感)
// Linux: 严格返回false(敏感)

生产环境(Linux):

// JAR文件内部的资源查找
JarFile jar = new JarFile("app.jar");
JarEntry entry1 = jar.getJarEntry("application-PRO.yml"); // null
JarEntry entry2 = jar.getJarEntry("application-pro.yml"); // 找到条目
SpringBoot 配置加载的完整链条

理解整个配置加载过程中 ClassLoader 的作用:

// 配置解析的完整调用链
ConfigDataEnvironmentPostProcessor.postProcessEnvironment()→ ConfigDataEnvironment.processAndApply()→ ConfigDataImporter.resolveAndLoad()→ StandardConfigDataLocationResolver.resolve()→ ClassPathResource.exists()→ LaunchedURLClassLoader.getResource()→ JarFile.getJarEntry() // 严格大小写匹配

关键发现:

  • 在 FatJar 中,资源查找最终委托给java.util.jar.JarFile
  • JarFile.getJarEntry()方法基于哈希表实现,要求精确的键匹配
  • 哈希键的计算基于原始字节,不进行大小写转换
问题复现的技术根源

通过源码分析,可以精确重现问题:

// 问题重现的伪代码
public class ProblemReproduction {public static void main(String[] args) {// 开发环境(IDE)ClassLoader devLoader = Launcher.AppClassLoader;URL devResource = devLoader.getResource("application-PRO.yml");System.out.println("DEV Found: " + (devResource != null)); // true// 生产环境(FatJar)ClassLoader prodLoader = new LaunchedURLClassLoader();URL prodResource = prodLoader.getResource("application-PRO.yml");System.out.println("PROD Found: " + (prodResource != null)); // false// 实际存在的文件URL actualResource = prodLoader.getResource("application-pro.yml");System.out.println("Actual Found: " + (actualResource != null)); // true}
}
设计启示与最佳实践

架构层面的启示:

  1. 环境一致性:开发、测试、生产环境的运行时行为应该尽可能一致
  2. 早期验证:在构建阶段就应该检测配置文件和类路径的一致性
  3. 防御性编程:对资源加载进行适当的容错处理

技术实践建议:

// 防御性的资源配置加载
public class SafeConfigLoader {public static Resource loadConfig(ClassLoader loader, String baseName,  String profile) {// 尝试规范化的命名String[] possibleNames = {baseName + "-" + profile.toLowerCase() + ".yml",baseName + "-" + profile.toUpperCase() + ".yml",baseName + "-" + profile + ".yml"};for (String name : possibleNames) {Resource resource = loader.getResource(name);if (resource != null && resource.exists()) {return resource;}}return null;}
}

构建期检查:

<!-- Maven 构建期资源验证 -->
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-enforcer-plugin</artifactId><configuration><rules><requireFilesExist><files><!-- 验证配置文件命名一致性 --><file>src/main/resources/application-${env}.yml</file></files></requireFilesExist></rules></configuration>
</plugin>

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

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

相关文章

python 包来源镜像

python 镜像python安装包,默认地址非常的慢,可改用国内相关镜像‌清华大学开源软件镜像站‌ 地址:https://pypi.tuna.tsinghua.edu.cn/simple‌阿里云开源镜像站‌ 地址:https://mirrors.aliyun.com/pypi/simple/‌…

CSharp基础复习-1

基本语法 usiing 关键字 using 关键字用于在程序中包含命名空间。一个程序可以包含多个 using 语句 class关键字 class 关键字用于声明一个类。 注释 单行注释 多行注释 成员变量 变量是类的属性或数据成员,用于存储…

软件工程第三次作业-结对作业

软件工程第三次作业——结对作业结对作业 实现一个自动生成小学四则运算题目的命令行程序 (也可以用图像界面,具有相似功能)这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScienc…

AI代码生成技术解析与应用实践

本文详细介绍基于机器学习的代码生成技术,重点分析自然语言转编程代码的工作原理、多语言支持能力、安全过滤机制及实时推荐特性,探讨AI如何提升开发效率并改变编程范式。AWS CodeWhisperer从自然语言创建计算机代码…

米理 课程描述/学习计划/Study program

其实没官方的模板,只是有个类似的东西 https://www.polimi.it/fileadmin/user_upload/futuri_studenti/ammissione-laurea-magistrale/Sample2.pdf

2025年线路调压器厂家推荐榜:10kv线路调压器/单相线路调压器/三相线路调压器/助力电网稳定运行,优选品牌指南

随着电力系统升级、新能源接入规模扩大及电网稳定性要求提升,线路调压器作为关键配电设备,已从传统电力行业扩展至工业、新能源、农村电网等多个领域。2025年,市场需求预计持续增长,但厂商技术实力、产品适配性及服…

2025 智能/商超照明/灯具/灯光/源头厂家推荐榜:上海富明阳凭分区域光效领跑,生鲜 / 百货场景适配优选

在商超竞争聚焦 “商品展示力” 的 2025 年,商超照明已从基础照明升级为 “提升商品吸引力、优化购物体验” 的核心工具。但行业普遍存在光效不均、场景适配差、能耗过高的痛点,优质服务商能精准破解难题。结合光效指…

2025 变电站厂家推荐榜最新资讯:撬装变电站/移动车载变电站/预制舱式变电站/移动变电站/预装式变电站/聚焦智能适配与可靠服务,这家企业成优选​

随着新型电力系统加速建设,新能源并网、电网改造及应急供电等需求持续攀升,变电站作为电力传输核心枢纽,其模块化、智能化与环境适配能力成为选型关键。2025 年全球预制舱箱式变电站市场规模已达 1966 百万美元,市…

银河麒麟Kylin申威SW64系统安装 rpcbind-1.2.5-2.p01.ky10.sw_64.rpm 方法

银河麒麟Kylin申威SW64系统安装 rpcbind-1.2.5-2.p01.ky10.sw_64.rpm 方法​ 一、准备工作​确认系统架构是申威(SW)的​ 一般这个包是专门为申威平台的银河麒麟操作系统(比如 KY10)准备的,你下载的包名里已经有 …

helloworld的输出

helloworld的输出public class hello {public static void main(String[] args){System.out.print("helloword");} }hello类名和文件名hello.java一样 cmd编译Java文件 1、cmd当前Java文件目录 2、javac hel…

2025 艺考文化课推荐榜:济南震华学校 5 星领跑,全阶段体系适配基础补弱到高分冲刺

随着艺考竞争加剧,艺考生对 “文化课精准补弱、高效提分、适配专业课时间” 的需求愈发迫切,专业艺考文化课培训需兼顾 “针对性与系统性”。结合课程完整性、提分效果、师资专业性与用户反馈,2025 年艺考文化课推荐…

2025 广州人力资源/派遣/劳务外包/人事代理/推荐榜:精典人才凭派遣合规 + 全场景适配领跑,企业用工优选

在广州企业用工需求日趋多元化的 2025 年,人力资源与人力资源派遣服务成为企业灵活配置人力、降低用工风险的核心选择。但行业中存在合规性不足、岗位适配差、售后支持弱等痛点,优质服务商可有效规避用工隐患。结合合…

png隐写文件与文件占用

png隐写文件正确解封装 1.ffmpeg自动推测。 2.得到推测格式为png_pipe,尝试使用mpegts格式进行解封装,打开成功并且媒体流大于0则认为成功。 3.使用mpegts上下文替换png上下文。 ps:部分vob文件需以mpeg格式打开。 文件…

Windows和Linux设置Https(SSL)访问 - 详解

Windows和Linux设置Https(SSL)访问 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "…

题解:P12525 [Aboi Round 1] 私は雨

link 顺带一提这是我第一道没看题解做出来的黑(也是第四道黑)。 写完看了一圈题解,我想说: 欸不是凭啥我不用卡常啊? 前言 这篇题解的复杂度是这样的: 小 \(p\) \(O(q \sqrt n \log \sqrt n + n \sqrt V)\),大 …

完整教程:罗技G102有线鼠标自己维修教程

完整教程:罗技G102有线鼠标自己维修教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "M…

杂谈

代码如下,将这一段代码优化了下 string id = AAA(msg->msg_sender_);if (id.empty()){BBB(msg);VX_INFO_FUN_OUT();return;}#ifdef PROJECT_A//理论上不会到这里,因为id.empty会会处理,这里做一个保护if(U::i…

挖矿-学校挖矿排查

挖矿-学校挖矿排查 1、使用工具分析共有多少IP存在扫描web特征,提交其数量 这里我们直接访问百度网盘将流量下载到本地然后直接导入到 ZUI里面,这个工具很方便对流量进行筛选流量分析工具ZUI安装然后使用命令搜索 co…

读书日记2

四五章深入探讨了软件构建的关键前期工作,让我认识到优秀代码的质量在很大程度上是由设计阶段决定的。 核心收获与深刻见解: 1.设计的层次性思维:McConnell详细阐述了从系统架构到类设计,再到子程序设计的完整层次…

定位问题3:明明堆栈已经打印出来了,偏就是定位不出来?

堆栈如下,明明是在AAA::B函数里core了,而且是概率core的,可就是定位不出来 B函数很简单 1. 第一句打印入参 2. 其它都是函数调用 3. 打印退出消息 对于1 ,参数如下,不可能为nullptr,也排查了调用处(仅2处),不存…