@SpringBootApplication 到底是什么呢?

任何复杂事物的背后,都有一些不可再分解的基本事实。找到它们,你就找到了理解整件事的钥匙。

首先他是一个注解,我们要知道这个注解干了什么.

注解的定义

先看这个@SpringBootApplication 注解的定义

java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "nameGenerator"
    )
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

我们可以看到,这个注解上还有很多注解

复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

元注解

@Target--注解的作用位置

@Target({ElementType.TYPE}) ~一种可以应用到其他类/接口/枚举/注解上的注解

Target--- 注解作用的目标

ElementType---规定了注解作用的元素

java 复制代码
public enum ElementType {
    /** Class, interface (including annotation interface), enum, or record
     * declaration */
    TYPE, //类、接口、枚举、注解

    /** Field declaration (includes enum constants) */
    FIELD, // 属性字段

    /** Method declaration */
    METHOD,//方法

    /** Formal parameter declaration */
    PARAMETER,//参数

    /** Constructor declaration */
    CONSTRUCTOR,//构造函数

    /** Local variable declaration */
    LOCAL_VARIABLE,//局部变量

    /** Annotation interface declaration (Formerly known as an annotation type.) */
    ANNOTATION_TYPE,//注解

    /** Package declaration */
    PACKAGE,//包

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,//类参数

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE,//任何类型使用的地方

    /**
     * Module declaration.
     *
     * @since 9
     */
    MODULE, //模块上

    /**
     * Record component
     *
     * @jls 8.10.3 Record Members
     * @jls 9.7.4 Where Annotations May Appear
     *
     * @since 16
     */
    RECORD_COMPONENT;//record组件上
}

我们看到@SpringBootApplication 上的 @Target({ElementType.TYPE}) ,所以他只能作用于 类上

@Retention注解的生命周期

@Retention(RetentionPolicy.RUNTIME) --决定注解的生命周期

java 复制代码
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE, // 只作用于源码,编译后丢弃

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,//编译进class文件,但运行时不加载

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME //运行时存活,可以反射读取
}

@RetentionPolicy (RetentionPolicy.SOURCE) 举个例子

还有lombok的@Data

作用就是编译时生成.class,他的使命就完成了

@RetentionPolicy (RetentionPolicy.CLASS) 举个例子

编译进class文件,但是JVM不加载

@RetentionPolicy (RetentionPolicy.RUNTIME)

这个最常见了

spring boot里最常见的就是这类

java 复制代码
@SpringBootApplication
@Controller
@Service
@Autowired

这些注解在spring 运行时通过反射读取

@Documented

这个跟API文档生成有关,

加了这个注解,那么生成的API文档可以看到

@Inherited

注解可以被子类继承,即父类有这个注解,子类也会自动给有这个注解

java 复制代码
@Inherited
@MyAnnotation
public class Parent {
}

// 子类自动继承 @MyAnnotation
public class Child extends Parent {
}

下面的这三个注解是springboot 自动装配实现的精髓

springboot 自动装配原理

@SpringBootConfiguration

java 复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

该注解作用域类上,运行时生效,是spring的配置功能

我们可以看到这个注解本质就是一个被spring管理的bean

java 复制代码
@Configuration(value = "gavin")
public class GavinConfig {




    public GavinConfig createConfig(){
        System.out.println("-------------输出一段文字------------");
        return new GavinConfig( );
    }


}

所以加上这个注解后的类 其实就相当于

下面这样

java 复制代码
@Component(value = "gavin")
public class GavinConfig {
    public GavinConfig createConfig(){
        System.out.println("-------------输出一段文字------------");
        return new GavinConfig( );
    }


}

@EnableAutoConfiguration

java 复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

可以看到这个注解上多了 @AutoConfigurationPackage 跟@Import

@AutoConfigurationPackage注解 自动配置包,spring 会自动锁定启动类所在的包

java 复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

这个注解里有一个 @Import注解,是springboot 自动装配的核心

java 复制代码
    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }
// 拿到启动类的包名
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }
//拿到包名之后,放进一个set集合里
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImports(metadata));
        }
    }

可以看到一个静态类, registerBeanDefinitions 方法作用~把启动类所在的包名,自动注册到 Spring 容器里,供第三方框架使用!

@Import({AutoConfigurationImportSelector.class})

这个注解作用是去classpath下扫描所有自动配置类,然后批量导入Spring 容器

java 复制代码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
    private static final String[] NO_IMPORTS = new String[0];
    private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
    private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
    private ConfigurableListableBeanFactory beanFactory;
    private Environment environment;
    private ClassLoader beanClassLoader;
    private ResourceLoader resourceLoader;
    private ConfigurationClassFilter configurationClassFilter;

    public AutoConfigurationImportSelector() {
    }
// 哪些应该导入spring 容器
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

    public Predicate<String> getExclusionFilter() {
        return this::shouldExclude;
    }
//排除的类
    private boolean shouldExclude(String configurationClassName) {
        return this.getConfigurationClassFilter().filter(Collections.singletonList(configurationClassName)).isEmpty();
    }
//自动装配核心方法
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    //
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {

            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            
            return new AutoConfigurationEntry(configurations, exclusions);
        }
    }

    public Class<? extends DeferredImportSelector.Group> getImportGroup() {
        return AutoConfigurationGroup.class;
    }

    protected boolean isEnabled(AnnotationMetadata metadata) {
        return this.getClass() == AutoConfigurationImportSelector.class ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true;
    }

    protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
        String name = this.getAnnotationClass().getName();
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
        Assert.notNull(attributes, () -> {
            return "No auto-configuration attributes found. Is " + metadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?";
        });
        return attributes;
    }

    protected Class<?> getAnnotationClass() {
        return EnableAutoConfiguration.class;
    }
// 去读取自动化配置类,加载到spring容器里
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
    }

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

    private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
        List<String> invalidExcludes = new ArrayList(exclusions.size());
        Iterator var4 = exclusions.iterator();

        while(var4.hasNext()) {
            String exclusion = (String)var4.next();
            if (ClassUtils.isPresent(exclusion, this.getClass().getClassLoader()) && !configurations.contains(exclusion)) {
                invalidExcludes.add(exclusion);
            }
        }

        if (!invalidExcludes.isEmpty()) {
            this.handleInvalidExcludes(invalidExcludes);
        }

    }

    protected void handleInvalidExcludes(List<String> invalidExcludes) {
        StringBuilder message = new StringBuilder();
        Iterator var3 = invalidExcludes.iterator();

        while(var3.hasNext()) {
            String exclude = (String)var3.next();
            message.append("\t- ").append(exclude).append(String.format("%n"));
        }

        throw new IllegalStateException(String.format("The following classes could not be excluded because they are not auto-configuration classes:%n%s", message));
    }

    protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        Set<String> excluded = new LinkedHashSet();
        excluded.addAll(this.asList(attributes, "exclude"));
        excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
        excluded.addAll(this.getExcludeAutoConfigurationsProperty());
        return excluded;
    }

    protected List<String> getExcludeAutoConfigurationsProperty() {
        Environment environment = this.getEnvironment();
        if (environment == null) {
            return Collections.emptyList();
        } else if (environment instanceof ConfigurableEnvironment) {
            Binder binder = Binder.get(environment);
            return (List)binder.bind("spring.autoconfigure.exclude", String[].class).map(Arrays::asList).orElse(Collections.emptyList());
        } else {
            String[] excludes = (String[])environment.getProperty("spring.autoconfigure.exclude", String[].class);
            return excludes != null ? Arrays.asList(excludes) : Collections.emptyList();
        }
    }

    protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
    }

    private ConfigurationClassFilter getConfigurationClassFilter() {
        if (this.configurationClassFilter == null) {
            List<AutoConfigurationImportFilter> filters = this.getAutoConfigurationImportFilters();
            Iterator var2 = filters.iterator();

            while(var2.hasNext()) {
                AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var2.next();
                this.invokeAwareMethods(filter);
            }

            this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
        }

        return this.configurationClassFilter;
    }

    protected final <T> List<T> removeDuplicates(List<T> list) {
        return new ArrayList(new LinkedHashSet(list));
    }

    protected final List<String> asList(AnnotationAttributes attributes, String name) {
        String[] value = attributes.getStringArray(name);
        return Arrays.asList(value);
    }

    private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
        List<AutoConfigurationImportListener> listeners = this.getAutoConfigurationImportListeners();
        if (!listeners.isEmpty()) {
            AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
            Iterator var5 = listeners.iterator();

            while(var5.hasNext()) {
                AutoConfigurationImportListener listener = (AutoConfigurationImportListener)var5.next();
                this.invokeAwareMethods(listener);
                listener.onAutoConfigurationImportEvent(event);
            }
        }

    }

    protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
    }

    private void invokeAwareMethods(Object instance) {
        if (instance instanceof Aware) {
            if (instance instanceof BeanClassLoaderAware) {
                ((BeanClassLoaderAware)instance).setBeanClassLoader(this.beanClassLoader);
            }

            if (instance instanceof BeanFactoryAware) {
                ((BeanFactoryAware)instance).setBeanFactory(this.beanFactory);
            }

            if (instance instanceof EnvironmentAware) {
                ((EnvironmentAware)instance).setEnvironment(this.environment);
            }

            if (instance instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware)instance).setResourceLoader(this.resourceLoader);
            }
        }

    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory);
        this.beanFactory = (ConfigurableListableBeanFactory)beanFactory;
    }

    protected final ConfigurableListableBeanFactory getBeanFactory() {
        return this.beanFactory;
    }

    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassLoader = classLoader;
    }

    protected ClassLoader getBeanClassLoader() {
        return this.beanClassLoader;
    }

    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    protected final Environment getEnvironment() {
        return this.environment;
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    protected final ResourceLoader getResourceLoader() {
        return this.resourceLoader;
    }

    public int getOrder() {
        return 2147483646;
    }

    protected static class AutoConfigurationEntry {
        private final List<String> configurations;
        private final Set<String> exclusions;

        private AutoConfigurationEntry() {
            this.configurations = Collections.emptyList();
            this.exclusions = Collections.emptySet();
        }

        AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) {
            this.configurations = new ArrayList(configurations);
            this.exclusions = new HashSet(exclusions);
        }

        public List<String> getConfigurations() {
            return this.configurations;
        }

        public Set<String> getExclusions() {
            return this.exclusions;
        }
    }

// 一个静态类, 作用延迟加载自动配置,先加载用户自己的bean,在加载自动配置
    private static class AutoConfigurationGroup implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
        private final Map<String, AnnotationMetadata> entries = new LinkedHashMap();
        private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList();
        private ClassLoader beanClassLoader;
        private BeanFactory beanFactory;
        private ResourceLoader resourceLoader;
        private AutoConfigurationMetadata autoConfigurationMetadata;

        private AutoConfigurationGroup() {
        }
//类加载器
        public void setBeanClassLoader(ClassLoader classLoader) {
            this.beanClassLoader = classLoader;
        }

        public void setBeanFactory(BeanFactory beanFactory) {
            this.beanFactory = beanFactory;
        }

        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }

        public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
                return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
            });
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();

            while(var4.hasNext()) {
                String importClassName = (String)var4.next();
                this.entries.putIfAbsent(importClassName, annotationMetadata);
            }

        }

        public Iterable<DeferredImportSelector.Group.Entry> selectImports() {
            if (this.autoConfigurationEntries.isEmpty()) {
                return Collections.emptyList();
            } else {
                Set<String> allExclusions = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
                Set<String> processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
                processedConfigurations.removeAll(allExclusions);
                return (Iterable)this.sortAutoConfigurations(processedConfigurations, this.getAutoConfigurationMetadata()).stream().map((importClassName) -> {
                    return new DeferredImportSelector.Group.Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
                }).collect(Collectors.toList());
            }
        }

        private AutoConfigurationMetadata getAutoConfigurationMetadata() {
            if (this.autoConfigurationMetadata == null) {
                this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            }

            return this.autoConfigurationMetadata;
        }

        private List<String> sortAutoConfigurations(Set<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
            return (new AutoConfigurationSorter(this.getMetadataReaderFactory(), autoConfigurationMetadata)).getInPriorityOrder(configurations);
        }

        private MetadataReaderFactory getMetadataReaderFactory() {
            try {
                return (MetadataReaderFactory)this.beanFactory.getBean("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory", MetadataReaderFactory.class);
            } catch (NoSuchBeanDefinitionException var2) {
                return new CachingMetadataReaderFactory(this.resourceLoader);
            }
        }
    }

    private static class ConfigurationClassFilter {
        private final AutoConfigurationMetadata autoConfigurationMetadata;
        private final List<AutoConfigurationImportFilter> filters;

        ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) {
            this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
            this.filters = filters;
        }

        List<String> filter(List<String> configurations) {
            long startTime = System.nanoTime();
            String[] candidates = StringUtils.toStringArray(configurations);
            boolean skipped = false;
            Iterator var6 = this.filters.iterator();

            int i;
            while(var6.hasNext()) {
                AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var6.next();
                boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);

                for(i = 0; i < match.length; ++i) {
                    if (!match[i]) {
                        candidates[i] = null;
                        skipped = true;
                    }
                }
            }

            if (!skipped) {
                return configurations;
            } else {
                List<String> result = new ArrayList(candidates.length);
                String[] var12 = candidates;
                int var14 = candidates.length;

                for(i = 0; i < var14; ++i) {
                    String candidate = var12[i];
                    if (candidate != null) {
                        result.add(candidate);
                    }
                }

                if (AutoConfigurationImportSelector.logger.isTraceEnabled()) {
                    int numberFiltered = configurations.size() - result.size();
                    AutoConfigurationImportSelector.logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
                }

                return result;
            }
        }
    }
}

自动装配的流程

  1. 启动类 @SpringBootApplication
  2. 开启 @EnableAutoConfiguration
  3. @Import(AutoConfigurationImportSelector.class)
  4. 执行 selectImports()
  5. 读取 META-INF/spring/...imports 文件
  6. 获取所有自动配置类
  7. 排除不需要的
  8. 根据 @Conditional 过滤
  9. 返回最终需要装配的列表
  10. Spring 加载这些配置类 → 自动创建DataSource、Tomcat、Redis...

spring在启动时还有一个其他注解

\常用四大条件注解(必背)

@ConditionalOnClass项目里存在某个类,才装配→ 你引入了依赖,才自动配

@ConditionalOnMissingBean容器没有这个 Bean,Spring 才自己创建→ 你自定义了配置,就覆盖默认配置

@ConditionalOnPropertyyml 配置文件开启某项配置,才装配

@ConditionalOnMissingClass不存在某个类,才装配

相关推荐
星秀日2 小时前
大学生开发社区项目-CLXHXH-登录功能
java
兩尛2 小时前
struct,union,Class,bitfield各自的作用和区别
java·开发语言
算.子2 小时前
【Spring AI 实战】八、完整 RAG 问答实战:检索 + 重排序 + 生成全链路
java·人工智能·spring
wuminyu2 小时前
专家视角看 Java 字节码与Class 文件格式
java·linux·c语言·jvm·c++
Gauss松鼠会2 小时前
【openGauss】openGauss 磁盘引擎之 ustore
java·服务器·开发语言·前端·数据库·经验分享·gaussdb
lee_curry2 小时前
线程中断,等待,唤醒与ThreadLocal
java·线程·juc·threadlocal·中断
许彰午2 小时前
Spring Boot + Vue 实现 XML 动态表单:固定字段 + 自由扩展方案
xml·vue.js·spring boot
indexsunny2 小时前
互联网大厂Java面试实战:Spring Boot微服务与Kafka消息队列深度解析
java·spring boot·微服务·面试·kafka·消息队列·电商
杨凯凡2 小时前
【019】IO/NIO 概念:Web 开发要掌握到什么程度
java·开发语言·nio