springCore完整学习教程2,入门级别

简介: 本文是Spring Core学习教程的第二部分,深入讲解了Spring Boot中的外部化配置,包括配置文件的加载规则、使用配置文件、配置属性的加密、YAML的使用、配置随机值、系统环境属性的配置,以及@ConfigurationProperties注解的详细使用和与@Value注解的比较。

上集说到:2. 3,咱们从2.3集开始

2. Externalized Configuration

2.3. External Application Properties

Spring Boot会自动找到并加载应用程序。属性和应用程序。当应用程序启动时,从以下位置获取Yaml文件:

从类路径

类路径root

类路径/配置包

从当前目录

使用当前目录

当前目录下的config/子目录

config/子目录的直接子目录

该列表按优先级排序(较低项的值覆盖较早项的值)。加载文件中的文档作为propertresources添加到Spring环境中。
如果您不喜欢application作为配置文件名,您可以通过指定spring.config.name环境属性来切换到另一个文件名。例如,查找我的项目。属性和myproject。你可以这样运行你的应用程序:

2.3.1. Optional Locations

默认情况下,当指定的配置数据位置不存在时,Spring Boot将抛出ConfigDataLocationNotFoundException,并且您的应用程序将不会启动。

如果您想指定一个位置,但不介意它不总是存在,则可以使用可选的:前缀。您可以将此前缀与spring.config.location和spring.config一起使用。附加位置属性,以及spring.config.import声明。
例如,spring.config.import的值为optional:file:./myconfig。属性允许您的应用程序启动,即使myconfig。属性文件丢失。

如果你想忽略所有的configdatalocationnotfoundexception并始终继续启动你的应用程序,你可以使用spring.config。on-not-found财产。使用SpringApplication.setDefaultProperties(…)或使用系统/环境变量将值设置为忽略。

2.3.2. Wildcard Locations

使用通配符

例如,如果你有一些Redis配置和一些MySQL配置,你可能希望将这两部分配置分开,同时要求这两部分都存在于应用程序中。属性文件。这可能导致两个独立的应用程序。属性文件挂载在不同的位置,如/config/redis/application。Properties和/config/mysql/application.properties。在这种情况下,使用通配符位置config/*/将导致两个文件都被处理。

默认情况下,Spring Boot在默认搜索位置中包含config/*/。这意味着将搜索jar之外的/config目录的所有子目录。

通配符位置必须只包含一个*并以*/结尾,如果搜索位置是目录,或者如果搜索位置是文件,则必须包含*/。带有通配符的位置根据文件名的绝对路径按字母顺序排序。

通配符位置仅适用于外部目录。不能在类路径:location中使用通配符。

2.3.3. Profile Specific Files

就是application-{profile}.yml等 的类型

特定于配置文件的属性是从与标准应用程序相同的位置加载的。属性,特定于配置文件的文件总是覆盖非特定的文件。如果指定了多个配置文件,则应用最后获胜策略。例如,如果profile prod、live是由spring.profiles.active属性指定的,那么application-prod中的值就会被替换。属性可以被application-live.properties中的属性覆盖。

spring.profiles.active=prod
2.3.4. Importing Additional Data

应用程序属性可以使用spring.config.import属性从其他位置导入配置数据。当发现导入时,将对其进行处理,并将其视为插入声明导入的文档下面的附加文档。

例如,在您的类路径应用程序中可能有以下内容。属性文件:

spring.application.name=myapp
spring.config.import=optional:file:./dev.properties

这将触发在当前目录中导入dev.properties文件(如果存在这样的文件)。导入的dev.properties中的值将优先于触发导入的文件。在上面的例子中,dev.properties可以将spring.application.name重新定义为一个不同的值。

这里我举个例子吧

application-prod.yml文件:

server:
  port: 3344
spring:
  config:
    import: classpath:/config/dev.properties
  application:
    name: myapp

dev.properties

spring.applicdation.name=bb
package com.example.demo.demos;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Test {

    @Value("${spring.application.name}")
    public String name;
    @GetMapping("/get")
    public String getName(){
        return name;
    }

}

访问:localhost:3344/get

发现生成的是:

这个例子很明显吧!!!(明显还不关注我)

2.3.5. Importing Extensionless Files

某些云平台无法为卷挂载的文件添加文件扩展名。要导入这些无扩展文件,需要给Spring Boot一个提示,以便它知道如何加载它们。您可以通过在方括号中添加扩展提示来实现这一点。

比如:

spring.config.import=file:/etc/config/myconfig[.yaml]
2.3.6. Using Configuration Trees

在云平台(如Kubernetes)上运行应用程序时,通常需要读取平台提供的配置值。出于这种目的使用环境变量并不罕见,但这可能有缺点,特别是在值应该保密的情况下。

2.3.7. Property Placeholders

应用中的值。属性和应用程序。yaml在使用时将通过现有环境进行过滤,因此您可以引用以前定义的值(例如,从系统属性或环境变量)。标准的${name}属性占位符语法可以在值中的任何地方使用。属性占位符还可以使用:指定默认值,以将默认值与属性名称分开,例如${name:default}。
以下示例显示了带默认值和不带默认值的占位符的使用:

app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}

假如用户名属性没有在其他地方设置,app.description将具有MyApp是由Unknown编写的Spring Boot应用程序的值。

2.3.8. Working With Multi-Document Files

Spring Boot允许您将单个物理文件拆分为多个逻辑文档,每个文档都是独立添加的。文档按照从上到下的顺序进行处理。以后的文档可以覆盖以前文档中定义的属性。
为应用程序。yaml文件,则使用标准的yaml多文档语法。三个连续的连字符表示一个文档的结束和下一个文档的开始。
例如,下面的文件有两个逻辑文档:

spring:
  application:
    name: "MyApp"
---
spring:
  application:
    name: "MyCloudApp"
  config:
    activate:
      on-cloud-platform: "kubernetes"

like this:

spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes

两个物理文件,但是有三个逻辑项目

2.3.9. Activation Properties

有时,只有在满足某些条件时才激活给定的一组属性是有用的。例如,您可能拥有仅在特定概要文件处于活动状态时才相关的属性。
您可以使用spring.config.activate.*有条件地激活属性文档。
以下激活属性可用:

Property Note
on-profile A profile expression that must match for the document to be active.
on-cloud-platform The CloudPlatform that must be detected for the document to be active.

例如,下面的命令指定第二个文档仅在Kubernetes上运行时才激活,并且仅当“prod”或“staging”配置文件处于激活状态时才激活:

myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set

2.4. Encrypting Properties

Spring Boot没有为加密属性值提供任何内置支持,但是,它提供了修改Spring环境中包含的值所必需的钩子点。

2.5. Working With YAML

YAML是JSON的超集,因此是指定分层配置数据的方便格式。只要在你的类路径上有SnakeYAML库,SpringApplication类就会自动支持YAML作为属性的替代。

2.5.1. Mapping YAML to Properties

YAML文档需要从层次结构格式转换为可与Spring环境一起使用的平面结构。例如,考虑以下YAML文档:

environments:
  dev:
    url: "https://dev.example.com"
    name: "Developer Setup"
  prod:
    url: "https://another.example.com"
    name: "My Cool App"

为了从环境中访问这些属性,它们将被扁平化如下:

environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App
2.5.2. Directly Loading YAML

Spring Framework提供了两个方便的类,可用于加载YAML文档。YamlPropertiesFactoryBean将YAML作为属性加载,而YamlMapFactoryBean将YAML作为Map加载。
你也可以使用YamlPropertySourceLoader类,如果你想加载YAML作为一个Spring PropertySource。

2.6. Configuring Random Values

RandomValuePropertySource对于注入随机值很有用(例如,在秘密或测试用例中)。它可以生成整数、长整数、uuid或字符串,示例如下:

my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number-less-than-ten=${random.int(10)}
my.number-in-range=${random.int[1024,65536]}

我都测试过了,各位当真不点关注?0.0

2.7. Configuring System Environment Properties

Spring Boot支持为环境属性设置前缀。如果系统环境由具有不同配置需求的多个Spring Boot应用程序共享,这将非常有用。系统环境属性的前缀可以直接在SpringApplication上设置。
例如,如果将前缀设置为input,则远程。超时也将在系统环境中解析为input.remote.timeout。

2.8. Type-safe Configuration Properties

使用@Value("${property}")注释注入配置属性有时会很麻烦,特别是在处理多个属性或数据本质上是分层的情况下。Spring Boot提供了另一种处理属性的方法,让强类型bean管理和验证应用程序的配置。

2.8.1. JavaBean Properties Binding

可以绑定声明标准JavaBean属性的bean,如下例所示:

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("my.service")
public class MyProperties {

    private boolean enabled;

    private InetAddress remoteAddress;

    private final Security security = new Security();

    public boolean isEnabled() {
        return this.enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public void setRemoteAddress(InetAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

    public Security getSecurity() {
        return this.security;
    }

    public static class Security {

        private String username;

        private String password;

        private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

        public String getUsername() {
            return this.username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return this.password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public List<String> getRoles() {
            return this.roles;
        }

        public void setRoles(List<String> roles) {
            this.roles = roles;
        }

    }

}
  • my.service.enabled, with a value of false by default.

  • my.service.remote-address, with a type that can be coerced from String.

  • my.service.security.username, with a nested "security" object whose name is determined by the name of the property. In particular, the type is not used at all there and could have been SecurityProperties.

  • my.service.security.password.

  • my.service.security.roles, with a collection of String that defaults to USER.

配置一下主类

@ConfigurationPropertiesScan(basePackages = "com.example.demo.demos")

在配置文件上写:

my.service.enabled=true
my.service.remoteAddress=127.0.0.1
my.service.security.username=admin
my.service.security.password=admin123
my.service.security.roles=USER,ADMIN

就可以测试了,自己测试吧,又不关注我,凭什么给你们测试,哼

2.8.2. Constructor Binding

上一节的例子可以用不可变的方式重写,如下面的例子所示:

import java.net.InetAddress;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;

@ConstructorBinding
@ConfigurationProperties("my.service")
public class MyProperties {

    private final boolean enabled;

    private final InetAddress remoteAddress;

    private final Security security;

    public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
        this.enabled = enabled;
        this.remoteAddress = remoteAddress;
        this.security = security;
    }

    public boolean isEnabled() {
        return this.enabled;
    }

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public Security getSecurity() {
        return this.security;
    }

    public static class Security {

        private final String username;

        private final String password;

        private final List<String> roles;

        public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
            this.username = username;
            this.password = password;
            this.roles = roles;
        }

        public String getUsername() {
            return this.username;
        }

        public String getPassword() {
            return this.password;
        }

        public List<String> getRoles() {
            return this.roles;
        }

    }

}

在这个设置中,@ConstructorBinding注释用于指示应该使用构造函数绑定。这意味着绑定器将期望找到一个构造函数,其中包含您希望绑定的参数。如果使用的是Java 16或更高版本,则可以对记录使用构造函数绑定。在这种情况下,除非您的记录有多个构造函数,否则不需要使用@ConstructorBinding。
@ConstructorBinding类的嵌套成员(如上面示例中的Security)也将通过它们的构造函数绑定。
默认值可以使用@DefaultValue on来指定

2.8.3. Enabling @ConfigurationProperties-annotated Types

Spring Boot提供了绑定@ConfigurationProperties类型并将它们注册为bean的基础设施。您既可以逐个类地启用配置属性,也可以启用与组件扫描类似的配置属性扫描。
有时,带有@ConfigurationProperties注释的类可能不适合扫描,例如,如果您正在开发自己的自动配置,或者希望有条件地启用它们。在这些情况下,使用@ enablecconfigurationproperties注释指定要处理的类型列表。

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {

}

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("some.properties")
public class SomeProperties {

}

要使用配置属性扫描,请在应用程序中添加@ConfigurationPropertiesScan注释。通常,它被添加到带有@SpringBootApplication注释的主应用程序类中,但它也可以添加到任何@Configuration类中。默认情况下,将从声明注释的类的包进行扫描。如果您想定义要扫描的特定包,可以这样做,如下所示:

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {

}
2.8.6. Relaxed Binding

Spring Boot使用一些宽松的规则将Environment属性绑定到@ConfigurationProperties bean,因此在Environment属性名和bean属性名之间不需要精确匹配。这很有用的常见示例包括用虚线分隔的环境属性(例如,上下文路径绑定到contextPath)和大写的环境属性(例如,PORT绑定到PORT)。

例如,考虑下面的@ConfigurationProperties类:

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "my.main-project.person")
public class MyPersonProperties {

    private String firstName;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

}

使用上述代码,可以使用以下属性名称:

Property Note
my.main-project.person.first-name Kebab case, which is recommended for use in .properties and YAML files.
my.main-project.person.firstName Standard camel case syntax.
my.main-project.person.first_name Underscore notation, which is an alternative format for use in .properties and YAML files.
MY_MAINPROJECT_PERSON_FIRSTNAME Upper case format, which is recommended when using system environment variables.

注释的前缀值必须是kebab case(小写并用-分隔,如my.main-project.person),上面使用了驼峰命名法。

2.8.7. Merging Complex Types

当列表在多个位置配置时,重写通过替换整个列表来工作。
例如,假设MyPojo对象的名称和描述属性默认为空。下面的例子展示了MyProperties中的MyPojo对象列表:

import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("my")
public class MyProperties {

    private final List<MyPojo> list = new ArrayList<>();

    public List<MyPojo> getList() {
        return this.list;
    }

}

考虑以下配置:

my.list[0].name=my name
my.list[0].description=my description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name
2.8.8. Properties Conversion

当Spring Boot绑定到@ConfigurationProperties bean时,它尝试将外部应用程序属性强制为正确的类型。如果您需要自定义类型转换,您可以提供ConversionService bean(带有名为ConversionService的bean)或自定义属性编辑器(通过CustomEditorConfigurer bean)或自定义转换器(带有注释为@ConfigurationPropertiesBinding的bean定义)。

2.8.9. @ConfigurationProperties Validation

每当@ConfigurationProperties类被Spring的@Validated注释时,Spring Boot都会尝试验证它们。您可以使用JSR-303 javax。直接在配置类上验证约束注释。要做到这一点,请确保在你的类路径上有一个兼容的JSR-303实现,然后向你的字段添加约束注释,如下例所示:

import java.net.InetAddress;

import javax.validation.constraints.NotNull;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@ConfigurationProperties("my.service")
@Validated
public class MyProperties {

    @NotNull
    private InetAddress remoteAddress;

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public void setRemoteAddress(InetAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

}

您还可以通过用@Validated注释创建配置属性的@Bean方法来触发验证。

为了确保总是触发对嵌套属性的验证,即使没有找到任何属性,也必须用@Valid注释关联字段。下面的示例建立在前面的MyProperties示例之上:

import java.net.InetAddress;

import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@ConfigurationProperties("my.service")
@Validated
public class MyProperties {

    @NotNull
    private InetAddress remoteAddress;

    @Valid
    private final Security security = new Security();

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public void setRemoteAddress(InetAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

    public Security getSecurity() {
        return this.security;
    }

    public static class Security {

        @NotEmpty
        private String username;

        public String getUsername() {
            return this.username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

    }

}

您还可以通过创建一个名为configurationPropertiesValidator的bean定义来添加自定义Spring Validator。@Bean方法应该声明为静态的。配置属性验证器是在应用程序生命周期的早期创建的,并且将@Bean方法声明为静态可以在不实例化@Configuration类的情况下创建bean。这样做可以避免可能由早期实例化引起的任何问题。

2.8.10. @ConfigurationProperties vs. @Value

@Value注释是容器的核心特性,它不提供与类型安全配置属性相同的特性。下表总结了@ConfigurationProperties和@Value支持的特性:

Feature @ConfigurationProperties @Value
Relaxed binding Yes Limited (see note below)
Meta-data support Yes No
SpEL evaluation No Yes

如果您确实想使用@Value,我们建议您使用规范形式(仅使用小写字母的kebab-case)引用属性名。这将允许Spring Boot使用与放松绑定@ConfigurationProperties时相同的逻辑。
例如,@Value("${demo.item-price}")将选择demo。项目价格和演示。itemPrice表单。属性文件,以及系统环境中的DEMO_ITEMPRICE。如果你使用了@Value("${demo. itemprice}"), demo. itemprice将会被替换。item-price和DEMO_ITEMPRICE将不被考虑。

如果您为自己的组件定义了一组配置键,我们建议您将它们分组在带有@ConfigurationProperties注解的POJO中。这样做将为您提供结构化的、类型安全的对象,您可以将其注入到自己的bean中。
在解析这些文件和填充环境时,不会处理来自应用程序属性文件的SpEL表达式。但是,可以在@Value中编写斯佩尔表达式。如果应用程序属性文件中的属性值是一个SpEL表达式,则在通过@Value使用时对其进行计算。

目录
相关文章
|
22天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
14天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
19天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2567 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
17天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
1天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
150 2
|
18天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1564 16
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
2天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
21天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
906 14
|
16天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
678 9
|
14天前
|
存储 监控 调度
云迁移中心CMH:助力企业高效上云实践全解析
随着云计算的发展,企业上云已成为创新发展的关键。然而,企业上云面临诸多挑战,如复杂的应用依赖梳理、成本效益分析等。阿里云推出的云迁移中心(CMH)旨在解决这些问题,提供自动化的系统调研、规划、迁移和割接等功能,简化上云过程。CMH通过评估、准备、迁移和割接四个阶段,帮助企业高效完成数字化转型。未来,CMH将继续提升智能化水平,支持更多行业和复杂环境,助力企业轻松上云。