1 概述

日志这里采用logback,其为springboot默认的日志工具。其整体已经被springboot封装得比较好了,扔个配置文件到classpath里就能够使用。

但在实际使用中,日志配置文件有可能需要进行改动,比如日志的打印级别,平时可能定的是WARN或者ERROR级别,如果出点问题,可能希望临时能够调整为INFO或者DEBUG,方便产生更加丰富的日志以定位问题。如果配置文件是放到classpath里,也就会被打包到jar包里,修改配置文件就需要重新打包、部署、启动等,很可能做不到只修改配置文件并生效。要想改变配置文件的位置,就有必要了解一下这个配置文件是如何加载的。

2 原理

2.1 logback是如何被依赖的

前面看对spring-boot-starter的依赖的时候,有个不起眼的依赖:spring-boot-starter-logging

https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter/2.7.18/spring-boot-starter-2.7.18.pom

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot</artifactId><version>2.7.18</version><scope>compile</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId><version>2.7.18</version><scope>compile</scope></dependency><!--  省略其它依赖 -->
<dependencies>

 查看spring-boot-starter-logging的依赖:https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-logging/2.7.18/spring-boot-starter-logging-2.7.18.pom

<dependencies><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.12</version><scope>compile</scope></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-to-slf4j</artifactId><version>2.17.2</version><scope>compile</scope></dependency><dependency><groupId>org.slf4j</groupId><artifactId>jul-to-slf4j</artifactId><version>1.7.36</version><scope>compile</scope></dependency>
</dependencies>

 logback-classic包提供了logback打印日志功能。

2.2 查找logback.xml配置文件

2.2.1 触发查找的Listener

spring-boot-2.7.18.jar包里提供了META-INF/spring.factories配置文件,里面配置了LoggingApplicationListener:

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

2.2.2 查找过程

Springboot提供了一个LoggingApplicationListener,其继实现了GenericApplicationListener接口,该接口最终继承了ApplicationListener。

按Springboot的规则,实现了ApplicationListener接口的都会被Springboot统一调用。这个类就是响应springboot的准备环境对象事件来初始化日志对象LogbackLoggingSystem的:

// 源码位置:org.springframework.boot.context.logging.LoggingApplicationListener
public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationStartingEvent) {onApplicationStartingEvent((ApplicationStartingEvent) event);}else if (event instanceof ApplicationEnvironmentPreparedEvent) {// 1. 在PreparedEvent的时候加载日志配置文件onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);}else if (event instanceof ApplicationPreparedEvent) {onApplicationPreparedEvent((ApplicationPreparedEvent) event);}else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {onContextClosedEvent();}else if (event instanceof ApplicationFailedEvent) {onApplicationFailedEvent();}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {SpringApplication springApplication = event.getSpringApplication();if (this.loggingSystem == null) {this.loggingSystem = LoggingSystem.get(springApplication.getClassLoader());}// 2. 调用日志初始化接口initialize(event.getEnvironment(), springApplication.getClassLoader());
}
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {getLoggingSystemProperties(environment).apply();// 3. 获取环境变量里配置的日志文件,在环境变量里配置了才有this.logFile = LogFile.get(environment);if (this.logFile != null) {this.logFile.applyToSystemProperties();}this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);initializeEarlyLoggingLevel(environment);initializeSystem(environment, this.loggingSystem, this.logFile);initializeFinalLoggingLevels(environment, this.loggingSystem);registerShutdownHookIfNecessary(environment, this.loggingSystem);
}// 源码位置:org.springframework.boot.logging.LogFile
public static LogFile get(PropertyResolver propertyResolver) {// 4. 如果配置了logging.file.name、logging.file.path环境变量,则它们组成一个log文件的路径,用此路径初始化一个日志文件对象//    FILE_NAME_PROPERTY = "logging.file.name"//    FILE_PATH_PROPERTY = "logging.file.path"String file = propertyResolver.getProperty(FILE_NAME_PROPERTY);String path = propertyResolver.getProperty(FILE_PATH_PROPERTY);if (StringUtils.hasLength(file) || StringUtils.hasLength(path)) {return new LogFile(file, path);}return null;
}// 回到LoggingApplicationListener继续处理环境变量里配置的日志文件
// 源码位置:org.springframework.boot.context.logging.LoggingApplicationListener
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {getLoggingSystemProperties(environment).apply();// 3. 获取环境变量里配置的日志文件,在环境变量里配置了才有this.logFile = LogFile.get(environment);if (this.logFile != null) {// 5. 把环境变量里的配置日志文件路径和文件名设置到系统属性里this.logFile.applyToSystemProperties();}this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);initializeEarlyLoggingLevel(environment);initializeSystem(environment, this.loggingSystem, this.logFile);initializeFinalLoggingLevels(environment, this.loggingSystem);registerShutdownHookIfNecessary(environment, this.loggingSystem);
}// 源码位置:org.springframework.boot.logging.LogFile
public void applyToSystemProperties() {// 6. 提供系统属性为参数applyTo(System.getProperties());
}
public void applyTo(Properties properties) {// 7. 把配置文件的路径和文件名设置到系统属性里,可以在logback.xml里作为变量引用,如${LOG_PATH}//    LoggingSystemProperties.LOG_PATH = "LOG_PATH"//    LoggingSystemProperties.LOG_FILE = "LOG_FILE"put(properties, LoggingSystemProperties.LOG_PATH, this.path);put(properties, LoggingSystemProperties.LOG_FILE, toString());
}
private void put(Properties properties, String key, String value) {if (StringUtils.hasLength(value)) {properties.put(key, value);}
}// 回到LoggingApplicationListener继续处理环境变量里配置的日志文件
// 源码位置:org.springframework.boot.context.logging.LoggingApplicationListener
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {getLoggingSystemProperties(environment).apply();// 3. 获取环境变量里配置的日志文件,在环境变量里配置了才有this.logFile = LogFile.get(environment);if (this.logFile != null) {// 5. 把环境变量里的配置日志文件路径和文件名设置到系统属性里this.logFile.applyToSystemProperties();}this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);initializeEarlyLoggingLevel(environment);// 8. 初始化LogbackLoggingSystem对象initializeSystem(environment, this.loggingSystem, this.logFile);initializeFinalLoggingLevels(environment, this.loggingSystem);registerShutdownHookIfNecessary(environment, this.loggingSystem);
}
private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) {// 9. 读取logging.config配置(在命令行配置),CONFIG_PROPERTY = "logging.config"String logConfig = StringUtils.trimWhitespace(environment.getProperty(CONFIG_PROPERTY));try {LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);// 10. 调用LogbackLoggingSystem对象的初始化方法,//     如果配置了logging.config,则把配置的文件作为logConfig参数传入,否则logConfig参数为nullif (ignoreLogConfig(logConfig)) {system.initialize(initializationContext, null, logFile);}else {system.initialize(initializationContext, logConfig, logFile);}}// 省略其它代码
}// 源码位置:org.springframework.boot.logging.logback.LogbackLoggingSystem
// 注意:configLocation有null和非null两种情况
public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {LoggerContext loggerContext = getLoggerContext();if (isAlreadyInitialized(loggerContext)) {return;}// 11. 调用父类的初始化方法,父类为AbstractLoggingSystemsuper.initialize(initializationContext, configLocation, logFile);loggerContext.getTurboFilterList().remove(FILTER);markAsInitialized(loggerContext);if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY+ "' system property. Please use 'logging.config' instead.");}
}// 源码位置:org.springframework.boot.logging.AbstractLoggingSystem
public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {// 12. 如果logging.config配置的值不为空,加载配置指定的日志配置文件if (StringUtils.hasLength(configLocation)) {initializeWithSpecificConfig(initializationContext, configLocation, logFile);return;}// 13. 当没有配置logging.config时,则加载默认的配置文件,这里重点关注默认的initializeWithConventions(initializationContext, logFile);
}
private void initializeWithSpecificConfig(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {// 配置了logging.config的时候,主要是先把里面可能出现${}占位符替换为实际值,// 然后得到一个正常的日志配置文件路径,按正常流程处理,参考下面对loadConfiguration()的说明configLocation = SystemPropertyUtils.resolvePlaceholders(configLocation);loadConfiguration(initializationContext, configLocation, logFile);
}
private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {// 14. 获取可能的默认的日志配置文件路径String config = getSelfInitializationConfig();if (config != null && logFile == null) {reinitialize(initializationContext);return;}if (config == null) {config = getSpringInitializationConfig();}if (config != null) {loadConfiguration(initializationContext, config, logFile);return;}loadDefaults(initializationContext, logFile);
}
protected String getSelfInitializationConfig() {// 15. getStandardConfigLocations()获取默认的日志配置文件return findConfig(getStandardConfigLocations());
}// 源码位置:org.springframework.boot.logging.logback.LogbackLoggingSystem
protected String[] getStandardConfigLocations() {// 16. 默认支持四种配置文件名称,注意其顺序,带test的在前面,logback.xml是最后一种//     如果开发环境了放logback-test.xml和logback.xml,生产环境只放logback.xml,//     则可以开发环境用的是带test的,不影响生产文件的修改,会比较便利return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
}// 回到AbstractLoggingSystem的getSelfInitializationConfig(),调用findConfig()
// 源码位置:org.springframework.boot.logging.AbstractLoggingSystem
protected String getSelfInitializationConfig() {// 17. 调findConfig()查找配置文件的路径return findConfig(getStandardConfigLocations());
}
private String findConfig(String[] locations) {// 18. 遍历每个可能的配置文件名,调用Spring提供的ClassPathResource,从classpath中检查文件是否存在,即上面指定的4中文件需要放到classpath中//     如果存在,则在文件名的前面加上classpath:路径前缀,注意这里体现顺序,只要找到第一个就返回for (String location : locations) {ClassPathResource resource = new ClassPathResource(location, this.classLoader);if (resource.exists()) {return "classpath:" + location;}}return null;
}// 回到AbstractLoggingSystem的initializeWithConventions(),继续处理获取到的日志文件路径
// 源码位置:org.springframework.boot.logging.AbstractLoggingSystem
private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {// 14. 获取可能的默认的日志配置文件路径String config = getSelfInitializationConfig();if (config != null && logFile == null) {// 15. 重新初始化,这里并没有传入找到的文件,因为里面还会再找一次reinitialize(initializationContext);return;}if (config == null) {config = getSpringInitializationConfig();}if (config != null) {loadConfiguration(initializationContext, config, logFile);return;}loadDefaults(initializationContext, logFile);
}// 源码位置:org.springframework.boot.logging.logback.LogbackLoggingSystem
protected void reinitialize(LoggingInitializationContext initializationContext) {getLoggerContext().reset();getLoggerContext().getStatusManager().clear();// 16. 加载配置文件,这里重新调了getSelfInitializationConfig(),从classpath找配置文件路径,参考上面步骤17loadConfiguration(initializationContext, getSelfInitializationConfig(), null);
}
protected void loadConfiguration(LoggingInitializationContext initializationContext, String location, LogFile logFile) {// 如果logFile有值,则调父类的方法加载配置文件,没有用到location,这里先忽略// logFile是前面从环境变量里获取的日志配置文件路径和文件名super.loadConfiguration(initializationContext, location, logFile);LoggerContext loggerContext = getLoggerContext();stopAndReset(loggerContext);try {// 17. 用url的方式加载文件,ResourceUtils.getURL(location)把以classpath前缀的路径转为文件绝对路径,比如classpath:logback.xmlconfigureByResourceUrl(initializationContext, loggerContext, ResourceUtils.getURL(location));}catch (Exception ex) {throw new IllegalStateException("Could not initialize Logback logging from " + location, ex);}reportConfigurationErrorsIfNecessary(loggerContext);
}// 源码位置:org.springframework.boot.logging.logback.LogbackLoggingSystem
private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext, URL url) throws JoranException {if (XML_ENABLED && url.toString().endsWith("xml")) {JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);configurator.setContext(loggerContext);// 18. SpringBootJoranConfigurator继承了logback-classic包的类GenericConfigurator,从而转到logback-classic包进行日志文件处理了//     GenericConfigurator提供了doConfigure()方法实际加载配置文件,由logback包完成日志对象的初始化configurator.doConfigure(url);}else {new ContextInitializer(loggerContext).configureByResource(url);}
}// 上面看的是如果配置了日志文件(如logback.xml的情况),回到AbstractLoggingSystem.initialize()看看没有配置日志文件的情况
// 源码位置:org.springframework.boot.logging.AbstractLoggingSystem
private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {// 14. 获取可能的默认的日志配置文件路径String config = getSelfInitializationConfig();if (config != null && logFile == null) {reinitialize(initializationContext);return;}// 19. 如果没有配置日志文件,可找spring提供的日志配置文件if (config == null) {config = getSpringInitializationConfig();}if (config != null) {loadConfiguration(initializationContext, config, logFile);return;}loadDefaults(initializationContext, logFile);
}// 源码位置:org.springframework.boot.logging.AbstractLoggingSystem
protected String getSpringInitializationConfig() {// 20. 找配置文件return findConfig(getSpringConfigLocations());
}
protected String[] getSpringConfigLocations() {// 21. 先找标准文件,参考步骤16可得logback-test.groovy、logback-test.xml、logback.groovy、logback.xmlString[] locations = getStandardConfigLocations();// 22. 给每个文件名加上-spring后缀,作为新的文件名,如logback.xml转为logback-spring.xmlfor (int i = 0; i < locations.length; i++) {String extension = StringUtils.getFilenameExtension(locations[i]);locations[i] = locations[i].substring(0, locations[i].length() - extension.length() - 1) + "-spring." + extension;}return locations;
}// 回到AbstractLoggingSystem.initialize()继续加载带-spring后缀的日志配置文件
// 源码位置:org.springframework.boot.logging.AbstractLoggingSystem
private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {// 14. 获取可能的默认的日志配置文件路径String config = getSelfInitializationConfig();if (config != null && logFile == null) {reinitialize(initializationContext);return;}// 19. 如果没有配置日志文件,可找spring提供的日志配置文件if (config == null) {config = getSpringInitializationConfig();}// 23. 加载日志配置文件,方式同步骤17if (config != null) {loadConfiguration(initializationContext, config, logFile);return;}// 24. 如果spring的配置文件也没有,则加载默认的,保证一定可以打印日志loadDefaults(initializationContext, logFile);
}// 默认的日志是把日志格式等硬编码在代码里的,一般也不使用,大概参考一下即可
// 源码位置:org.springframework.boot.logging.logback.DefaultLogbackConfiguration
private void defaults(LogbackConfigurator config) {config.conversionRule("clr", ColorConverter.class);config.conversionRule("wex", WhitespaceThrowableProxyConverter.class);config.conversionRule("wEx", ExtendedWhitespaceThrowableProxyConverter.class);config.getContext().putProperty("CONSOLE_LOG_PATTERN", resolve(config, "${CONSOLE_LOG_PATTERN:-"+ "%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) "+ "%clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} "+ "%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"));String defaultCharset = Charset.defaultCharset().name();config.getContext().putProperty("CONSOLE_LOG_CHARSET", resolve(config, "${CONSOLE_LOG_CHARSET:-" + defaultCharset + "}"));config.getContext().putProperty("FILE_LOG_PATTERN", resolve(config, "${FILE_LOG_PATTERN:-"+ "%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] "+ "%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"));config.getContext().putProperty("FILE_LOG_CHARSET", resolve(config, "${FILE_LOG_CHARSET:-" + defaultCharset + "}"));config.logger("org.apache.catalina.startup.DigesterFactory", Level.ERROR);config.logger("org.apache.catalina.util.LifecycleBase", Level.ERROR);config.logger("org.apache.coyote.http11.Http11NioProtocol", Level.WARN);config.logger("org.apache.sshd.common.util.SecurityUtils", Level.WARN);config.logger("org.apache.tomcat.util.net.NioSelectorPool", Level.WARN);config.logger("org.eclipse.jetty.util.component.AbstractLifeCycle", Level.ERROR);config.logger("org.hibernate.validator.internal.util.Version", Level.WARN);config.logger("org.springframework.boot.actuate.endpoint.jmx", Level.WARN);
}

2.2.3 小结

SpringBoot加载日志配置文件的核心过程:
  • 如果在启动参数里通过-Dlogging.config指定了日志配置文件,则直接加载此日志配置文件;这种方法指定的配置文件,可以使用${}占位符引用系统属性或者系统环境变量。
  • 如果没有手工指定,则从classpath目录下按顺序加载四种日志配置文件(logback-test.groovy、logback-test.xml、logback.groovy、logback.xml),只要加载到一个就返回。
  • 如果还是没有找到日志配置文件,则加上-spring后缀再尝试按顺序加载logback-test-spring.groovy、logback-test-spring.xml、logback-spring.groovy、logback-spring.xml,只要加载到一个就返回。
  • 如何还没有加载到日志配置文件,则加载默认的,默认的日志配置是硬编码到代码里的。

3 使用

了解原理之后,可以来考虑一下,如何使用。Springboot提供的日志配置文件名有4种:logback-test.groovy、logback-test.xml、logback.groovy、logback.xml,一般情况已经够用了,看看如何用好就可以。

3.1 不需要修改配置文件的场景

如果日志配置文件放到服务器环境上就不需要改动了,那么最简单的方式就是直接放到classpath中,在idea里就是放到resources目录下,打包时则是放到classes目录下打包。按上面原理就可以直接加载使用。

3.2 需要修改配置文件的场景

有下面几种场景需要更改日志配置文件:
  • 在生产环境中,一般日志级别只会开到WARN甚至ERROR级别,如果想看INFO甚至DEBUG级别的日志就有可能看不到,在定位棘手问题时可能需要更详细的日志信息。此时如果想修改一下日志级别,那么就希望配置文件能够改动一下。
  • 日志配置的一些参数配置不理想,想调整一下。比如日志文件过大或者过小,不利于日志文件维护。
  • 增加一些场景的日志打印,比如原来没有加spring相关的日志控制,比较影响问题定位,希望加上spring相关日志只有ERROR才打印的控制等。
如果是在生产环境中,若因为日志配置文件的改动,就需要重新打包并重启,那代价有点大。所以偏向不把配置文件放到jar中,此时可以在启动参数指定-Dlogging.config=logback.xml,则会加载jar外同级目录的配置文件;如果配置文件在别的目录,也可以指定绝对路径。

3.3 测试和生产分开的场景

如果配置文件是打包到jar里的,那么生产环境使用的配置文件和平时开发测试用的配置文件有可能是不一样的。如果只维护一个文件,那么很容易把测试的配置带到了生产环境中,这是不允许的。按照上面的加载文件顺序的原理,带test的是放前面的,比如logback-test.xml是在logback.xml前面的,那就可以同时维护这两个文件,开发测试的时候使用带test的,打包到生产环境的时候只打包不带test的配置文件,由于打包工具是工具化的,就不容易出现漏掉的情况。

4 架构一小步

增加日志配置文件:
  • 开发测试环境,在代码src/main/resources目录下放一个带test的配置文件(如logback-test.xml),springboot优先加载带test是日志配置文件;一般也放一个不带test的配置文件,作为代码版本管理的一部分。
  • 在部署环境的时候,不把logback.xml文件打包到jar中,而是放到和jar包同级的config/logback.xml中,使用启动参数-Dlogging.config手工指定配置文件。

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

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

相关文章

一文讲清楚React Hooks

文章目录一文讲清楚React Hooks一、什么是 React Hooks&#xff1f;二、常用基础 Hooks2.1 useState&#xff1a;状态管理基本用法特点2.2 useEffect&#xff1a;副作用处理基本用法依赖数组说明2.3 useContext&#xff1a;上下文共享基本用法特点三、其他常用 Hooks3.1 useRed…

Apache http 强制 https

1. 修改一下文件配置 sudo nano /etc/apache2/sites-enabled/000-default.conf<VirtualHost *:80>ServerName hongweizhu.comServerAlias www.hongweizhu.comServerAdmin webmasterlocalhostDocumentRoot /var/www/html# 强制重定向到HTTPSRewriteEngine OnRewriteCond …

【读代码】GLM-4.1V-Thinking:开源多模态推理模型的创新实践

一、基本介绍 1.1 项目背景 GLM-4.1V-Thinking是清华大学KEG实验室推出的新一代开源视觉语言模型,基于GLM-4-9B-0414基础模型构建。该项目通过引入"思维范式"和强化学习课程采样(RLCS)技术,显著提升了模型在复杂任务中的推理能力。其创新点包括: 64k超长上下文…

从代码生成到智能运维的革命性变革

AI大模型重塑软件开发&#xff1a;从代码生成到智能运维的革命性变革 希望对大家有一定的帮助&#xff0c;进行参考 目录AI大模型重塑软件开发&#xff1a;从代码生成到智能运维的革命性变革 希望对大家有一定的帮助&#xff0c;进行参考一、范式转移&#xff1a;软件开发进入&…

豆包编写Java程序小试

今天下载了一本第四版电气工程师手册&#xff0c;非常棒的一本书&#xff0c;在给PDF添加目录的时候&#xff0c;由于目录有将近60页&#xff0c;使用老马开发的PdgCntEditor有点卡顿&#xff0c;不过补充下&#xff0c;老马这个PdgCntEditor还是非常好的。所以我决定用Java编一…

SpringBoot整合腾讯云新一代行为验证码

一 产品介绍 腾讯云官方介绍链接 腾讯云新一代行为验证码&#xff08;Captcha&#xff09;&#xff0c;基于十道安全防护策略&#xff0c;为网页、App、小程序开发者打造立体、全面的人机验证。在保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时&…

SenseGlove新一代外骨骼力反馈手套Rembrand来袭!亚毫米级手部动捕+指尖触觉力采集+5Dof主动力反馈多模态

在远程机器人操作领域&#xff0c;精准的触觉感知与灵活的动作控制始终是核心需求。SenseGlove 新推出的 Rembrandt 力反馈外骨骼数据手套&#xff0c;以先进技术为支撑&#xff0c;为远程操控人形机器人手部提供了无缝解决方案&#xff0c;让操作更精准、更高效。值得一提的是…

Linux 信号机制:操作系统的“紧急电话”系统

想象一下&#xff0c;你正在电脑前专心工作&#xff0c;突然手机响了——这是一个通知&#xff0c;要求你立即处理一件新事情&#xff08;比如接电话&#xff09;。 Linux 系统中的信号&#xff08;Signal&#xff09;​​ 机制&#xff0c;本质上就是操作系统内核或进程之间用…

论文略读:Prefix-Tuning: Optimizing Continuous Prompts for Generation

2021 ACL固定预训练LM&#xff0c;为LM添加可训练&#xff0c;任务特定的前缀这样就可以为不同任务保存不同的前缀这种前缀可以看成连续可微的soft prompt&#xff0c;相比于离散的token&#xff0c;更好优化&#xff0c;效果更好训练的时候只需要更新prefix部分的参数&#xf…

CSS基础选择器、文本属性、引入方式及Chorme调试工具

CSS基础 1.1 CSS简介 CSS 是层叠样式表 ( Cascading Style Sheets ) 的简称. 有时我们也会称之为 CSS 样式表或级联样式表。 CSS 是也是一种标记语言 CSS 主要用于设置 HTML 页面中的文本内容&#xff08;字体、大小、对齐方式等&#xff09;、图片的外形&#xff08;宽高、边…

RabbitMQ 高级特性之事务

1. 简介与 MySQL、Redis 一样&#xff0c;RabbitMQ 也支持事务。事务中的消息&#xff0c;要么全都发送成功&#xff0c;要么全部发送失败&#xff0c;不会出现一部分成功一部分失败的情况。2. 使用事务发送消息spring 中使用 RabbitMQ 开启事务需要两步&#xff1a;第一步&…

iframe 的同源限制与反爬机制的冲突

一、事件背景A域名接入了动态防护&#xff08;Bot 防护、反爬虫机制&#xff09;&#xff0c;同时第三方业务B域名通过内嵌iframe的方式调用了A域名下的一个链接。二、动态防护介绍&#xff1a;动态防护&#xff08;也称为 Bot 防护、反爬虫机制&#xff09;是网站为了防止自动…

Rust 的 Copy 语义:深入浅出指南

在 Rust 中&#xff0c;Copy 是一个关键的特性&#xff0c;它定义了类型的复制行为。理解 Copy 语义对于掌握 Rust 的所有权系统和编写高效代码至关重要。一、核心概念&#xff1a;Copy vs Move特性Copy 类型非 Copy 类型 (Move)赋值行为按位复制 (bitwise copy)所有权转移 (ow…

Qt的信号与槽(二)

Qt的信号与槽&#xff08;二&#xff09;1.自定义槽2.通过图形化界面来生成自定义槽3.自定义信号3.信号和槽带参数4.参数数量5.connect函数的设计&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x1f680;&#x1f680;系列专栏&#xf…

Java研学-MongoDB(三)

三 文档相关 7 文档统计查询① 语法&#xff1a; // 精确统计文档数 慢 准 dahuang> db.xiaohuang.countDocuments({条件}) 4 // 粗略统计文档数 快 大致准 dahuang> db.xiaohuang.estimatedDocumentCount({条件}) 4② 例子&#xff1a; // 精确统计文档数 name为奔波儿灞…

TCP协议格式与连接释放

TCP报文段格式 TCP虽然是面向字节流的&#xff0c;但TCP传送带数据单元确是报文段。TCP报文段分为首部和数据段部分&#xff0c;而TCP的全部功能体现在它在首部中各字段的作用。因此&#xff0c;只有弄清TCP首部各字段的作用才能掌握TCP的工作原理。 TCP报文段首部的前20字节是…

CSS05:结构伪类选择器和属性选择器

结构伪类选择器 /*ul的第一个子元素*/ ul li:first-child{background: #0af6f6; }/*ul的最后一个子元素*/ ul li:last-child{background: #d27bf3; } /*选中p1&#xff1a;定位到父元素&#xff0c;选择当前的第一个元素 选择当前p元素的父级元素&#xff0c;选中父级元素的第…

使用策略模式 + 自动注册机制来构建旅游点评系统的搜索模块

✅ 目标&#xff1a; 搜索模块支持不同内容类型&#xff08;攻略、达人、游记等&#xff09;每种搜索逻辑用一个策略类表示自动注册&#xff08;基于注解 Spring 容器&#xff09;新增搜索类型时&#xff0c;只需添加一个类 一个注解&#xff0c;无需改工厂、注册表等&#x…

第八十九篇 大数据开发中的数据算法:贪心策略 - 生活中的“精打细算”艺术

在资源有限的世界里&#xff0c;贪心算法教会我们&#xff1a;局部最优的累积&#xff0c;往往是通往全局最高效的捷径。本文通过3个生活化场景原创图表&#xff0c;揭示大数据开发中最实用的优化策略。目录一、贪心算法核心思想&#xff1a;当下即最优二、三大核心应用场景详解…

【论文阅读】Dynamic Few-Shot Visual Learning without Forgetting

系统概述如下: (a) 一个基于卷积神经网络(ConvNet)的识别模型,该模型包含特征提取器和分类器; (b) 一个少样本分类权重生成器。这两个组件都是在一组基础类别上训练的,我们为这些类别准备了大量训练数据。在测试阶段,权重生成器会接收少量新类别的训练数据以及基础类别的…