【Spring Boot】第二篇 自动装配原来就这么简单

导航

      • 一. 什么是自动装配?
      • 二. 如何实现自动装配?
        • 1. 配置清单在哪里?
        • 2. 自动装配实现
          • 核心点1: 从META‐INF/spring.factories路径读取配置类清单
          • 核心点2: 过滤
            • 第一次过滤: 根据EnableAutoConfiguration注解中exclude和excludeName属性
            • 第二次过滤: 通过AutoConfigurationImportFilter过滤器
        • 3. 总结自动装配的实现原理

还记得以前面试的时候,经常被问道,说一下Spring Boot的自动装配原理?
咋一听,原理? 觉得逼格应该挺高, 很高大上的东西, 等自己真正了解之后, 卧槽, 就那么回事。
Spring Boot版本是2.6.13

一. 什么是自动装配?

基于注解的Spring Boot的自动装配是Spring Boot框架中的一种特性,它允许开发者使用注解来简化和自动化应用程序的配置和装配过程。
通过使用@EnableAutoConfiguration特定的注解,Spring Boot可以根据应用程序的依赖关系和配置来自动装配和配置一系列的Bean对象和组件。开发者只需要在相应的类或方法上添加特定的注解,Spring Boot就会根据这些注解的配置信息自动完成相应的初始化和装配工作。
简单来说,就是解放开发者的双手, 一切的脏活累活直接交给Spring Boot来完成。

二. 如何实现自动装配?

在介绍自动装配原理之前, 先举一个生活中的简单例子,某天你发工资了, 要犒劳一下自己, 决定去吃一顿火锅, 来到火锅店扫描点餐, 点了一个鸳鸯锅底,并备注要微辣, 还有一些荤菜和素菜之类的。然后服务端打印了你下单的清单,给到后厨,厨师根据你的清单准备锅底和食材, 一切准备好后,服务员端到你的餐桌上, 直接开吃了。
在这里插入图片描述
这个例子重点在于菜单, 需要有人写,还需要有人读,; 例子中大厨就是Spring Boot的角色, 顾客就是写Spring Boot的开发人员, 菜单实际上就是Spring Boot自动装配的配置清单, 所以自动装配实际上就是一个实现这个读的步骤 。
接下来结合源码来具体分析:

1. 配置清单在哪里?

首先pom文件中依赖了spring-boot-starter-web

 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
 </dependency>

其内部实际上又依赖了spring-boot-autoconfigure, 使用mvn dependency:tree -Dverbose生成依赖树, 来验证下
在这里插入图片描述
这个配置清单就在spring-boot-autoconfigureMETA-INF/spring.factories文件中
在这里插入图片描述
spring.factories文件中, 找到org.springframework.boot.autoconfigure.EnableAutoConfiguration, 下面都是自动装配的配置清单

# Auto Configure  核心配置: 待自动装配的配置清单
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration,\
.......省略.......

配置清单已经有了,已经知道在什么位置了, 那么只需要让Spring Boot来读取就行了!

2. 自动装配实现

首先看Spring Boot 项目的启动类

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication  
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootDemoApplication.class, args);
    }
}

启动类被注解@SpringBootApplication定义,此注解用于标识该类是Spring Boot的配置类。配置类通常用于定义各种Bean(例如数据源、事务管理器等)以及其他配置(例如扫描路径、是否开启自动配置等)

细看注解@SpringBootApplication内部构造

@Target(ElementType.TYPE)   //表明@SpringBootApplication注解只能定义在类上
@Retention(RetentionPolicy.RUNTIME) //表明当注解标注的类编译以被JVM加载方式保留
@Documented   //Java Doc会生成注解信息 
@Inherited  //是否会被继承 
@SpringBootConfiguration //标注在某个类上,表示这是一个Spring Boot的配置类
@EnableAutoConfiguration  //★核心, 开启自动配置功能
//@ComponentScan 扫描包, 相当于在spring.xml 配置中<context:comonent-scan> 
//但是并没有指定basepackage,如果没有指定spring底层会自动扫描当前配置类所有在的包
@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 {};

    /**
	 * 属性可以指定一个或多个包名,用逗号分隔。告诉Spring Boot在启动时需要扫描的包,以查找带有注解的类
	 * @SpringBootApplication(scanBasePackages = {"com.example.package1", "com.example.package2"})
	 */
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};
	
    /**
	 * 属性指定一个或多个类,Spring Boot 在启动时会扫描这些类所在的包
	 * @SpringBootApplication(scanBasePackageClasses = {MyClass1.class, MyClass2.class})
	 */
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

    /**
	 * 该属性指定一个自定义的Bean名称生成器。
	 * Spring Boot应用程序中的每个Bean都需要一个唯一的名称来标识它们,通常使用默认的命名策略。
     * 但是,如果需要对Bean的名称进行自定义,可以通过实现BeanNameGenerator接口并在nameGenerator属性中指定自定义的Bean名称生成器。
     * 使用自定义的名称生成器可以根据项目需求为Bean生成特定的名称。
	 */
	@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    /**
	 * 该属性指定一个自定义的Bean名称生成器。
	 * 用于控制是否为@Configuration配置类中的@Bean方法生成代理对象
     * 默认情况下,Spring Boot应用程序会为配置类(也就是带有@Configuration注解的类)中的@Bean方法生成代理对象。
     * 这样做的目的是为了确保@Bean方法每次调用都返回同一个Bean实例,以充分利用Spring的缓存机制。
     * 但是,如果希望禁用这种代理行为,可以将proxyBeanMethods属性设置为false。
     * 禁用代理行为可能会导致@Bean方法每次都创建一个新的Bean实例,可能会降低应用程序的性能。
	 */
	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

可以看到,注解类SpringBootApplication又被@EnableAutoConfiguration注解定义, 这么直白的名字,一看就知道它要开启自动装配,SpringBoot要开始骚了,于是默默进去看源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage   //用于指定自动配置类所在的包。它的作用是告诉Spring Boot应该扫描哪个包来寻找自动配置类
@Import(AutoConfigurationImportSelector.class)   //核心
public @interface EnableAutoConfiguration {

	/**
	 * 一个常量,用于指定一个环境变量或系统属性,用于在启用自动配置时覆盖默认值。
	 * 默认情况下,自动配置是启用的,但可以通过设置这个属性来覆盖默值。如果设置了该属性的值为false,则自动配置将被禁用。
	 */
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * 用于排除某些自动配置类。可以指定一个或多个类,使其不被自动配置
	 * 例如:@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
	 */
	Class<?>[] exclude() default {};

	/**
	 * 与 exclude 属性类似,但是通过指定类的全限定名来排除自动配置类
	 * 例如:@EnableAutoConfiguration(excludeName"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration")
	 */
	String[] excludeName() default {};
}

利用注解@Import导入了AutoConfigurationImportSelector类, 而AutoConfigurationImportSelector类实现了DeferredImportSelector,并重写了getImportGroup()方法

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

返回了一个自定义实现DeferredImportSelector.Group类的AutoConfigurationGroup静态内部类, 并重写process()方法

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;

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

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

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

		@Override
		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
			Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
					() -> String.format("Only %s implementations are supported, got %s",
							AutoConfigurationImportSelector.class.getSimpleName(),
							deferredImportSelector.getClass().getName()));
			//核心方法getAutoConfigurationEntry(),加载配置类清单				
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(annotationMetadata);
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}
		........省略......
   }

Spring Boot内部在解析@Import注解时会调用AutoConfigurationImportSelector.getAutoConfigurationEntry()方法,
下面是Spring Boot 2.6.13版本的源码: 重点关注三个核心点

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	//核心点1: 从META‐INF/spring.factories中获得候选的自动配置类
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	//去重
	configurations = removeDuplicates(configurations);
	//核心点2:过滤
	//第一次过滤:根据EnableAutoConfiguration注解中exclude和excludeName属性,获取不需要自动装配的类名单
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    //进行排除
	checkExcludedClasses(configurations, exclusions);
    //exclusions 也排除
	configurations.removeAll(exclusions);
    //第二次过滤: 通过读取spring.factories 中的OnBeanCondition\OnClassCondition\OnWebApplicationCondition进行过滤
	configurations = getConfigurationClassFilter().filter(configurations);
	//这个方法是调用实现了AutoConfigurationImportListener的bean..  分别把候选的配置名单,和排除的配置名单传进去做扩展
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}
核心点1: 从META‐INF/spring.factories路径读取配置类清单

SpringFactoriesLoader中的loadSpringFactories()方法中有明确定义

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
   MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            //指定了路径
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            MultiValueMap<String, String> result = new LinkedMultiValueMap();

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryImplementationName = var9[var11];
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }

            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}

spring.factories配置清单中的内容是不是全部读?
不是的, 根据标识符org.springframework.boot.autoconfigure.EnableAutoConfiguration, 从指定位置读取内容。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   //从META‐INF/spring.factories中获得候选的自动配置类
   //读取的配置类有133个
   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;
}

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

在源码SpringFactoriesLoader.loadFactoryNames()方法中有定义

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    //从META‐INF/spring.factories中获得候选的自动配置类
    //读取的配置类有133个
	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;
}

/**
 * 依据getSpringFactoriesLoaderFactoryClass()可知, 参数factoryType其实就是EnableAutoConfiguration.class
 */
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
       //factoryTypeName的值就是org.springframework.boot.autoconfigure.EnableAutoConfiguration
       //刚好对应上spring.factories配置清单上指定位置
       String factoryTypeName = factoryType.getName(); 
       return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
核心点2: 过滤

为什么需要过滤? 从spring.factories文件中读取到所有配置类,是否都需要进行自动装配,然后让如IOC容器中?
答案是否定的, 并不是都需要进行自动装配, 需要按需装配, 这就需要Spring Boot自己进行选择性的判断。
根据源码AutoConfigurationImportSelector.getAutoConfigurationEntry()方法可知,会进行两次过滤

第一次过滤: 根据EnableAutoConfiguration注解中exclude和excludeName属性

根据EnableAutoConfiguration注解中excludeexcludeName属性,获取不需要自动装配的类名单, 但是从注解SpringBootApplication类中可知, 定义的@EnableAutoConfiguration注解并没有设置exclude和excludeName属性值,所以可忽略

第二次过滤: 通过AutoConfigurationImportFilter过滤器

首先会在spring.factories文件根据org.springframework.boot.autoconfigure.AutoConfigurationImportFilter标识, 在指定位置读取需要进行过滤的内容

# Auto Configuration Import Filters 核心过滤: 根据条件进行许选择性的自动装配
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

使用过滤器AutoConfigurationImportFilter, 争对三个spring.factories文件定义的内容分别对注解@OnBeanCondition, @OnClassCondition@OnWebApplicationCondition进行条件按判断, 不符合条件的不会进行自动装配。
见源码SpringFactoriesLoader中的loadFactories()方法

public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
      Assert.notNull(factoryType, "'factoryType' must not be null");
      ClassLoader classLoaderToUse = classLoader;
      if (classLoader == null) {
          classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
      }
      //factoryType 是org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
      //从spring.factories文件的org.springframework.boot.autoconfigure.AutoConfigurationImportFilter指定位置读取内容
      //集合中有三个
      //org.springframework.boot.autoconfigure.condition.OnBeanCondition
      //org.springframework.boot.autoconfigure.condition.OnClassCondition
      //org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
      List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
      if (logger.isTraceEnabled()) {
          logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
      }

      List<T> result = new ArrayList(factoryImplementationNames.size());
      Iterator var5 = factoryImplementationNames.iterator();

      while(var5.hasNext()) {
          String factoryImplementationName = (String)var5.next();
          result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
      }

      AnnotationAwareOrderComparator.sort(result);
      return result;
  }

最后过滤完成后, 剩下的就是需要进行自动装配的配置类了

HttpEncodingAutoConfiguration配置类,来举例

//标记了@Configuration Spring底层会给配置创建cglib动态代理。 作用:就是防止每次调用本类的Bean方法而重新创建对象,Bean是默认单例的
@Configuration(proxyBeanMethods = false) 
@EnableConfigurationProperties(ServerProperties.class)  //启用可以在配置类设置的属性 对应的类
//条件注解, 当前是web环境,条件成立,才进行自动装配
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) 
//条件注解,容器中必须有CharacterEncodingFilter类,条件成立,才进行自动装配
@ConditionalOnClass(CharacterEncodingFilter.class) 
//条件注解, 表示配置文件application.properties有server.servlet.encoding配置,且定义的值是enabled时,条件成立,允许进行自动装配
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true) 
public class HttpEncodingAutoConfiguration {
	private final Encoding properties;

	public HttpEncodingAutoConfiguration(ServerProperties properties) {
		this.properties = properties.getServlet().getEncoding();
	}

	@Bean
	@ConditionalOnMissingBean  // 当IOC容器中没有CharacterEncodingFilter时,才会创建
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
		return filter;
	}

	@Bean
	public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
		return new LocaleCharsetMappingsCustomizer(this.properties);
	}

......省略......
}

当注解@ConditionalOnWebApplication, @ConditionalOnClass@ConditionalOnProperty定义的条件都成立时, 那么Spring Boot就会对该配置类里被注解@Bean定义的方法,进行自动装配, 创建对应的Bean对象,然后放入IOC容器中。这样当Spring Boot正常运行后,就可以直接从IOC容器中拿Bean对象直接使用了。

3. 总结自动装配的实现原理

在这里插入图片描述
从从spring-boot-autoconfigure JAR包的META-INF/spring.factories文件中获取的配置列表起初有133个
在这里插入图片描述
经过层层过滤之后, 最后还剩下24个
在这里插入图片描述
最后结合例子和源码详解,Spring Boot基于注解的自动装配的原理,是不是很简单。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/378513.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【学网攻】 第(23)节 -- PPP协议

系列文章目录 目录 系列文章目录 文章目录 前言 一、PPP协议是什么&#xff1f; 二、实验 1.引入 实验目的 实验背景你是某公司的网络管理员&#xff0c;现在需要与另一个公司进行通信,需要你配置PPP协议保证双方发送的人是真正的而非黑客 技术原理 实验步骤新建Pack…

Backtrader 文档学习- Plotting - Plotting Date Ranges

Backtrader 文档学习- Plotting - Plotting Date Ranges 1.概述 1.9.31.x版本增加了绘制部分图形的功能。 可以使用策略实例中保留完整长度的时间戳数组的索引或者使用实际的datetime.date 或datetime.datetime 实例来限制需要绘制的内容。 仍然可以使用标准的cerebro.plot…

自然语言学习nlp 六

https://www.bilibili.com/video/BV1UG411p7zv?p118 Delta Tuning&#xff0c;尤其是在自然语言处理&#xff08;NLP&#xff09;和机器学习领域中&#xff0c;通常指的是对预训练模型进行微调的一种策略。这种策略不是直接更新整个预训练模型的权重&#xff0c;而是仅针对模型…

YOLOv8改进 更换轻量级网络结构

一、GhostNet论文 论文地址:1911.11907.pdf (arxiv.org) 二、 GhostNet结构 GhostNet是一种高效的目标检测网络,具有较低的计算复杂度和较高的准确性。该网络采用了轻量级的架构,可以在计算资源有限的设备上运行,并能够快速地实时检测图像中的目标物体。 GhostNet基于Mo…

【大模型上下文长度扩展】RoFormer:一种结合了旋转位置编码(RoPE)的Transformer模型

RoFormer&#xff1a;一种结合了旋转位置编码&#xff08;RoPE&#xff09;的Transformer模型 提出背景旋转位置编码&#xff08;RoPE&#xff09;旋转矩阵子问题1: 位置信息编码的需求子问题2: 序列长度的灵活性子问题3: 线性自注意力与相对位置编码的整合 RoPE的创新性绝对位…

CRNN介绍:用于识别图中文本的深度学习模型

CRNN&#xff1a;用于识别图中文本的深度学习模型 CRNN介绍&#xff1a;用于识别图中文本的深度学习模型CRNN的结构组成部分工作原理 CRNN结构分析卷积层&#xff08;Convolutional Layers&#xff09;递归层&#xff08;Recurrent Layers&#xff09;转录层&#xff08;Transc…

恒创科技:怎么看云主机的性价比

随着云计算技术的不断发展&#xff0c;云主机已经成为越来越多企业和个人用户的首选计算资源。然而&#xff0c;在选择云主机时&#xff0c;性价比是一个非常重要的考虑因素。那么&#xff0c;怎么看云主机的性价比呢&#xff1f; 通常来说&#xff0c;云主机的成本主要包括硬…

相机图像质量研究(10)常见问题总结:光学结构对成像的影响--光圈

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

MySQL数据库基础第四篇(多表查询与事务)

文章目录 一、多表关系二、多表查询三、内连接查询四、外连接查询五、自连接查询六、联合查询 union, union all七、子查询1.标量子查询2.列子查询3.行子查询4.表子查询 八、事务八、事务的四大特性九、并发事务问题十、事务隔离级级别 在这篇文章中&#xff0c;我们将深入探讨…

MySQL篇之定位与优化MySQL慢查询

一、如何定位慢查询 1.方案一&#xff1a;开源工具 调试工具&#xff1a;Arthas。 运维工具&#xff1a;Prometheus 、Skywalking。 2.方案二&#xff1a;MySQL自带慢日志 慢查询日志记录了所有执行时间超过指定参数&#xff08;long_query_time&#xff0c;单位&#xff1a;…

SpringIOC之support模块ReloadableResourceBundleMessageSource

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

【分布式】雪花算法学习笔记

雪花算法学习笔记 来源 https://pdai.tech/md/algorithm/alg-domain-id-snowflake.html概述 雪花算法是推特开源的分布式ID生成算法&#xff0c;以划分命名空间的方式将64位分割成多个部分&#xff0c;每一个部分代表不同的含义&#xff0c;这种就是将64位划分成不同的段&…

armbian ddns

参考https://mp.weixin.qq.com/s/0Uu_nbGH_W6vAYHPH4kHqg Releases jeessy2/ddns-go GitHub mkdir -p /usr/local/ddns-go cd /usr/local/ddns-gowget https://github.com/jeessy2/ddns-go/releases/download/v6.1.1/ddns-go_6.1.1_freebsd_armv7.tar.gztar zxvf ddns-go_…

Unity2D 学习笔记 0.Unity需要记住的常用知识

Unity2D 学习笔记 0.Unity需要记住的常用知识 前言调整Project SettingTilemap相关&#xff08;创建地图块&#xff09;C#脚本相关程序运行函数private void Awake()void Start()void Update() Collider2D碰撞检测private void OnTriggerStay2D(Collider2D player)private void…

DevOps落地笔记-20|软件质量:决定系统成功的关键

上一课时介绍通过提高工程效率来提高价值交付效率&#xff0c;从而提高企业对市场的响应速度。在提高响应速度的同时&#xff0c;也不能降低软件的质量&#xff0c;这就是所谓的“保质保量”。具备高质量软件&#xff0c;高效率的企业走得更快更远。相反&#xff0c;低劣的软件…

idea自带的HttpClient使用

1. 全局变量配置 {"local":{"baseUrl": "http://localhost:9001/"},"test": {"baseUrl": "http://localhost:9002/"} }2. 登录并将结果设置到全局变量 PostMapping("/login")public JSONObject login(H…

前端文件下载的多种方式

前端文件下载的多种方式。 前言a标签下载a标签常用属性介绍- target&#xff0c;href&#xff0c;download。 window.location.href下载window.open下载iframe 下载动态生成a标签下载文件url下载文件流下载blob文件流转换常用类型 使用 streamSaver 看实时下载进度 前言 如果我…

spring boot(2.4.x之前版本)和spring cloud项目中自动装配的监听执行顺序

目录 扫描 org.springframework.context.ApplicationListener 指定的类 内置的监听 spring boot 中的监听 spring boot autoconfigure 中的监听 spring boot context 中的监听 将加载的监听进行排序 spring boot 中的监听 spring boot context 中的监听 监听执行 监听…

讲讲GPT-4模型中13万亿个token的故事

Token从字面意思上看是游戏代币&#xff0c;用在深度学习中的自然语言处理领域中时&#xff0c;代表着输入文字序列的“代币化”。那么海量语料中的文字序列&#xff0c;就可以转化为海量的代币&#xff0c;用来训练我们的模型。这样我们就能够理解“用于GPT-4训练的token数量大…

Java stream 流的基本使用

Java stream 的基本使用 package com.zhong.streamdemo.usestreamdemo;import jdk.jfr.DataAmount; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.util.ArrayList; import java.util.Comparator; import java.util.Li…
最新文章