当前位置:

首页 > 编程开发 > SpringBoot外部化如何配置

SpringBoot外部化如何配置

SpringBoot外部化配置(基于2.4.0以后)SpringBoot可以让你将配置外部化,这样你就可以在不同的环境中使用相同的应用程序代码。你可以使用各种外部配置源,包括Javaproperties文件、YAML文件、环境变量和命令行参数。属性值可以通过使用@Value注解直接注入你的Bean,也可以通过Spring的Environment访问,或者通过@ConfigurationProperties绑定到对象。同时SpringBoot也提供了一种非常特殊的PropertyOrder,来允许用户可以在

SpringBoot外部化配置(基于2.4.0以后)

Spring Boot可以让你将配置外部化,这样你就可以在不同的环境中使用相同的应用程序代码。 你可以使用各种外部配置源,包括Java properties 文件、YAML文件、环境变量和命令行参数。

属性值可以通过使用 @Value 注解直接注入你的Bean,也可以通过Spring 的 Environment 访问,或者通过 @ConfigurationProperties 绑定到对象。同时 Spring Boot 也提供了一种非常特殊的 PropertyOrder,来允许用户可以在适当的场景下覆盖某些属性值,该顺序旨在允许合理地覆盖值。

按以下顺序优先级从低到高, 后者的属性值覆盖前者 ,所有的配置会形成互补配置:

默认属性(使用 SpringApplication.setDefaultProperties 指定)

@Configuration类上的@PropertySource注解引入的配置属性

请注意,这样的属性源直到ApplicationContext被刷新时才会被添加到环境中。这对于配置某些属性来说已经太晚了,比如logging.* spring.main.* ,它们在刷新开始前就已经被读取了。

配置数据(例如application.properties文件)

对于random.*形式的属性,优先从RandomValuePropertySource获取(指优先于后者)

OS environment variables((操作系统环境变量)

Java System properties(Java 系统属性System.getProperties()

JNDI 属性

ServletContext 的 初始化参数

ServletConfig 的 初始化参数

SPRING_APPLICATION_JSON 属性

命令行参数

test 模块下的 properties 属性

test 模块下 @TestPropertySource 注解引入的配置文件

启用 devtools 时 $HOME/.config/spring-boot 路径下的配置

配置数据文件按以下加载顺序考虑:

  • 打包在 jar 中的应用程序属性(application.properties 和 YAML)

  • 打包在 jar 中的特定配置文件的应用程序属性(application-{profile}.properties 和 YAML)

  • 打包 jar 之外的应用程序属性(application.properties 和 YAML)

  • 打包 jar 之外的特定配置文件的应用程序属性(application-{profile}.properties 和 YAML)

SpringBoot配置文件

Spring中常见的配置文件类型

  • XML资源

  • Properties资源

  • YAML资源

Profile概述

Profile 本质上代表一种用于组织配置信息的维度,在不同场景下可以代表不同的含义。例如,如果 Profile 代表的是一种状态,我们可以使用 open、halfopen、close 等值来分别代表全开、半开和关闭等。再比如系统需要设置一系列的模板,每个模板中保存着一系列配置项。

配置命名规则:

/{application}.yml
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

配置文件加载顺序

Spring Boot 启动时,会自动加载 JAR 包内部及 JAR 包所在目录指定位置的配置文件(Properties 文件、YAML 文件)。列表按优先级排序(较低项目的值覆盖较早项目的值)

classpath( –classpath )

classpath 根路径

classpath 下的 /config 包

当前目录( –file )

当前目录下

当前目录下的 config/ 子目录

当前目录下的 config/ 子目录的直接子目录

. project-sample
├── config
│   ├── application.yml (4)
│   └── src/main/resources
|   │   ├── application.yml (1)
|   │   └── config
|   |   │   ├── application.yml (2)
├── application.yml (3)

启动时加载配置文件顺序:1 > 2 > 3 > 4

Profile 配置覆盖变更(2.4.0以后)

2.4.0以前版本,默认情况的加载顺序如下:

  • 打包在 jar 中的应用程序属性(application.properties 和 YAML)。

  • 打包 jar 之外的应用程序属性(application.properties 和 YAML)

  • 打包在 jar 中的特定于配置文件的应用程序属性(application-{profile}.properties 和 YAML)

  • 打包 jar 之外的特定于配置文件的应用程序属性(application-{profile}.properties 和 YAML)

注意:在之前的版本中,JAR 包外部的application.properties配置文件不会覆盖 JAR 包里面的基于 "profile" 的application-{profile}.properties 配置文件。

2.4.0以后版本,默认情况的搜索顺序如下:保证了 JAR 包外部的应用程序参数应优先于 JAR 包内部的特定激活的配置参数

  • 打包在 jar 中的应用程序属性(application.properties 和 YAML)。

  • 打包在 jar 中的特定于配置文件的应用程序属性(application-{profile}.properties 和 YAML)

  • 打包 jar 之外的应用程序属性(application.properties 和 YAML)

  • 打包 jar 之外的特定于配置文件的应用程序属性(application-{profile}.properties 和 YAML)

注意:同一位置下,Properties 文件优先级高于 YAML 文件 , 如果Spring Boot在优先级更高的位置找到了配置,那么它就会无视优先级低的配置。

文档排序(2.4.0以后)

从 Spring Boot 2.4 开始,加载 Properties 和 YAML 文件时候会遵循, 在文档中声明排序靠前的属性将被靠后的属性覆盖 。

激活指定配置文件

命令行激活: --spring.profiles.active=prod 

spring:
  profiles:
    active: dev #激活开发环境配置

配置文件激活如上,只需要在application.yml或者properties文件中配置即可

注意:在application.yml或者properties文件存在的情况下,不管激活的是prod还是dev,还是会读取默认的配置文件,只不过指定的配置文件会覆盖默认配置文件中的属性

导入额外的配置文件(2.4.0以后)

可以使用spring.config.import属性从其他地方导入更多的配置数据,比如spring.config.import=my.yaml 。它会将 my.yaml 文件作为临时文件放在当前配置文件之后处理,因此其属性具有更高的优先级

激活外部配置文件

在运行Jar包的命令中加入这个参数就可以指定Jar包以外的配置文件的位置了,也可以在application的配置文件中配置该属性

$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

这个参数就是指定外部application.yml配置文件位置的参数,它支持classpathfile路径

java -jar myproject.jar --spring.config.name=myproject

如果您不喜欢application.properties作为配置文件名,您可以通过指定spring.config.name环境属性来切换到另一个文件名

optional可选的配置文件

对于spring.config.locationspring.config.additional-locationspring.config.import等属性的路径,添加optional:前缀,则当对应文件不存在时应用仍可以正常启动

比如spring.config.location=optional:file:/my.yaml,当应用启动加载文件 my.yaml 不存在时,不会抛出异常

嵌入系统配置信息

例如,如果想要获取当前应用程序的名称并作为一个配置项进行管理,那么很简单,我们直接通过 ${spring.application.name} 占位符:

myapplication.name : ${spring.application.name}

假设我们使用 Maven 来构建应用程序,那么可以按如下所示的配置项来动态获取与系统构建过程相关的信息:

info: 
  app:
    encoding: @project.build.sourceEncoding@
    java:
      source: @java.version@
      target: @java.version@
# 等同于下述效果
info:
  app:
    encoding: UTF-8
    java:
        source: 1.8.0_31
        target: 1.8.0_31

配置参数提示

additional-spring-configuration-metadata.jsonspring-configuration-metadata.json在springboot-starter官方项目或第三方starter项目中随处可见,那它起的作用是什么?

  • 配置additional-spring-configuration-metadata.json文件后,在开发人员的IDE工具使用个人编写的配置读取很有效的在application.propertiesapplication.yml文件下完成提示

配置处理器

在Maven中,该依赖关系应被声明为可选的,如以下例子所示。


    org.springframework.boot
    spring-boot-configuration-processor
    true

创建additional-spring-configuration-metadata.json

resources/META-INF目录下创建additional-spring-configuration-metadata.json,分类为 “groups” 或 “properties”,附加值提示分类为 "hints",如以下例子所示:

{
    "groups": [
        {
            "name": "server",
            "type": "org.springframework.boot.autoconfigure.web.ServerProperties",
            "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
        },
        {
            "name": "spring.jpa.hibernate",
            "type": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties$Hibernate",
            "sourceType": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties",
            "sourceMethod": "getHibernate()"
        }
    ...
    ],
    "properties": [
        {
            "name": "server.port",
            "type": "java.lang.Integer",
            "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
        },
        {
            "name": "server.address",
            "type": "java.net.InetAddress",
            "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
        },
        {
            "name": "spring.jpa.hibernate.ddl-auto",
            "type": "java.lang.String",
            "description": "DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property.",
            "sourceType": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties$Hibernate"
        }
    ...
    ],
    "hints": [
        {
            "name": "spring.jpa.hibernate.ddl-auto",
            "values": [
                {
                    "value": "none",
                    "description": "Disable DDL handling."
                },
                {
                    "value": "validate",
                    "description": "Validate the schema, make no changes to the database."
                },
                {
                    "value": "update",
                    "description": "Update the schema if necessary."
                },
                {
                    "value": "create",
                    "description": "Create the schema and destroy previous data."
                },
                {
                    "value": "create-drop",
                    "description": "Create and then destroy the schema at the end of the session."
                }
            ]
        }
    ]
}
Property 属性

properties 数组中包含的JSON对象可以包含下表中描述的属性。

Name类型目的
nameString属性的全名。 名称采用小写的句号分隔形式(例如,server.address)。 这个属性是强制性的。
typeString该属性的数据类型的完整签名(例如,java.lang.String),但也有完整的通用类型(例如 java.util.Map)。 您可以使用此属性来指导用户可以输入的值的类型。 为了保持一致性,基元的类型是通过使用其包装类型来指定的(例如,boolean 变成 java.lang.Boolean)。 如果该类型不知道,可以省略。
descriptionString可以显示给用户的该property的简短描述。 如果没有描述,可以省略。 描述中的最后一行应以句号(.)结束。
sourceTypeString贡献此属性的来源的类名。 例如,如果该属性来自于一个用 @ConfigurationProperties 注解的类,该属性将包含该类的完全限定名称。 如果源类型未知,可以省略。
defaultValueObject默认值,如果没有指定该属性,则使用该值。 如果该属性的类型是一个数组,它可以是一个数组的值。 如果默认值是未知的,它可以被省略。
deprecationDeprecation指定该属性是否被废弃。 如果该字段没有被废弃,或者不知道该信息,可以省略。 下表提供了关于 deprecation 属性的更多细节。
Hint 属性

包含在 hints 数组中的JSON对象可以包含下表中的属性。

Name类型目的
nameString此提示所指向的属性的全名。 名称采用小写的句号分隔形式(如 spring.mvc.servlet.path)。 这个属性是强制性的。
valuesValueHint[]由 ValueHint 对象定义的有效值的列表(在下表中描述)。 每个条目都定义了值,并且可以有一个description。

每个 hint 元素的 values 属性中包含的JSON对象可以包含下表中描述的属性。

Name类型目的
valueObject提示所指的元素的一个有效值。 如果该属性的类型是一个数组,它也可以是一个数组的值。 这个属性是强制性的。
descriptionString可以显示给用户的价值的简短描述。 如果没有描述,可以省略。 描述中的最后一行应以句号(.)结束。

SpringBoot命令行参数

参考:https://www.yisu.com/article/191629.htm

启动Spring Boot项目时传递参数,有三种参数形式:

  • 选项参数,基本格式为--optName[=optValue]--为连续两个减号)

--foo
--foo=bar
--foo="bar then baz"
--foo=bar,baz,biz
  • 非选项参数

java -jar xxx.jar abc def

  • 系统参数

java -jar -Dserver.port=8081 xxx.jar

相当于 SpringBoot 基于 Java 命令行参数中的非选项参数自定义了选项参数的规则,具体可以看解析器SimpleCommandLineArgsParser,它里面调用其parse方法对参数进行解析

class SimpleCommandLineArgsParser {
    public CommandLineArgs parse(String... args) {
        CommandLineArgs commandLineArgs = new CommandLineArgs();
        for (String arg : args) {
            // --开头的选参数解析
            if (arg.startsWith("--")) {
                // 获得key=value或key值
                String optionText = arg.substring(2, arg.length());
                String optionName;
                String optionValue = null;
                // 如果是key=value格式则进行解析
                if (optionText.contains("=")) {
                    optionName = optionText.substring(0, optionText.indexOf('='));
                    optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
                } else {
                    // 如果是仅有key(--foo)则获取其值
                    optionName = optionText;
                }
                // 如果optionName为空或者optionValue不为空但optionName为空则抛出异常
                if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
                    throw new IllegalArgumentException("Invalid argument syntax: " + arg);
                }
                // 封装入CommandLineArgs
                commandLineArgs.addOptionArg(optionName, optionValue);
            } else {
                commandLineArgs.addNonOptionArg(arg);
            }
        }
        return commandLineArgs;
    }
}

参数值的获取

如果您需要访问传递给应用程序的参数SpringApplication.run(…),您可以注入一个ApplicationArguments。该ApplicationArguments接口提供对原始String[]参数以及选项参数和非选项参数的访问,如以下示例所示:

@Component
public class MyBean {
    @Autowired
    public MyBean(ApplicationArguments args) {
        boolean debug = args.containsOption("debug");
        List<String> files = args.getNonOptionArgs();
        // if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
    }
}
  • 另外,选项参数,也可以直接通过@Value在类中获取

  • 系统参数可以通过java.lang.System提供的方法获取

参数值的区别

关于参数值区别,重点看选项参数和系统参数。通过上面的示例我们已经发现使用选项参数时,参数在命令中是位于xxx.jar之后传递的,而系统参数是紧随java -jar之后。

如果不按照该顺序进行执行,比如使用如下方式使用选项参数:

java -jar --server.port=8081 xxx.jar

则会抛出如下异常:

Unrecognized option: --server.port=8081
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

如果将系统参数放在jar包后面,问题会更严重,会出现可以正常启动,但参数无法生效。这个错误是最坑的,所以一定谨记:通过-D传递系统参数时,务必放置在待执行的jar包之前。

扩展“外部化配置”属性源

SpringBoot外部化如何配置

SpringBoot外部化如何配置

SpingBoot怎么支持YAML配置文件解析?

处理@PropertySource注解从ConfigurationClassParser#processPropertySource方法进

Spring中@PropertySource默认不支持YAML格式的解析,但是SpringBoot的配置文件却可以解析YAML,这说明SpringBoot中已经实现了YAML文件的解析,我们只需要复用即可,我们可以看该注解源码

/**
 * Specify a custom {@link PropertySourceFactory}, if any.
 * 

By default, a default factory for standard resource files will be used.  * @since 4.3  * @see org.springframework.core.io.support.DefaultPropertySourceFactory  * @see org.springframework.core.io.support.ResourcePropertySource  */ Class factory() default PropertySourceFactory.class;

PropertySourceFactory的默认实现是DefaultPropertySourceFactory

public class DefaultPropertySourceFactory implements PropertySourceFactory {
	@Override
	public PropertySource createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
		return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
	}
}

ResourcePropertySource默认不支持YAML,所以我们可以通过实现PropertySourceFactory接口,然后用@PropertySource的factory属性来实现YAML的解析

public class YamlPropertySourceFactory implements PropertySourceFactory {
    @Override
    public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException {
        YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
        yamlPropertiesFactoryBean.setResources(resource.getResource());
        Properties yamlProperties = yamlPropertiesFactoryBean.getObject();
        return new PropertiesPropertySource(name, yamlProperties);
    }
}

关于ApplicationEnvironmentPreparedEvent没有被执行的原因

官方文档中有说到:有些事件实际上是在ApplicationContext被创建之前触发的,所以我们不能将这些事件的监听器注册为@Bean。

因为这个时候应用上下文还没有被创建,也就是说监听器也还没有被初始化,这个先后顺序不对,会导致这些事件的监听器不会被触发

但可以使用SpringApplication.addListeners(...) 方法或SpringApplicationBuilder.listeners(...) 方法注册它们。

如果您希望这些侦听器自动注册的话,可以通过新建一个META-INF/spring.factories文件,添加类似以下内容,SpringBoot会自动帮你注册。

org.springframework.context.ApplicationListener=com.example.project.MyListener
应用程序事件

应用程序运行时,应用程序事件按以下顺序发送:

  • An ApplicationStartingEvent is sent at the start of a run but before any processing, except for the registration of listeners and initializers.

  • An ApplicationEnvironmentPreparedEvent is sent when the Environment to be used in the context is known but before the context is created.

  • An ApplicationContextInitializedEvent is sent when the ApplicationContext is prepared and ApplicationContextInitializers have been called but before any bean definitions are loaded.

  • An ApplicationPreparedEvent is sent just before the refresh is started but after bean definitions have been loaded.

  • An ApplicationStartedEvent is sent after the context has been refreshed but before any application and command-line runners have been called.

  • An AvailabilityChangeEvent is sent right after with LivenessState.CORRECT to indicate that the application is considered as live.

  • An ApplicationReadyEvent is sent after any application and command-line runners have been called.

  • An AvailabilityChangeEvent is sent right after with ReadinessState.ACCEPTING_TRAFFIC to indicate that the application is ready to service requests.

  • An ApplicationFailedEvent is sent if there is an exception on startup.

The above list only includes SpringApplicationEvents that are tied to a SpringApplication. In addition to these, the following events are also published after ApplicationPreparedEvent and before ApplicationStartedEvent:

  • A WebServerInitializedEvent is sent after the WebServer is ready. ServletWebServerInitializedEvent and ReactiveWebServerInitializedEvent are the servlet and reactive variants respectively.

  • A ContextRefreshedEvent is sent when an ApplicationContext is refreshed.

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
C++动态数组初始化怎么写?常用语句与代码示例
C++动态数组初始化怎么写?常用语句与代码示例

深入解析C++中动态数组的初始化机制,涵盖new操作符的不同用法、基本类型与类对象的初始化差异,以及为何在现代C++开发中应优先使用std::vector。

using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

c语言函数递归 怎么选?常见方案对比分析
c语言函数递归 怎么选?常见方案对比分析

递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的

Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解

理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的

如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏

理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de

深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制
深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制

内存管理的基石在Objective-C的世界里,内存管理是开发者必须掌握的核心技能之一。作为一门在手动引用计数(MRC)时代诞生的语言,Objective-C要求程序员对对象的生命周期有清晰的认识。dealloc方法正是这一生命周期中至关重要的终点站。它是一个实例方法,当对象的引用计数降为零时,系统

理解 native2ascii:Java 国际化开发中的字符编码工具
理解 native2ascii:Java 国际化开发中的字符编码工具

native2ascii 工具的基本定位在Ja va应用程序的国际化与本地化开发过程中,处理非拉丁字符集是一个常见且关键的环节。Ja va内部使用Unicode字符集来统一表示全球各种语言的文字,但其属性文件(.properties)在历史上要求使用ASCII编码,或者更准确地说,要求非ASCII字

如何使用 native2ascii 转换中文字符为 Unicode 转义序列
如何使用 native2ascii 转换中文字符为 Unicode 转义序列

理解 native2ascii 工具的基本用途在软件开发,特别是涉及国际化处理的场景中,开发者常常需要处理不同编码的文本资源。native2ascii 是 Ja va 开发工具包(JDK)中提供的一个命令行实用程序,其主要功能是将包含本地字符编码(非ASCII字符)的文件,转换为包含 Unicode

Java native2ascii 命令详解:解决属性文件乱码问题
Java native2ascii 命令详解:解决属性文件乱码问题

native2ascii 命令的由来与作用在Ja va开发中,处理国际化资源文件是一个常见需求。资源文件通常以.properties格式存储,用于支持多语言界面。然而,Ja va属性文件默认采用ISO-8859-1字符集编码,这导致了一个直接的问题:当文件中包含非拉丁字符(如中文、日文、韩文等)时,

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。