源码分析

SpringBoot自动配置流程

​ 首先,我们要了解在@SpringBootApplication注解的内部,还具有@EnableAutoConfiguration,@SpringBootConfiguration,@ComponentScan三个主要注解。

@SpringBootConfiguration  //标注该类是配置类,需要通过该类查找自动配置文件
@EnableAutoConfiguration //自动配置的关键注解 其内部就是执行自动配置的代码
@ComponentScan(excludeFilters = {
//type : 要使用的筛选器类型 , classes 指定类型筛选器
//TypeExcludeFilter.class 筛选掉spirngBootApplication中被指定排除的配置类
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
//AutoConfigurationExcludeFilter 将配置类与spirng.factories中的EnableAutoConfiguration对应的配置类进行对比匹配, 如果一致,会被排除掉
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }
) //扫描指定包的文件,将带有特定注解的类注入到Bean中
public @interface SpringBootApplication { }

@ComponentScan

  1. @ComponentScan注解主要用来扫描我们项目中的所有被像@service ,@Repository , @Controller,@configuration 等注解修饰的类, 将其注入到我们的IOC容器中,其中也包括我们的自动配置的文件:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class) //表示可以重复利用@ComponentScan注解
/**
*作用 : 可以扫描指定的包,如果未指定包范围,将从该注解标注类所在的包进行扫描,
* 与XML形式的<context:component scan>不同的是 @componentScan没有Config属性(true * 就开启了属性自动注入的功能,如果是false就是关闭属性自动注入的功能),因为使用
* @ComponentScan则默认所有的类都进行自动注入,会将所有扫描到的组件注入到IOC容器中
*/
public @interface ComponentScan {
}

@SpringBootConfiguration

  1. @SpringBootConfiguration 是SpringBoot替代@Configuration的注解,增加了自动找到配置的功能
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //表示这是一个配置类 通过其间接了解到,@SpringBootApplication也是一个配置类
/**
* 用作Spring的标准@Configuration注解的替代,以便可以自动找到配置
*/
public @interface SpringBootConfiguration {
}

@EnableAutoConfiguration

  1. @EnableAutoConfiguration注解就是启动自动配置的关键注解,其内部使用了@import注解引入了一个AutoConfigurationImportSelector 自动配置类选择器
@AutoConfigurationPackage //自动配置所在包注解,通过basePackages指定配置所在的包或者通过basePackageClasses指定基本包类,如果未指定,会默认注册指定注解类所在的包
//AutoConfigurationImportSelector自动配置选择器,实现了ImportSelector接口,重写了selectImports方法,自动配置的具体实现就在其内部进行
//ImportSelector接口作用 :根据给定的选择条件(通常是一个或多个注解属性)确定应导入哪个配置类。
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration { }

​ 在其内部重写了selectImports方法, 通过调用getAutoConfigurationEntry()方法根据传入的注解元数据,获取到自动配置类的实体,而后从实体中获取具体的配置信息,配置信息在实体内部是一个list集合,所以将其转化为String数组后返回。

//为方便显示及理解,省略了该类实现的部分接口和具体的代码实现,需要了解可进入源码查看
public class AutoConfigurationImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//AnnotationMetadata: 配置类的注解元数据,也就是配置类的注解信息
//调用getAutoConfigurationEntry()方法根据传入的注解信息,获取并返回自动配置类的实体
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
//从配置实体中获取具体的配置信息,返回的是一个list集合,而后通过toStringArray()方法转存到字符串数组中返回
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}

getAutoConfigurationEntry()

//可以先看下获取的大致流程,而后进入查看器方法内部的具体实现
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
//1. 从注解元数据中获取注解的相应属性,将相应属性存储到map中返回
//1.1AnnotationAttributes是一个Map集合,其继承了LinkedHashMap
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//2. 通过getCandidateConfigurations()方法根据注解元数据和注解的属性信息 获取应该进行自动配置的类名,可以理解为自动配置候选项
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//2.1 通过removeDuplicates()方法对自动配置的类名进行去重处理
configurations = removeDuplicates(configurations);
//3. 根据注解元数据和注解属性获取到需排除配置项
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//3.1检查是否有无效的排除类存在
checkExcludedClasses(configurations, exclusions);
//3.2从自动配置候选项中删除需要排除的配置项
configurations.removeAll(exclusions);
//4. 调用getConfigurationClassFilter()方法获取到获取配置的所有AutoConfigurationImportFilter的实现类(对spring.factories进行过滤的类),调用filter方法对配置文件进行筛选,而后返回需要自动配置的类
configurations = getConfigurationClassFilter().filter(configurations);
//5. 根据spring.factories文件中的AutoConfigurationImportListener事件监听器发布并处理监听事件,最后根据多次过滤、判重返回配置类合集
fireAutoConfigurationImportEvents(configurations, exclusions);
//6. 创建一个新的配置实体ConfigurationEntry并返回,包含需要配置项configurations,和被排除配置项exclusions
return new AutoConfigurationEntry(configurations, exclusions);
}

下面了解以下getAutoConfigurationEntry()内部调用的方法源码

从注解元数据中返回相应的属性信息

  1. getAttributes(AnnotationMetadata annotationMetadata)

    /**
    * 从注解元数据中返回相应的属性信息。
    * param 注解元数据信息
    * return 注解元数据的属性信息 其本质是一个Map集合
    */
    protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
    /**
    * getAnnotationClass() 返回源注解类 -->EnableAutoConfiguration.class
    * getAnnotationClass().getName(); 获取注解类的完全限定类名
    */
    String name = getAnnotationClass().getName();
    /**
    * metadata.getAnnotationAttributes(String annotationName,boolean classValuesAsString)
    * 作用: 检索给定注解的属性
    * @param1 要查找的注解类的完全限定类名
    * @param2 是否将类引用转换为String类名,以便作为返回Map中的值公开,而不是可能必须首先加载的类引用
    *
    *AnnotationAttributes.fromMap(@Nullable Map<String, Object> map);
    * 基于给定的集合返回AnnotationAttributes实例。如果该集合是AnnotationAttributes实例或其子类,它将被强制转换并立即返回,而无需创建新实例。否则,将通过将提供的映射传递给AnnotationAttributes的map)的构造函数来创建新实例。其参数是一个Map类型的注解属性数据源,也就是attrbuties
    */
    AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
    /**
    *Assert类 是一个协助验证参数的断言实用程序类,详细使用可以查看其源码
    *Assert.notNull(@Nullable Object object, Supplier<String> messageSupplier)方法
    * 作用 : 判断对象是不是null, 如果为null,报错提示
    * param1 : 要进行判断的对象
    * param2 : 如果为null,要给予返回的异常信息
    */
    Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()+ " annotated with " + ClassUtils.getShortName(name) + "?");
    //返回注解元数据的属性信息map集合
    return attributes;
    }

获取应该进行自动配置的类名

  1. getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes);

    /**
    *根据注解元数据和注解的属性信息 获取应该进行自动配置的类名,可以理解为自动配置的候选项(初选名单)
    *param1 元注解数据
    *param2 元注解数据的属性信息集合
    *return List<String> 存储的数据就是应该继续宁自动配置的类名
    */ protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    /**
    * SpringFactoriesLoader是一个用于框架内部使用的通用工厂加载机制
    *
    *
    */
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
    getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
    }

对自动配置项进行去重处理

  1. 1 configurations = removeDuplicates(configurations);对自动配置的类名进行去重处理
//通过removeDuplicates()方法对自动配置的类名进行去重处理
//利用Set集合数据不重复特性,将list集合存储到LinkedHashSet集合中进行去重处理,而后再将去重的结果存储到List集合中返回
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}

从自动配置项中筛选被排除配置项

  1. configurations.removeAll(exclusions);
//从自动配置候选项中筛选需排除配置项

protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//创建一个需排除配置项集合excluded
Set<String> excluded = new LinkedHashSet<>();
//从属性信息集合中获取到key为exclude的值,将其存储到excluded集合中
excluded.addAll(asList(attributes, "exclude"));
//从属性信息集合中获取到key为excludeName的数据,返回的是一个字符串数组,返回后将其转化为List集合,存储到excluded集合中
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
/**
* getExcludeAutoConfigurationsProperty():
*返回 spring.autoconfigure.exclude 属性排除的自动配置
*/
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
} -----------------------------------------------------------------------------------------
/*下面方法是上面方法所调用的个别方法源码,不深究者可以略过*/
----------------------------------------------------------------------------------------- //attributes.getStringArray("excludeName")
public String[] getStringArray(String attributeName) {
return getRequiredAttribute(attributeName, String[].class);
}

exclude 和excludeName 都是指定某些类在项目启动时不进行自动配置,其一般在@SpringBootApplication 中进行配置。

检查是否有无效的排除类存在

  1. 1 configurations.removeAll(exclusions);
//检查是否有无效的排除类存在
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
//创建一个用于存储无效配置项的集合
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
//循环需排除配置项
for (String exclusion : exclusions) {
//根据类的全限定名判断该类是否存在且可以被加载,并且 需排除配置项集合是否包含该类
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
//如果存在,且不再需排除配置项的集合中,将其添加到无效配置项集合中
invalidExcludes.add(exclusion);
}
}
//如果无效配置项集合不为空,说明存在无效配置项
if (!invalidExcludes.isEmpty()) {
//处理无效配置项 --> 报错 IllegalStateException 无效状态异常
handleInvalidExcludes(invalidExcludes);
}
}
-----------------------------------------------------------------------------------------
/*下面方法是上面方法所调用的个别方法源码,不深究者可以略过*/
----------------------------------------------------------------------------------------- /**
*ClassUtils.isPresent() 根据类名称判断是否存在并且可以加载,如果类或其依赖项之一不存在或无法 加载返回false
* param1 className 要检查的类的名称
* param2 classLoader 要使用的类加载器(如果为null,表示默认的类加载器)
*/
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
//forName(类名称,类加载器) 用于替换Class.forName()方法, 并且还返回所提供名称的类实例
forName(className, classLoader);
return true;
}
catch (IllegalAccessError err) {
throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
className + "]: " + err.getMessage(), err);
}
catch (Throwable ex) {
// Typically ClassNotFoundException or NoClassDefFoundError...
return false;
}
}

从自动配置项中删除需要被排除的配置项

  1. 2 configurations.removeAll(exclusions);
/**
*从自动配置候选项中删除需要排除的配置项
* 集合A.removeAll(集合B);作用就是从集合A数据项中删除掉集合B所包含的元素
*/
configurations.removeAll(exclusions);

创建配置类过滤器对配置项进行筛选过滤

  1. configurations = getConfigurationClassFilter().filter(configurations);
//通过getConfigurationClassFilter()获取所有AutoConfigurationImportFilter的实现类(对spring.factories进行过滤的类),而后调用filter方法对配置文件进行筛选,而后返回需要自动配置的类
configurations = getConfigurationClassFilter().filter(configurations); -----------------------------------------------------------------------------------------
/*下面方法是上面方法所调用的个别方法源码,不深究者可以略过*/
----------------------------------------------------------------------------------------- //获取配置类过滤器
private ConfigurationClassFilter getConfigurationClassFilter() {
//this.configurationClassFilter当前类的配置类过滤器是不是为null
if (this.configurationClassFilter == null) {
// 获取AutoConfigurationImportFilter过滤器的实现类集合
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters(); for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter); //在监听器注入是有描述,两者使用的同一方法
}
//实例化配置类过滤器 ,根据 类加载器和过滤器实现类实例化配置类过滤器
//ConfigurationClassFilter类内部含有类加载器和过滤器实现类集合的属性
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
}
//返回配置类过滤器
return this.configurationClassFilter;
}
//getAutoConfigurationImportFilters(); 获取AutoConfigurationImportFilter过滤器的实现类集合
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
/**
* List<T> loadFacotries(Class<T> factoryType, @Nullable ClassLoader classLoader)
* 使用给定的类加载器从{"META-INF/spring.factories"}加载并实例化指定过滤器工厂的实现类
* 在结果返回之前会对结果集进行排序
* param1 表示工厂的接口或者抽象类,-->生成其子类
* param2 当前类的类加载器-->用于加载抽象类的实现类
* return 返回指定接口或者抽象类的实现类List集合
*/
//返回AutoConfigurationImportFilter实现类的集合
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
} //configurations = getConfigurationClassFilter().filter(configurations);
//过滤 自动配置项
List<String> filter(List<String> configurations) {
long startTime = System.nanoTime();
//将将配置转化为字符串数组
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false; for (AutoConfigurationImportFilter filter : this.filters) {
//循环过滤条件与配置项进行一一匹配,剔除掉条件不成立的配置项
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
candidates[i] = null;
skipped = true;
}
}
}
//如果全都符合则直接返回配置项集合
if (!skipped) {
return configurations;
}
//创建结果集集合
List<String> result = new ArrayList<>(candidates.length);
for (String candidate : candidates) {
//配置项不为null就添加到配置类中
if (candidate != null) {
result.add(candidate);
}
}
//返回结果
return result;
}
}

创建配置类监听器对自动配置进行监听

  1. fireAutoConfigurationImportEvents(configurations, exclusions);
//根据spring.factories文件中的AutoConfigurationImportListener事件监听器发布并处理监听事件,最后根据多次过滤、判重返回配置类合集

private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
//从{ "META-INF/spring.factories"}加载并实例化自动配置类监听器 AutoConfigurationImportListener的实现类集合
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
//如果监听器不为空的话
if (!listeners.isEmpty())
//创建fireAutoConfigurationImportEvents监听事件
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
//循环遍历 判断listener是否是 Aware 通过Aware接口 实现对bean各阶段的监听
for (AutoConfigurationImportListener listener : listeners) {
//通过Aware类的实现类对监听器进行配置 -->解这一模块,可以重点关注以下Aware接口
invokeAwareMethods(listener);
//进行自动配置的导入 event 到自动配置时进行的事件-->对自动配置的监听
listener.onAutoConfigurationImportEvent(event);
}
}
}
//根据Aware类对bean的各阶段进行监听配置
private void invokeAwareMethods(Object instance) {
//判断监听器是否是Aware或其实现类
if (instance instanceof Aware) { if (instance instanceof BeanClassLoaderAware) {
/**
* BeanClassLoaderAware 允许bean知道bean的回调ClassLoader,即当前bean工厂用来加载bean类的类加载器。这主要是由框架类来实现的,这些框架类必须通过名称来获取应用程序类,尽管它们本身可能
从共享类加载器加载的。
* 方法 setBeanClassLoader(ClassLoader classLoader);
* 将bean的类加载器 提供给bean实例的回调。
* 作用范围: 在填充普通bean属性之后,初始化回调之前调用
*/
((BeanClassLoaderAware) instance).setBeanClassLoader(this.beanClassLoader);
}
if (instance instanceof BeanFactoryAware) {
/**
* BeanFactoryAware 表示接口知道其拥有的{BeanFactory}的bean实现。
*注意 :大多数的bean都可以通过属性注入和构造注入接收对bean的引用
*方法 :setBeanFactory(BeanFactory beanFactory)
* 向bean实例提供拥有工厂的回调。
* 作用范围: 在填充普通bean属性之后,初始化回调之前调用,
*/ ((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
}
if (instance instanceof EnvironmentAware) {
/**
* EnvironmentAware 表示该接口可以收到其运行环境的通知
*方法 :setEnvironment(Environment environment);
* 设置此组件的运行环境
*/ ((EnvironmentAware) instance).setEnvironment(this.environment);
}
if (instance instanceof ResourceLoaderAware) {
/**
* ResourceLoaderAware 接口,该接口可以收到该对象运行的ResourceLoader资源封装类加载器
*方法 :setResourceLoader(ResourceLoader resourceLoader);
*作用 :设置此对象运行的ResourceLoader。可能是一个ResourcePatternResolver
*作用范围: 在填充普通bean属性之后,初始化回调之前调用
*/ ((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
}
}
}

创建新的配置实体后返回SelectImports方法体内

  1. return new AutoConfigurationEntry(configurations, exclusions);
根据需要配置项和被排除项实例化新的配置实体,并返回
AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) {
this.configurations = new ArrayList<>(configurations);
this.exclusions = new HashSet<>(exclusions);
}

将配置实体中的配置信息转化为字符串数组返回,完成注入

//获取最终要导入的配置实体
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
//从配置实体中获取具体的配置信息,返回的是一个list集合,而后通过toStringArray()方法转存到字符串数组中返回
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());

本笔记个人查看源码时根据立即理解缩写,如有错误可留言告知,谢谢

SpringBoot自动配置(装配)流程的更多相关文章

  1. 小BUG大原理:重写WebMvcConfigurationSupport后SpringBoot自动配置失效

    一.背景 公司的项目前段时间发版上线后,测试反馈用户的批量删除功能报错.正常情况下看起来应该是个小 BUG,可怪就怪在上个版本正常,且此次发版未涉及用户功能的改动.因为这个看似小 BUG 我了解到不少 ...

  2. 小BUG大原理 | 第一篇:重写WebMvcConfigurationSupport后SpringBoot自动配置失效

    一.背景 公司的项目前段时间发版上线后,测试反馈用户的批量删除功能报错.正常情况下看起来应该是个小BUG,可怪就怪在上个版本正常,且此次发版未涉及用户功能的改动.因为这个看似小BUG我了解到不少未知的 ...

  3. SpringBoot实战之SpringBoot自动配置原理

    SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties 或者 @Confi ...

  4. springboot自动配置源码解析

    springboot版本:2.1.6.RELEASE SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfig ...

  5. SpringBoot自动配置原理学习

    介绍 构建Springboot项目时我们会创建一个启动类 @SpringBootApplication public class DemoApplication { public static voi ...

  6. 浅谈springboot自动配置原理

    前言 springboot自动配置关键在于@SpringBootApplication注解,启动类之所以作为项目启动的入口,也是因为该注解,下面浅谈下这个注解的作用和实现原理 @SpringBootA ...

  7. SpringBoot自动配置源码调试

    之前对SpringBoot的自动配置原理进行了较为详细的介绍(https://www.cnblogs.com/stm32stm32/p/10560933.html),接下来就对自动配置进行源码调试,探 ...

  8. 源码学习系列之SpringBoot自动配置(篇一)

    源码学习系列之SpringBoot自动配置源码学习(篇一) ok,本博客尝试跟一下Springboot的自动配置源码,做一下笔记记录,自动配置是Springboot的一个很关键的特性,也容易被忽略的属 ...

  9. 源码学习系列之SpringBoot自动配置(篇二)

    源码学习系列之SpringBoot自动配置(篇二)之HttpEncodingAutoConfiguration 源码分析 继上一篇博客源码学习系列之SpringBoot自动配置(篇一)之后,本博客继续 ...

随机推荐

  1. 使用Linux、Nginx和Github Actions托管部署ASP.NET Core 6.0应用

    使用Linux.Nginx和Github Actions托管部署ASP.NET Core 6.0应用 前言 本文主要参考微软这篇文档而来 Host ASP.NET Core on Linux with ...

  2. JUC源码学习笔记5——线程池,FutureTask,Executor框架源码解析

    JUC源码学习笔记5--线程池,FutureTask,Executor框架源码解析 源码基于JDK8 参考了美团技术博客 https://tech.meituan.com/2020/04/02/jav ...

  3. QFile 对文件进行读写操作

    QFile 对文件进行读写操作 1 QFile 进行读写操纵 2 QFile file(pah ) 文件路径 3 读  file.open(打开方式)  file.readAll(). file.re ...

  4. KingbaseES V8R6集群维护案例之--修改securecmdd工具服务端口

    案例说明: 在一些生产环境,为了系统安全,不支持ssh互信,或限制root用户使用ssh登录,KingbaseES V8R6可以使用securecmdd工具支持主机之间的通讯.securecmdd工具 ...

  5. 001从零开始入门Entity Framework Core——基础知识

    Entity Framework (EF) Core 是轻量化.可扩展.开源和跨平台版的常用 Entity Framework 数据访问技术. 一.什么是 Entity Framework Core ...

  6. 使用Vite快速构建Vue3+ts+pinia脚手架

    一.前言 vue3的快速更新,很多IT发展快的地区在22开始都已经提上日程,小编所在的青岛好像最近才有点风波.vue3的人才在青岛还是比较稀缺的哈,纯属小编自己的看法,可能小编是个井底之蛙!! vue ...

  7. HCIA-STP原理与配置

    STP协议生成树协议: 为了保证网络可靠,所以在组网时需要设置冗余链路和设备,从而在物理结构上形成结构,又因为交换机的工作特点导致二层网络中产生广播风暴和MAC地址表震荡现象,影响用户体验. 广播风暴 ...

  8. Python数据科学手册-机器学习:线性回归

    朴素贝叶斯是解决分类任务的好起点,线性回归是解决回归任务的好起点. 简单线性回归 将数据拟合成一条直线. y = ax + b , a 是斜率, b是直线截距 原始数据如下: 使用LinearRegr ...

  9. CMD和Entrypoint命令使用变量的用法

    CMD 支持三种格式 CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式: CMD c ...

  10. 使用shell做http web接口,可以传递参数--废弃

    此文章废弃,参考另一篇 参考网址: https://me.jinchuang.org/archives/114.html https://www.cnblogs.com/jinchuang/p/142 ...