`
shinesuo
  • 浏览: 156795 次
  • 性别: Icon_minigender_1
  • 来自: 宇宙
社区版块
存档分类
最新评论

Spring Boot 源码分析 —— Condition

 
阅读更多

1. 概述

在前面的文章,我们已经看过 Spring Boot 如何实现自动配置的功能,但是,实际场景下,这显然不够。为什么呢?因为每个框架的配置,需要满足一定的条件,才应该进行自动配置。这时候,我们很自然就可以想到 Spring Boot 的 Condition 功能。不过呢,Condition 功能并不是 Spring Boot 所独有,而是在 Spring Framework 中就已经提供了。那么,究竟是什么样的关系呢,我们在 「2. Condition 演进史」 来瞅瞅。

2. Condition 演进史

2.1 Profile 的出场

在 Spring3.1 的版本,为了满足不同环境注册不同的 Bean ,引入了 @Profile 注解。例如:

@Configuration
public class DataSourceConfiguration {

@Bean
@Profile("DEV")
public DataSource devDataSource() {
// ... 单机 MySQL
}

@Bean
@Profile("PROD")
public DataSource prodDataSource() {
// ... 集群 MySQL
}

}
  • 在测试环境下,我们注册单机 MySQL 的 DataSource Bean 。
  • 在生产环境下,我们注册集群 MySQL 的 DataSource Bean 。

org.springframework.context.annotation.@Profile ,代码如下:

// Profile.java

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {

/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();

}
  • 这是 Spring5 版本的 @Profile 注解的代码。它已经是经过 Condition 改造的实现。详细的,我们放在 「2.2 Condition」 
  • 让我们在来看一眼 Spring3 版本的 @Profile 注解的代码。如下:

    // Profile.java

    @Retention(RetentionPolicy.RUNTIME)
    @Target({
    ANNOTATION_TYPE, // @Profile may be used as a meta-annotation
    TYPE // In conjunction with @Component and its derivatives
    })
    public @interface Profile {

    static final String CANDIDATE_PROFILES_ATTRIB_NAME = "value";

    String[] value();
    }
    • 可以大体猜出,此时并没有将 Profile 作为 Condition 的一种情况。

2.2 Condition 的出现

在 Spring4 的版本,正式出现 Condition 功能,体现在 org.springframework.context.annotation.Condition 接口,代码如下:

// Condition.java

@FunctionalInterface
public interface Condition {

boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}
  • 很简洁的一个接口,只有一个 #matches(...) 方法,用于判断是佛匹配。从参数中就可以看出,它是和注解配合,而这个注解便是 @Conditional 

org.springframework.context.annotation.@Conditional 注解,也是在 Spring4 的版本,一起出现。代码如下:

// Conditional.java

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

Class<? extends Condition>[] value();

}
  • 可以注解在方法、或者在上,表示需要满足的条件(Condition)。
  •  「2.1 Profile 的出现」 小节中,我们已经看到 @Profile 上,有 @Conditional(ProfileCondition.class) 的注解,表示使用 org.springframework.context.annotation.ProfileCondition 作为条件。
  • 当然,我们也可以直接在 Configuration 类上使用。例如:

    @Configuration
    public class TestConfiguration {

    @Bean
    @Conditional(XXXCondition.class)
    public Object xxxObject() {
    return new Object();
    }

    }
    • 即,创建 #xxxObject() 方法对应的 Bean 对象,需要满足 XXXCondition 条件。

在 Spring5 中,艿艿整理了下目前提供的 Condition 实现类,如下图:Condition 实现类

  • 显然,默认提供的 Condition 实现类非常少。

2.3 SpringBootCondition 的进击

为了满足更加丰富的 Condition(条件)的需要,Spring Boot 进一步拓展了更多的实现类,如下图所示:

Spring Boot Condition 实现类

  • org.springframework.boot.autoconfigure.condition.SpringBootCondition ,是 Spring Boot 实现 Condition 的抽象类,且是 Spring Boot 所有 Condition 实现类的基类。
  • 分别对应如下注解:
    • @ConditionalOnBean:当容器里有指定 Bean 的条件下。
    • @ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下。
    • @ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean 。
    • @ConditionalOnClass:当类路径下有指定类的条件下。
    • @ConditionalOnMissingClass:当类路径下没有指定类的条件下。
    • @ConditionalOnProperty:指定的属性是否有指定的值
    • @ConditionalOnResource:类路径是否有指定的值
    • @ConditionalOnExpression:基于 SpEL 表达式作为判断条件。
    • @ConditionalOnJava:基于 Java 版本作为判断条件
    • @ConditionalOnJndi:在 JNDI 存在的条件下差在指定的位置
    • @ConditionalOnNotWebApplication:当前项目不是 Web 项目的条件下
    • @ConditionalOnWebApplication:当前项目是 Web项 目的条件下。

2.4 小结

到了此处,我们基本算是理清了 Condition 的整个演进构成:

  • @Profile 注解,在 Spring3.1 提出,可以作为是 Condition 的雏形。
  • Condition 接口,在 Spring4 提出,是 Condition 的正式出现。
  • SpringCondition 抽象类,在 Spring Boot 实现,是对 Condition 进一步拓展。

下面,我们就正式开始撸 Condition 相关的源码落。

3. Condition 如何生效?

在上面的文章中,我们已经看到,@Conditional 注解,可以添加:

  • 类级别上
  • 方法级别上

添加到注解上,相当于添加到类级别或者方法级别上。

并且,一般情况下我们和配置类(Configuration)一起使用,但是实际上,我们也可以添加到普通的 Bean 类上。例如:

// DemoController.java

@Controller
@RequestMapping("/demo")
@Conditional(TestCondition.class)
public class DemoController {

@ResponseBody
@RequestMapping("/hello")
public String hello() {
return "world";
}

}

那么,究竟 Condition 是如何生效的呢?分成两种情况:

  • 方式一,配置类。添加到配置类(Configuration)上面。
  • 方式二,创建 Bean 对象。添加到配置类(Configuration)、或者 Bean Class 的上面。

    本质上,方式二上的两种,都是创建 Bean 对象,所以统一处理方式即可。

假设,我们在 TestConfiguration 这个示例下进行测试,看看具体的调用链。代码如下:

// TestConfiguration.java

@Configuration
@Conditional(TestCondition.class) // 艿艿自己编写的 Condition 实现类,方式测试调试
public class TestConfiguration {

@Bean
@Conditional(TestCondition.class)
public Object testObject() {
return new Object();
}

}

// TestCondition.java
public class TestCondition implements Condition {

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true;
}

}

本小节,不会讲特别细的源码。

3.1 方式一:配置类

在 TestCondition 的 #matches(...) 方法中,打个断点。看看方式一情况下的具体的表现。如下图所示:调用栈

  • 通过调用 Condition#matches(...) 方法,判断该是否匹配。如果不匹配,内部所有方法,都无法创建 Bean 对象。

3.2 方式二:创建 Bean 对象

在 TestCondition 的 #matches(...) 方法中,打个断点。看看方式二情况下的具体的表现。如下图所示:调用栈

  • 通过调用 Condition#matches(...) 方法,判断是否匹配。如果吧匹配,则不从该方法加载 BeanDefinition 。这样,就不会创建对应的 Bean 对象了。

3.3 小结

至此,我们已经看到 Condition 如何生效。还是相对比较简单的。

下面,我们一起来看看 SpringBootCondition 如何实现它的进击。

4. ProfileCondition

艿艿:先插播下 ProfileCondition 的实现代码。

org.springframework.context.annotation.ProfileCondition ,实现 Condition 接口,给 @Profile 使用的 Condition 实现类。代码如下:

// ProfileCondition.java

class ProfileCondition implements Condition {

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获得 @Profile 注解的属性
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
// 如果非空,进行判断
if (attrs != null) {
// 遍历所有 @Profile 的 value 属性
for (Object value : attrs.get("value")) {
// 判断 environment 有符合的 Profile ,则返回 true ,表示匹配
if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
return true;
}
}
// 如果没有,则返回 false
return false;
}
// 如果为空,就表示满足条件
return true;
}

}
  • 核心逻辑,获得 @Profile  value 属性,和 environment 是否有匹配的。如果有,则表示匹配。

5. SpringBootCondition

org.springframework.boot.autoconfigure.condition.SpringBootCondition ,实现 Condition 接口,Spring Boot Condition 的抽象基类,主要用于提供相应的日志,帮助开发者判断哪些被进行加载。如下是其上的类注释:

/**
* Base of all {@link Condition} implementations used with Spring Boot. Provides sensible
* logging to help the user diagnose what classes are loaded.
*/

5.1 matches

实现 #matches(ConditionContext context, AnnotatedTypeMetadata metadata) 方法,实现匹配逻辑。代码如下:

// SpringBootCondition.java

@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// <1> 获得注解的是方法名还是类名
String classOrMethodName = getClassOrMethodName(metadata);
try {
// <2> 条件匹配结果
ConditionOutcome outcome = getMatchOutcome(context, metadata);
// <3> 打印结果
logOutcome(classOrMethodName, outcome);
// <4> 记录
recordEvaluation(context, classOrMethodName, outcome);
// <5> 返回是否匹配
return outcome.isMatch();
} catch (NoClassDefFoundError ex) {
throw new IllegalStateException(
"Could not evaluate condition on " + classOrMethodName + " due to "
+ ex.getMessage() + " not "
+ "found. Make sure your own configuration does not rely on "
+ "that class. This can also happen if you are "
+ "@ComponentScanning a springframework package (e.g. if you "
+ "put a @ComponentScan in the default package by mistake)",
ex);
} catch (RuntimeException ex) {
throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
}
}
  • <1> 处,调用 #getClassOrMethodName(AnnotatedTypeMetadata metadata) 方法,获得注解的是方法名还是类名。代码如下:
    // SpringBootCondition.java

    private static String getClassOrMethodName(AnnotatedTypeMetadata metadata) {
    // 类
    if (metadata instanceof ClassMetadata) {
    ClassMetadata classMetadata = (ClassMetadata) metadata;
    return classMetadata.getClassName();
    }
    // 方法
    MethodMetadata methodMetadata = (MethodMetadata) metadata;
    return methodMetadata.getDeclaringClassName() + "#" + methodMetadata.getMethodName();
    }
  • <2> 处,调用 #getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) 抽象方法,执行匹配,返回匹配结果。这是一个抽象方法,由子类进行实现。
  • <3> 处,调用 #logOutcome(String classOrMethodName, ConditionOutcome outcome) 方法,打印结果日志。代码如下:

    // SpringBootCondition.java

    protected final void logOutcome(String classOrMethodName, ConditionOutcome outcome) {
    if (this.logger.isTraceEnabled()) {
    this.logger.trace(getLogMessage(classOrMethodName, outcome));
    }
    }

    private StringBuilder getLogMessage(String classOrMethodName, ConditionOutcome outcome) {
    StringBuilder message = new StringBuilder();
    message.append("Condition ");
    message.append(ClassUtils.getShortName(getClass()));
    message.append(" on ");
    message.append(classOrMethodName);
    message.append(outcome.isMatch() ? " matched" : " did not match");
    if (StringUtils.hasLength(outcome.getMessage())) {
    message.append(" due to ");
    message.append(outcome.getMessage());
    }
    return message;
    }
  • <4> 处,调用 #recordEvaluation(ConditionContext context, String classOrMethodName, ConditionOutcome outcome) 方法,记录到 ConditionEvaluationReport 。代码如下:

    // SpringBootCondition.java

    private void recordEvaluation(ConditionContext context, String classOrMethodName, ConditionOutcome outcome) {
    if (context.getBeanFactory() != null) {
    ConditionEvaluationReport.get(context.getBeanFactory()).recordConditionEvaluation(classOrMethodName, this, outcome);
    }
    }
  • <5> 处,返回是否匹配。

5.2 anyMatches

#anyMatches(ConditionContext context, AnnotatedTypeMetadata metadata, Condition... conditions) 方法,判断是否匹配指定的 Condition 们中的任一一个。代码如下:

艿艿:总感觉这个方法,应该是个静态方法才合适。所以,胖友即酱油看看即可。

// SpringBootCondition.java

protected final boolean anyMatches(ConditionContext context, AnnotatedTypeMetadata metadata, Condition... conditions) {
// 遍历 Condition
for (Condition condition : conditions) {
// 执行匹配
if (matches(context, metadata, condition)) {
return true;
}
}
return false;
}
  • 遍历 conditions 数组,调用 #matches(ConditionContext context, AnnotatedTypeMetadata metadata, Condition condition) 方法,执行匹配。代码如下:
    // SpringBootCondition.java

    protected final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata, Condition condition) {
    // 如果是 SpringBootCondition 类型,执行 SpringBootCondition 的直接匹配方法(无需日志)
    if (condition instanceof SpringBootCondition) {
    return ((SpringBootCondition) condition).getMatchOutcome(context, metadata).isMatch();
    }
    return condition.matches(context, metadata);
    }

总的来说,SpringBootCondition 这个类,没啥好说,重点还是在子类。

6. SpringBootCondition 的实现类

我们在回忆下,SpringBootCondition 的实现类,主要如下图:Spring Boot Condition 实现类

显然,我们不会去看每一个类的 SpringBootCondition 的实现类。所以呢,艿艿也不会每个类都写。

旁白君:偷懒都偷的如此猥琐,哈哈哈哈。

6.1 OnPropertyCondition

艿艿:来来来,先看一个容易的(捏个软柿子)。

org.springframework.boot.autoconfigure.condition.OnPropertyCondition ,继承 SpringBootCondition 抽象类,给 @ConditionalOnProperty 使用的 Condition 实现类。

如果胖友不熟悉 @ConditionalOnProperty 注解,赶紧打开 《@ConditionalOnProperty 来控制 Configuration 是否生效》 学习 3 分钟~不能再多了。

6.1.1 getMatchOutcome

#getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) 方法,获得匹配结果。代码如下:

// OnPropertyCondition.java

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
// <1> 获得 @ConditionalOnProperty 注解的属性
List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName()));
// <2> 存储匹配和不匹配的结果消息结果
List<ConditionMessage> noMatch = new ArrayList<>();
List<ConditionMessage> match = new ArrayList<>();
// <3> 遍历 annotationAttributes 属性数组,逐个判断是否匹配,并添加到结果
for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
}
// <4.1> 如果有不匹配的,则返回不匹配
if (!noMatch.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
}
// <4.2> 如果都匹配,则返回匹配
return ConditionOutcome.match(ConditionMessage.of(match));
}
  • <1> 处,调用 #annotationAttributesFromMultiValueMap(MultiValueMap<String, Object> multiValueMap) 方法,获得 @ConditionalOnProperty 注解的属性。代码如下:

    // OnPropertyCondition.java

    private List<AnnotationAttributes> annotationAttributesFromMultiValueMap(MultiValueMap<String, Object> multiValueMap) {
    List<Map<String, Object>> maps = new ArrayList<>();
    multiValueMap.forEach((key, value) -> {
    for (int i = 0; i < value.size(); i++) {
    Map<String, Object> map;
    if (i < maps.size()) {
    map = maps.get(i);
    } else {
    map = new HashMap<>();
    maps.add(map);
    }
    map.put(key, value.get(i));
    }
    });
    List<AnnotationAttributes> annotationAttributes = new ArrayList<>(maps.size());
    for (Map<String, Object> map : maps) {
    annotationAttributes.add(AnnotationAttributes.fromMap(map));
    }
    return annotationAttributes;
    }
    • 懒的看整个代码实现的过程,可以直接看最终执行的结果图:`annotationAttributes`
  • <2> 处,存储匹配和不匹配的结果消息结果。

  • <3> 处,遍历 annotationAttributes 属性数组,逐个调用 #determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) 方法,判断是否匹配,并添加到结果。详细解析,见 「6.1.2 determineOutcome」 
  • <4.1> 处,如果有不匹配的,则返回不匹配。返回结果示例如下:不匹配
  • <4.2> 处,如果都匹配,则返回匹配。返回结果示例如下:匹配

6.1.2 determineOutcome

#determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) 方法,判断是否匹配。代码如下:

// OnPropertyCondition.java

private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) {
// <1> 解析成 Spec 对象
Spec spec = new Spec(annotationAttributes);
// <2> 创建结果数组
List<String> missingProperties = new ArrayList<>();
List<String> nonMatchingProperties = new ArrayList<>();
// <3> 收集是否不匹配的信息,到 missingProperties、nonMatchingProperties 中
spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
// <4.1> 如果有属性缺失,则返回不匹配
if (!missingProperties.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
.didNotFind("property", "properties").items(Style.QUOTE, missingProperties));
}
// <4.2> 如果有属性不匹配,则返回不匹配
if (!nonMatchingProperties.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
.found("different value in property", "different value in properties").items(Style.QUOTE, nonMatchingProperties));
}
// <4.3> 返回匹配
return ConditionOutcome.match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec).because("matched"));
}
  • <1> 处,解析成 Spec 对象。Spec 是 OnPropertyCondition 的内部静态类。代码如下:

    // OnPropertyCondition#Spec.java

    private static class Spec {

    /**
    * 属性前缀
    */
    private final String prefix;
    /**
    * 是否有指定值
    */
    private final String havingValue;
    /**
    * 属性名
    */
    private final String[] names;
    /**
    * 如果属性不存在,是否认为是匹配的。
    *
    * 如果为 false 时,就认为属性丢失,即不匹配。
    */
    private final boolean matchIfMissing;

    Spec(AnnotationAttributes annotationAttributes) {
    String prefix = annotationAttributes.getString("prefix").trim();
    if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) {
    prefix = prefix + ".";
    }
    this.prefix = prefix;
    this.havingValue = annotationAttributes.getString("havingValue");
    this.names = getNames(annotationAttributes);
    this.matchIfMissing = annotationAttributes.getBoolean("matchIfMissing");
    }

    // 从 value 或者 name 属性种,获得值
    private String[] getNames(Map<String, Object> annotationAttributes) {
    String[] value = (String[]) annotationAttributes.get("value");
    String[] name = (String[]) annotationAttributes.get("name");
    Assert.state(value.length > 0 || name.length > 0, "The name or value attribute of @ConditionalOnProperty must be specified");
    Assert.state(value.length == 0 || name.length == 0, "The name and value attributes of @ConditionalOnProperty are exclusive");
    return (value.length > 0) ? value : name;
    }

    // ... 省略其它方法先~
    }
  • <2> 处,创建结果数组。

  • <3> 处,调用 Spec#collectProperties(PropertyResolver resolver, List<String> missing, List<String> nonMatching) 方法,收集是否不匹配的信息,到 missingPropertiesnonMatchingProperties 中。代码如下:

    // OnPropertyCondition#Spec.java
    private void collectProperties(PropertyResolver resolver, List<String> missing, List<String> nonMatching) {
    // 遍历 names 数组
    for (String name : this.names) {
    // 获得完整的 key
    String key = this.prefix + name;
    // 如果存在指定属性
    if (resolver.containsProperty(key)) {
    // 匹配值是否匹配
    if (!isMatch(resolver.getProperty(key), this.havingValue)) {
    nonMatching.add(name);
    }
    // 如果不存在指定属性
    } else {
    // 如果属性为空,并且 matchIfMissing 为 false ,则添加到 missing 中
    if (!this.matchIfMissing) {
    missing.add(name);
    }
    }
    }
    }

    private boolean isMatch(String value, String requiredValue) {
    // 如果 requiredValue 非空,则进行匹配
    if (StringUtils.hasLength(requiredValue)) {
    return requiredValue.equalsIgnoreCase(value);
    }
    // 如果 requiredValue 为空,要求值不为 false
    return !"false".equalsIgnoreCase(value);
    }
    • 匹配的逻辑,胖友自己瞅瞅。可能比较绕的逻辑是,matchIfMissing 那块,也就看两眼就明白。
  • <4.1> 处,如果有属性缺失,则返回不匹配。
  • <4.2> 处,如果有属性不匹配,则返回不匹配。
  • <4.3> 处,返回匹配。

6.2 其它实现类

SpringBootCondition 的其它实现类,胖友可以自己去瞅瞅啦。当然,有部分实现类,我们会在 「8. FilteringSpringBootCondition」 分享。

7. AutoConfigurationImportFilter

 《精尽 Spring Boot 源码分析 —— 自动配置》 一文中,我们埋了一个 AutoConfigurationImportSelector#filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) 方法的坑,没有进行详细解析。所以呢,这一节我们将填掉这个坑。

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter 接口,用于过滤掉无需自动引入的自动配置类(Configuration)。正如其类上的注释:

// AutoConfigurationImportFilter.java

/**
* Filter that can be registered in {@code spring.factories} to limit the
* auto-configuration classes considered. This interface is designed to allow fast removal
* of auto-configuration classes before their bytecode is even read.
* @since 1.5.0
*/
  • 重点是 "fast removal of auto-configuration classes before their bytecode is even read" 。因为自动配置类可能会很多,如果无需使用,而将字节码读取到内存中,这个是一种浪费。

AutoConfigurationImportFilter 的代码如下:

// AutoConfigurationImportFilter.java

@FunctionalInterface
public interface AutoConfigurationImportFilter {

/**
* Apply the filter to the given auto-configuration class candidates.
* @param autoConfigurationClasses the auto-configuration classes being considered.
* This array may contain {@code null} elements. Implementations should not change the
* values in this array.
* @param autoConfigurationMetadata access to the meta-data generated by the
* auto-configure annotation processor
* @return a boolean array indicating which of the auto-configuration classes should
* be imported. The returned array must be the same size as the incoming
* {@code autoConfigurationClasses} parameter. Entries containing {@code false} will
* not be imported.
*/
boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata);

}
  • 将传入的 autoConfigurationClasses 配置类们,根据 autoConfigurationMetadata 的元数据(主要是注解信息),进行匹配,判断是否需要引入,然后返回的 boolean[] 结果。
  • 并且,boolean[] 结果和 autoConfigurationClasses 配置类们是一一对应的关系噢。假设 autoConfigurationClasses[0] 对应的 boolean[0]  false ,表示无需引入,反之则需要引入。

7.1 AutoConfigurationImportFilter 类图

AutoConfigurationImportFilter 的子类如下图所示:AutoConfigurationImportFilter 类图

相关推荐

    无人机轨迹规划中的内外安全策略及其Matlab实现

    内容概要:本文详细探讨了无人机轨迹规划中的内外安全策略。外部安全主要依靠RRT(快速扩展随机树)算法,在未知环境中随机探索路径,避免障碍物。内部安全则关注无人机电机的工作状态,通过序列二次规划(SQP)优化轨迹,确保电机推力在合理范围内,避免过载。两者结合,形成了一种高效的无人机轨迹规划方法。文中还提供了具体的Matlab代码实现,展示了如何将这两种安全策略融合在一起,以提高无人机的安全性和能效。 适合人群:从事无人机研究、开发的技术人员,尤其是对轨迹规划感兴趣的工程师。 使用场景及目标:适用于需要在复杂环境中进行高效、安全飞行的无人机项目。目标是通过合理的算法设计,使无人机能够在避开障碍物的同时,保持电机在最佳工作状态,从而延长电池寿命,降低故障率。 其他说明:文中提到的实际案例和实验数据进一步验证了所提出方法的有效性。同时,作者强调了在实际应用中需要注意的一些细节问题,如平面度特性的正确使用、轨迹离散化的粒度选择以及电机模型的理想化处理等。

    基于物质点法的边坡滑坡破坏模拟:Anura3d软件的应用与案例分析

    内容概要:本文详细介绍了利用物质点法(Material Point Method, MPM)和Anura3D软件进行边坡滑坡破坏模拟的过程。文章首先解释了MPM的基本原理,即通过物质点跟踪材料属性并结合欧拉网格进行计算,适用于大变形和破坏问题。接着,作者分享了一个具体的案例——台阶边坡在降雨或地震作用下的滑坡模拟,涵盖了前处理建模、数值模拟计算和后处理分析三个主要步骤。前处理中使用Gid 11进行几何建模和网格划分,定义材料属性;数值模拟中设定边界条件和初始条件,采用显式积分方法;后处理中使用ParaView进行数据可视化,生成位移云图、应力云图等。此外,还讨论了一些常见问题和技术细节,如接触算法、时间步长的选择等。 适合人群:对数值模拟特别是物质点法感兴趣的地质工程研究人员、岩土工程师及相关领域的学生。 使用场景及目标:①理解和掌握物质点法的基本原理及其在边坡滑坡模拟中的应用;②学习使用Anura3D和ParaView进行数值模拟和结果可视化的具体操作;③探索滑坡破坏过程中的微观机制和宏观表现。 阅读建议:本文不仅提供了详细的理论介绍和技术指导,还包括了许多实用的操作技巧和注意事项。建议读者在实践中逐步尝试文中提到的方法和参数设置,同时关注可能出现的问题及其解决方案。

    Altium Designer 25.5.2 Build 35 (x64)

    Altium Designer 25.5.2 Build 35 (x64) ,不知道更新了什么,不好下载就先下载到雷盘再下载

    基于ssm的大学生创新创业项目管理系统(源码+数据库)171

    基于ssm的大学生创新创业项目管理系统:前端 jsp、jquery,后端 maven、springmvc、spring、mybatis;角色分为管理员、学生;集成学院、专业、班级管理,创新计划,项目管理等功能于一体的系统。 ## 功能介绍 - 学院管理:学院信息的增删改查,按名称模糊搜索查询,学院简介信息支持富文本编辑 - 专业管理:专业信息的增删改查,按名称模糊搜索,按学院选择查询 - 班级管理:班级信息的增删改查,,按名称模糊搜索,按学院、专业选择查询 - 用户管理:用户信息的增删改查,多条件搜索查询 - 创新计划:创新计划信息增删改查,详情信息支持富文本编辑,上传附件,查看申请 - 项目管理:项目列表查询,申请审核 ## 环境 - <b>IntelliJ IDEA 2021.3</b> - <b>Mysql 5.7.26</b> - <b>Tomcat 7.0.73</b> - <b>JDK 1.8</b>

    石油工程中基于深度学习的FracPredictor裂缝建模与压裂模拟技术解析

    内容概要:本文详细介绍了FracPredictor这一基于深度学习的裂缝预测工具及其应用。首先探讨了数据处理部分,如利用滑窗处理时序+空间特征混合体的方法,以及如何将岩石力学数据转换为适合神经网络的格式。接着深入剖析了模型架构,包括时空双流网络、注意力机制用于跨模态融合、HybridResBlock自定义层等创新设计。此外,文章还分享了训练技巧,如渐进式学习率衰减、CosineAnnealingWarmRestarts调度器的应用。对于可视化方面,则推荐使用PyVista进行三维渲染,以直观展示裂缝扩展过程。文中还提到了一些实用的小技巧,如数据预处理中的自动标准化、配置文件参数调整、以及针对特定地质条件的优化措施。最后,通过多个实际案例展示了FracPredictor在提高预测准确性、降低计算成本方面的优势。 适合人群:从事石油工程、地质勘探领域的研究人员和技术人员,尤其是对裂缝建模与压裂模拟感兴趣的从业者。 使用场景及目标:适用于需要高效、精准地进行裂缝预测和压裂模拟的工程项目。主要目标是帮助用户掌握FracPredictor的工作原理,学会从数据准备到结果可视化的完整流程,从而优化压裂方案,减少工程风险。 其他说明:文章不仅提供了详细的代码示例,还附带了丰富的实战经验和注意事项,有助于读者更好地理解和应用这项新技术。

    基于ssm的二手汽车销售管理系统(源码+数据库+万字文档)107

    基于ssm的二手汽车销售管理系统:前端 jsp、jquery,后端 maven、springmvc、spring、mybatis,角色分为经理和员工;集成品牌管理、客户管理、销售管理、库存管理、收购管理等功能于一体的系统。 ## 功能介绍 - 品牌管理:品牌信息的增删改查,热门品牌列表,销量比较好的顺序排行 - 客户管理:客户资料信息的增删改查 - 销售管理:车辆销售登记,列表查询,付款 - 库存管理:车辆库存情况查询,下订单 - 收购管理:收购评估,收购订单查询 - 统计管理:财务状况(柱状图),下载excel表格 - 员工管理:员工信息的增删改查,账号激活操作 - 聊天管理:聊天内容查看,指定人员发送聊天内容 ## 环境 - <b>IntelliJ IDEA 2009.3</b> - <b>Mysql 5.7.26</b> - <b>Tomcat 7.0.73</b> - <b>JDK 1.8</b>

    光伏MPPT中改进的樽海鞘群算法:领导更新与局部全局开发能力提升

    内容概要:本文详细介绍了改进的樽海鞘群算法(SSA)在光伏最大功率点跟踪(MPPT)中的应用。首先探讨了领导者更新机制的改进,引入反向学习思想,使算法能够更好地进行全局和局部搜索。其次,对追随者更新公式进行了优化,使其更倾向于适应度较高的个体,从而提高搜索效率。此外,增加了光照突变重启功能,确保算法在光照条件突变时仍能快速响应并重新定位最大功率点。通过仿真实验表明,改进后的算法显著提升了收敛速度和稳定性,尤其在光照突变情况下表现优异。 适合人群:从事光伏系统研究和技术开发的专业人士,尤其是对优化算法感兴趣的科研人员和工程师。 使用场景及目标:适用于光伏系统的最大功率点跟踪(MPPT)优化,旨在提高光伏系统的效率和稳定性,特别是在光照条件不稳定的情况下。 其他说明:文中提供了详细的代码示例和参数调优建议,有助于实际工程应用中的实施和调试。

    基于ssm的学生选课管理系统(源码+数据库)146

    基于ssm的学生选课管理系统:前端 html、jquery,后端 springmvc、spring、mybatis;角色分为:老师、学生;集成课程管理、上传成绩、选课等功能于一体的系统。 ## 功能介绍 - 基本功能:登录,注册,退出 - 我的课程:教师可以增加自己的课程供学生选择,可以查看当前课程有哪些学生选择 - 上传成绩:课程列表展示,双击课程可以进入该课程下面上传学生成绩 - 个人信息:个人信息查询与修改,密码修改 - 系统消息:系统消息列表查询 - 我的选课:选课信息查询,课程退选 - 网上选课:课程列表查询,关键词搜索,课程报名 - 往期课程:课程列表查询,导出成绩为word文档 ## 环境 - <b>IntelliJ IDEA 2021.3</b> - <b>Mysql 5.7.26</b> - <b>Tomcat 7.0.73</b> - <b>JDK 1.8</b>

    基于springboot+vue的物业管理系统(源码+数据库)129

    基于springboot+vue的物业管理系统:前端 vue、elementui,后端 maven、springmvc、spring、mybatis,角色分为管理员、用户;集成小区管理、楼盘管理、保障管理等功能于一体的系统。 ## 功能介绍 - 小区管理:小区基本信息查询与修改,周边设施增删改查,物业公告信息增删改查,支持模糊查询 - 楼宇管理:小区楼宇信息增删改查 - 房间管理:房间信息的增删改查,导出excel - 报修管理:报修信息列表查询,删除,审核 - 投诉管理:投诉信息列表查询,删除,审核 - 收费类型管理:收费类型信息的增删改查,关键词搜索 - 用户管理:物业人员信息的增删改查,住户信息的增删改查 - 定时任务管理:定时任务执行特定任务或数据处理任务 ## 环境 - <b>IntelliJ IDEA 2021.3</b> - <b>Mysql 5.7.26</b> - <b>Node 14.14.0</b> - <b>JDK 1.8</b>

    智能办公基于Dify和飞书知识库的AI智能问答助手构建:提升团队工作效率的企业级应用方案

    内容概要:本文介绍如何利用Dify和飞书知识库构建智能问答助手,解决团队高效提取信息的难题。Dify作为一个开源AI平台,适合构建自然语言处理应用;飞书提供API接口,方便获取知识库内容。通过结合两者,可创建24/7在线的智能问答助手,减少人工搜索文档时间。文中详细描述了实现步骤:准备开发环境(获取飞书API权限、注册使用Dify、搭建环境)、获取飞书知识库内容(调用API、数据处理)、使用Dify进行智能问答(训练或使用现成模型、调用API)、构建问答服务(搭建后端服务、部署服务、配置飞书机器人)以及持续优化模型和知识库内容。 适合人群:对AI技术和企业协作工具有一定了解的企业IT人员、开发者。 使用场景及目标:①为企业内部提供全天候智能问答服务,自动回答常见问题;②减少人工搜索文档时间,提高工作效率;③通过用户提问日志优化AI模型,提升答案准确性。 阅读建议:在实践过程中,需结合实际需求调整API权限和模型训练,确保系统的实用性和高效性。同时,定期更新飞书知识库内容,保证信息的时效性和准确性。

    基于GOOSE-Transformer-LSTM的时间序列数据回归预测模型及其优化

    内容概要:本文探讨了一种结合Transformer的全局注意力机制和LSTM的短期记忆及序列处理能力的数据回归预测模型,并引入了最新提出的GOOSE优化算法。Transformer通过自注意力机制捕捉数据的全局依赖性,LSTM则专注于捕捉短期依赖关系。GOOSE算法用于优化LSTM的隐含层神经元数目,从而提高模型的泛化能力和预测精度。文中提供了多个Python代码片段,展示了模型的具体实现和优化过程。实验结果显示,该模型在电力负荷预测、商品销量预测等场景中表现出色,预测误差显著降低。 适用人群:从事机器学习、深度学习研究的专业人士,尤其是对时间序列预测感兴趣的开发者和技术爱好者。 使用场景及目标:适用于需要同时考虑长期趋势和短期波动的时间序列预测任务,如电力负荷预测、股票价格预测、商品销售预测等。目标是提高预测精度,减少预测误差,增强模型的鲁棒性和泛化能力。 其他说明:文中提到的实际应用场景包括电力负荷预测和风电功率预测,强调了模型在处理异常波动数据时的优势。此外,还讨论了一些潜在的改进方向,如引入因果卷积和MoE架构等。 标签1: Transformer 标签2: LSTM 标签3: GOOSE优化算法 标签4: 时间序列预测 标签5: 自注意力机制

    脑肿瘤实例分割医疗影像数据集.zip

    数据集介绍:脑肿瘤实例分割医疗影像数据集 一、基础信息 数据集名称:脑肿瘤实例分割医疗影像数据集 数据规模: - 训练集:803张医学影像 - 验证集:237张医学影像 - 测试集:129张医学影像 标注类别: - Brain_Tumor(脑肿瘤):包含各类脑部肿瘤病变区域标注 标注特性: - YOLO实例分割格式:通过多边形坐标点精确标注肿瘤边界 - 包含正常脑组织与肿瘤组织的对比样本 - 数据来源于临床医学影像采集系统 二、适用场景 医疗影像AI系统开发: 支持构建脑肿瘤自动识别与病灶定位系统,辅助医生进行术前规划与病灶量化分析 智能诊断工具研发: 可用于开发脑部CT/MRI影像智能分析工具,实现肿瘤位置、形态的精准三维重建 医学影像算法研究: 为深度学习在医学图像分割领域的研究提供标准测试基准,支持UNet、Mask R-CNN等算法的训练验证 放射科医师培训: 可作为教学材料帮助医师学习脑肿瘤的影像学特征识别与病灶标注规范 三、核心优势 临床精准性: - 标注结果经三甲医院放射科医师双重校验 - 涵盖胶质瘤、脑膜瘤等多种常见脑肿瘤类型 技术适配性: - 原生支持YOLOv5/v7/v8等主流实例分割框架 - 提供标准化的数据划分方案与验证指标 科研价值: - 包含完整病例的DICOM原始数据转换样本 - 标注数据呈现肿瘤异质性和边界模糊等临床特征 工程实用性: - 提供肿瘤区域面积占比等量化指标计算基准 - 支持医疗影像PACS系统直接对接的数据格式

    永磁同步电机SVPWM算法故障诊断与容错控制的Simulink建模及应用

    内容概要:本文详细介绍了如何在Simulink中构建永磁同步电机(SVPWM)算法的故障诊断与容错控制模型。首先,通过搭建电流残差观测器进行故障诊断,采用瞬态突变和累积误差双重判据,确保能够及时捕捉到IGBT故障。接着,针对故障情况,提出了重构电压矢量的方法,将故障相的电压分配给其他相,从而实现系统的容错控制。此外,文中还讨论了模型中各个模块之间的耦合关系以及一些具体的实现细节,如故障注入模块、观测器环路延迟、磁饱和参数设置等。最后,通过仿真验证了该方法的有效性,展示了故障发生后系统的快速响应能力和良好的性能恢复。 适用人群:从事电机控制研究的技术人员、研究生及以上学历的相关专业学生。 使用场景及目标:适用于需要提高永磁同步电机控制系统可靠性的应用场景,特别是在工业自动化、电动汽车等领域。目标是在IGBT故障情况下,确保电机系统能够在短时间内恢复正常运行,减少故障带来的负面影响。 其他说明:文中提供了详细的MATLAB/Simulink代码片段,帮助读者更好地理解和实现相关算法。同时,强调了一些容易忽视的关键点,如IGBT模型的选择、采样周期的设定等,有助于提高仿真的准确性。

    高速公路联网收费系统优化升级:收费站标准化接口规范解析

    高速公路联网收费系统优化升级收费站标准化专项试点技术方案2024.11

    基于Matlab/Simulink的锂离子电池SOC估计:EKF与UKF结合二阶RC模型的应用

    内容概要:本文详细介绍了利用Matlab/Simulink实现锂离子电池荷电状态(SOC)估计的方法,特别关注扩展卡尔曼滤波(EKF)和无迹卡尔曼滤波(UKF)两种算法。首先,文章解释了二阶RC等效电路模型的基本原理及其参数设定,该模型用于描述电池内部动态行为。接着,分别阐述了EKF和UKF的工作机制,包括它们各自的状态预测和更新步骤,并给出了具体的MATLAB代码片段。此外,还讨论了这两种方法在不同工况下的表现差异以及如何选择合适的滤波器以提高估计精度。最后,强调了OCV-SOC曲线拟合质量和参数辨识的重要性,指出这是确保良好估计效果的关键因素之一。 适合人群:从事电池管理、电动汽车、储能系统等领域研究的技术人员,尤其是那些对卡尔曼滤波理论有一定了解并希望通过实际案例深入理解其应用的人士。 使用场景及目标:适用于需要精确估算锂离子电池SOC的研究项目或产品开发阶段,旨在帮助研究人员更好地掌握EKF和UKF的特点,从而为具体应用场景挑选最适合的算法。 其他说明:文中提供的代码仅为示例,实际应用时需根据具体情况调整参数配置。同时提醒读者重视电池模型本身的准确性,因为即使是最先进的滤波算法也无法弥补模型缺陷带来的误差。

    电动汽车动力系统仿真的关键技术:双向DCDC变换器与电机控制策略

    内容概要:本文详细介绍了电动汽车动力系统的仿真技术,涵盖双向DCDC变换器的能量反馈机制和支持异步电机与永磁同步电机的仿真。文中展示了多个关键控制策略,如电流环控制、最大转矩电流比(MTPA)控制、弱磁控制以及基于事件触发的协调控制。通过MATLAB、Python和C等多种编程语言的具体代码实例,解释了如何实现高效的能量管理和电机控制。此外,文章还讨论了仿真过程中遇到的实际问题及其解决方案,如电压过冲、电流振荡和系统耦合等问题。 适合人群:从事电动汽车研究与开发的技术人员、高校相关专业师生、对电动汽车动力系统感兴趣的工程师。 使用场景及目标:适用于电动汽车动力系统的设计与优化,帮助工程师理解和掌握双向DCDC变换器的工作原理及电机控制策略,提高能量利用效率,确保系统稳定性。 其他说明:文章不仅提供了详细的理论和技术背景,还分享了许多实践经验,有助于读者更好地将理论应用于实际项目中。

    实时通信RTMP协议规范详解:音视频流传输核心技术与应用场景分析

    内容概要:本文详细解析了RTMP(Real Time Messaging Protocol)协议规范,该协议由Adobe公司设计,主要用于音视频流传输,现已广泛应用于直播、点播、视频会议等场景。文章介绍了RTMP的核心设计,包括分块传输、多路复用、协议控制消息等机制,适用于低延迟、高并发的流媒体场景。文中解释了分块、消息流、字节序与对齐、时间戳等核心概念,并详细描述了RTMP的握手机制,包括三个阶段的握手过程。此外,还介绍了RTMP的数据包结构与分块格式,以及RTMP的几种变种协议(RTMPS、RTMPE、RTMPT)及其特点。最后,列举了RTMP的应用场景,如直播、点播、视频会议和远程控制等。 适合人群:从事音视频开发的技术人员,特别是对流媒体协议感兴趣的开发者和研究者。 使用场景及目标:①理解RTMP协议的工作原理和应用场景;②掌握RTMP协议的核心机制和技术细节;③学习RTMP协议的不同变种及其优缺点。 阅读建议:建议读者结合实际项目需求,重点关注RTMP协议的关键技术和应用场景,并参考提供的官方文档和实现库,以便更好地理解和应用RTMP协议。

    基于springboot+vue的网上鲜花交易管理系统(源码+数据库)177

    基于springboot+vue的网上鲜花交易管理系统:前端 vue、elementui,后端 maven、springmvc、spring、mybatis;角色分为管理员,用户;集成商家,鲜花浏览,购物车等功能于一体的系统。 ## 功能介绍 ### 用户 - 基本功能:登录,注册,退出 - 网站首页:主导航栏,轮播图,鲜花游览推荐,商家展示 - 商家:商家列表展示,按店铺名和店铺地址模糊搜索,商家详情,评论 - 鲜花:鲜花列表展示,按名称、用途、花语、店铺名模糊搜索,鲜花详情,购物车,在线结算 - 其他功能:系统公告,在线客服,鲜花结束 - 个人中心:个人信息查看与修改,我的订单查询,我的修改维护,我的收藏列表 ### 管理员 - 用户管理:用户信息的增删改查,用户也可以在前台自行注册 - 商家管理:商家信息的增删改查,查看商家评论,评论回复,评论删除 - 鲜花分类管理:分类信息的增删改查 - 鲜花管理:鲜花信息的增删改查,图片上传,查看评论,评论回复,评论删除 - 系统管理:系统公告管理,在线客服,轮播图管理 - 订单管理:用户下单后,管理员查看用户订单,发货操作 ## 环境 - <b>IntelliJ IDEA 2021.3</b> - <b>Mysql 5.7.26</b> - <b>Tomcat 7.0.73</b> - <b>Node 14.14.0</b> - <b>JDK 1.8</b>

    基于ssm+vue的高校宿舍管理系统(源码+数据库)238

    基于ssm+vue的高校宿舍管理系统:前端 vue2、element-ui,后端 maven、springmvc、spring、mybatis;角色分为管理员、宿管、学生;集成楼栋管理、宿舍安排、缴费信息、宿舍报修、宿舍检查、学生晚归等功能于一体的系统。 ## 环境-238 - <b>IntelliJ IDEA 2021.3</b> - <b>Mysql 5.7.26</b> - <b>Tomcat 7.0.73</b> - <b>Node 14.14.0</b> - <b>JDK 1.8</b>

    基于Matlab的多能源微网双层两阶段滚动优化调度模型及其应用

    内容概要:本文详细介绍了基于Matlab的多能源微网双层两阶段滚动优化调度模型。该模型采用双层框架,上层负责全局资源分配,下层负责各微网内部的成本最小化优化。通过滚动优化处理负载和可再生能源的随机性,确保系统的灵活性和鲁棒性。文中提供了详细的代码示例,涵盖从数据初始化、目标函数构建、约束条件定义到最终求解的全过程。此外,还讨论了储能管理、成本函数设计以及时间尺度衔接等关键技术点。 适合人群:从事电力系统优化、微网调度研究的技术人员和研究人员,尤其是熟悉Matlab编程并有一定优化理论基础的人群。 使用场景及目标:适用于多能源微网系统的调度优化,旨在提高能源利用效率,降低成本,增强系统的稳定性和可靠性。具体应用场景包括但不限于分布式发电系统、虚拟电厂等。 其他说明:该模型不仅实现了理论上的创新,还在实践中表现出良好的性能,能够有效应对复杂的能源供需关系。通过合理的参数配置和优化策略,可以显著提升系统的经济效益和技术水平。

Global site tag (gtag.js) - Google Analytics