百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

SpringBoot自动装配原理解析

bigegpt 2024-11-21 10:39 12 浏览


自动装配是 Spring Boot 的核心部分,也是 Spring Boot 功能的基础,正是由于自动装配,才将我们从 Bean 的繁复配置中解脱出来。那么 Spring Boot 中的自动装配指的是什么?我们继 续以 Spring MVC 为例,不使用 Spring Boot 时,我们可能需要配置视图解析器,文件解析器, 请求适配器等等各种 Bean , 如果在使用数据库 Redis ,还需要配置数据库Redis相关Bean。

一、从@SpringBootApplication 启动注解入手

源码如下:

@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 {};//直接根据 class 类扫描 	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") 	Class<?>[] scanBasePackageClasses() default {}; }

初看 @SpringBootApplication 有很多的注解组成,其实归纳就是一个 三体 结构,重要的只有三个 Annotation :

  1. @Configuration( @SpringBootConfiguration 实质就是一个 @Configuration )
  2. @EnableAutoConfiguration
  3. @ComponentScan

也就是说我们在开发的时候,加上上面的三个注解会等同于加上 @SpringBootApplication 注解。

@Configuration 注解 :

这个注解实际上就是代表了一个配置类,相当于一个 beans.xml 文件 ;

@ComponentScan 注解:

@ComponentScan 的功能其实就是自动扫描并加载符合条件的组件或 bean 定义,最终将这些bean 定义加载到容器中 ;

@EnableAutoConfiguration 注解:

在 spring 中有关于 @Enablexxx 的注解是开启某一项功能的注解,比如 @EnableScheduling表示开启 spring 的定时任务。其原理是借助 @Import 的帮助,将所有符合自动配置条件的 bean 定义加载到 Ioc 容器;

EnableAutoConfiguration 代表开启 springboot 的自动装配。

@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 {}; }

从上面源码中我们可以知道,最关键的要属 @Import(EnableAutoConfigurationImportSelector.class) ,借助 EnableAutoConfigurationImportSelector 和 @EnableAutoConfiguration 可以帮助 Spring Boot 应用将所有符合条件的 @Configuration 配置都加载到当前SpringBoot 创建并使用的 IoC 容器。同时借助于 Spring 框架原有的一个工具类: SpringFactoriesLoader , @EnableAutoConfiguration 就可以实现智能的自动配置。

从这里可以看出该类实现很多的 xxxAware 和 DeferredImportSelector ,所有的 aware 都优先于 selectImports ;

方法执行,也就是说 selectImports 方法最后执行,那么在它执行的时候所有需要的资源都已经获取到了

public class AutoConfigurationImportSelector implements  DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { ... public String[] selectImports(AnnotationMetadata annotationMetadata) { 	if (!this.isEnabled(annotationMetadata)) { 	return NO_IMPORTS; } else { //1. 加载 META-INF/spring-autoconfigure-metadata.properties文件 	AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);//2. 获取注解的属性及其值(PS:注解指的是@EnableAutoConfiguration 注解) 	AnnotationAttributes attributes = this.getAttributes(annotationMetadata);//3. 在classpath下所有的 META-INF/spring.factories 文件中查找 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值, 并将其封装到一个 List 中返回 	List<String> configurations =this.getCandidateConfigurations(annotationMetadata, attributes); //4. 对上一步返回的 List 中的元素去重、排序 	configurations = this.removeDuplicates(configurations);//5.依据第 2 步中获取的属性值排除一些特定的类 	Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);//6. 对上一步中所得到的 List 进行过滤,过滤的依据是条件匹配。这里用到的过滤器是org.springframework.boot.autoconfigure.condition.OnClassCondition最终返回的是一个 ConditionOutcome[]数组。//(PS:很多类都是依赖于其它的类的,当有某个类时才会装配,所以这次过滤的就是根据是否有某个class进而决定是否装配的。这些类所依赖的类都写在META-INF/spring-autoconfigure-metadata.properties 文件里)	this.checkExcludedClasses(configurations, exclusions); 	configurations.removeAll(exclusions); 	configurations = this.filter(configurations,autoConfigurationMetadata);	this.fireAutoConfigurationImportEvents(configurations,exclusions); 	return StringUtils.toStringArray(configurations); 	} }protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { 	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderF actoryClass(), 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; 	} 	... }

SpringFactoriesLoader 中加载配置, SpringFactoriesLoader 属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件 META-INF/spring.factories 加载配置, 即根据 @EnableAutoConfiguration 的完整类名 org.springframework.boot.autoconfigure.EnableAutoConfiguration 作为查找的 Key, 获取对应的一组 @Configuration 类。

public abstract class SpringFactoriesLoader { public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { 	MultiValueMap<String, String> result = cache.get(classLoader); 	if (result != null) 	return result; 	try { 		Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); 	result = new LinkedMultiValueMap<>(); 	while (urls.hasMoreElements()) { 		URL url = urls.nextElement(); 		UrlResource resource = new UrlResource(url);Properties properties = 		PropertiesLoaderUtils.loadProperties(resource); 		for (Map.Entry<?, ?> entry : properties.entrySet()) { 			List<String> factoryClassNames = Arrays.asList( 				StringUtils.commaDelimitedListToStringArray((String) entry.getValue())); 			result.addAll((String) entry.getKey(), factoryClassNames); 		} 	} 		cache.put(classLoader, result); 		return result; }catch (IOException ex) { 	throw new IllegalArgumentException("Unable to load  	factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); 	} }...

总结:

@EnableAutoConfiguration 作用就是从 classpath 中搜寻所有的 META-INF/spring.factories配置文件,并将其中 org.springframework.boot.autoconfigure.EnableutoConfiguration 对应的配置项通过反射(Java Refletion)实例化为对应的标注了 @Configuration 的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到 IoC 容器。

这些功能配置类要生效的话,会去 classpath 中找是否有该类的依赖类(也就是pom.xml 必须有对应功能的 jar 包才行)并且配置类里面注入了默认属性值类,功能类可以引用并赋默认值。生成功能类的原则是自定义优先,没有自定义时才会使用自动装配类。

所以功能类能生效需要的 条件:

  1. spring.factories 里面有这个类的配置类(一个配置类可以创建多个围绕该功能的依赖类);
  2. pom.xml里面需要有对应的JAR包。

二、自动装配案例说明 Redis为例

1、从 spring-boot-autoconfigure.jar/META-INF/spring.factories 中获取Redis的相关配置类全限定名(有 120 多个的配置类) RedisAutoConfiguration , 一般一个功能配置类围绕该功能,负责管理创建多个相关的功能类,比如 RedisAutoConfiguration 负责: JedisConnectionFactory 、 RedisTemplate 、 StringRedisTemplate 这3个功能类的创建 spring.factories 中的Redis配置类 。

2、 RedisAutoConfiguration 配置类生效的一个条件是在 classpath 路径下有 RedisOperations 类存在,因此 Spring Boot 的自动装配机制会去 classpath 下去查找对应的 class 文件。

@Configuration @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { ...}

3、如果 pom.xml 有对应的 jar 包,就能匹配到对应依赖 class

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

4、匹配成功, 这个功能配置类才会生效, 同时会注入默认的属性配置类 @EnableConfigurationProperties(RedisProperties.class)

@ConfigurationProperties(prefix = "spring.redis") 	public class RedisProperties { 	private int database = 0; 	private String url; 	private String host = "localhost"; 	private String password; 	private int port = 6379; ...

5、Redis功能配置里面会根据条件生成最终的 JedisConnectionFactory 、 RedisTemplate ,并提供了默认的配置形式`@ConditionalOnMissingBean(name =

"redisTemplate") `

@Configuration @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class)@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration {	@Bean //用户没定义就使用默认的 	@ConditionalOnMissingBean(name = "redisTemplate") 	public RedisTemplate<Object, Object> redisTemplate( 		RedisConnectionFactory redisConnectionFactory) throws  UnknownHostException { 	  RedisTemplate<Object, Object> template = new RedisTemplate<>(); 	  template.setConnectionFactory(redisConnectionFactory); 	  return template;     }    @Bean 	@ConditionalOnMissingBean(StringRedisTemplate.class) 	public StringRedisTemplate stringRedisTemplate( 		RedisConnectionFactory redisConnectionFactory) throws  UnknownHostException {       StringRedisTemplate template = new StringRedisTemplate(); 	  template.setConnectionFactory(redisConnectionFactory);       return template; 	} }

6、最终创建好的默认装配类,会通过功能配置类里面的 @Bean 注解,注入到 IOC 当中

7、用户使用,当用户在配置文件中自定义时候就会覆盖默认的配置 @ConditionalOnMissingBean(name = "redisTemplate")

三、自动依赖过程总结

1、通过各种注解实现了类与类之间的依赖关系,容器在启动的时候 Application.run ,会调用 EnableAutoConfigurationImportSelector.class 的 selectImports 方法(其实是其父类的方法)

这里需要注意,调用这个方法之前发生了什么和是在哪里调用这个方法需要进一步的探讨。

2、 selectImports 方法最终会调用 SpringFactoriesLoader.loadFactoryNames 方法来获取一个全面的常用 BeanConfiguration 列表。

3、 loadFactoryNames 方法会读取 FACTORIES_RESOURCE_LOCATION (也就是 spring-boot-autoconfigure.jar 下面的 spring.factories),获取到所有的Spring 相关的 Bean 的全限定名 ClassName,大概 120 多个 。

4、 selectImports 方法继续调用 filter(configurations, autoConfigurationMetadata);这个时候会根据这些 BeanConfiguration 里面的条件,来一一筛选,最关键的是 @ConditionalOnClass ,这个条件注解会去 classpath 下查找,jar 包里面是否有这个条件依赖类,所以必须有了相应的 jar 包,才有这些依赖类,才会生成 IOC 环境需要的一些默认配置 Bean 。

5、最后把符合条件的 BeanConfiguration 注入默认的 EnableConfigurationPropertie 类里面的属性值,并且注入到 IOC 环境当中。

文末福利:

最近整理了:Java零基础精品教学视频,技术书籍,技术文档,面试文档,学习脑图,基础核心知识等..学习资料!!

私信回复关键词:Java (点击我的头像,进入主页面,点击右上角私信按钮)即可免费领取。

希望大家将此篇文章分享,转载,让更多需要的朋友看到,这样不仅帮助了自己,也帮助了他人,谢谢!!

相关推荐

悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)

新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...

高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源

凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...

微服务架构实战:商家管理后台与sso设计,SSO客户端设计

SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...

还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑

在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...

Seata源码—6.Seata AT模式的数据源代理二

大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...

30分钟了解K8S(30分钟了解微积分)

微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...

SpringBoot条件化配置(@Conditional)全面解析与实战指南

一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...

一招解决所有依赖冲突(克服依赖)

背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...

你读过Mybatis的源码?说说它用到了几种设计模式

学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...

golang对接阿里云私有Bucket上传图片、授权访问图片

1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...

spring中的资源的加载(spring加载原理)

最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...

Android资源使用(android资源文件)

Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...

如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)

深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...

@Autowired与@Resource原理知识点详解

springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...

java的redis连接工具篇(java redis client)

在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...