自动装配(自动装配和依赖注入)

http://www.itjxue.com  2023-01-24 22:59  来源:未知  点击次数: 

SpringBoot自动装配原理

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

(1)@Configuration注解

(2)@ComponentScan

(3)@EnableAutoConfiguration

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

总结 :@EnableAutoConfiguration作用就是从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。这些功能配置类要生效的话,会去classpath中找是否有该类的依赖类(也就是pom.xml必须有对应功能的jar包才行)并且配置类里面注入了默认属性值类,功能类可以引用并赋默认值。生成功能类的原则是自定义优先,没有自定义时才会使用自动装配类。

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

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

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

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

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

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环境当中

SpringBoot自动配置/装配(SPI)

自己动手写一个启动器的经历:

这里报错了,原因是只导入了一个bean而SpringBoot无法启动服务器

解决办法一:将@Import改成@ConponentScan这样就会扫描同级包和子包。

解决办法二:关闭web服务器

自动装配的大概流程 :@EnableAutoConfiguration目的是自动装配在Maven中的第三方依赖,然后通过@Import(seleter)导入selecter,再通过selecter找到对应的.factories文件最终加载了打上@Configuration的文件。

首先开启了自动装配

通过Selector方法返回对应的类的名字

SPI全名为Service Provider Interface

模块 实现方案

|? |? |? |? |? |? |? ? ? ? ? ? ? 方案A

调用方 标准服务接口? ? 方案B

|? |? |? |? |? |? |? ? ? ? ? ? ? 方案C

基于interface + 策略模式 + 配置模式

整体解决方案的变化

与@Primary@条件注解的区别:具体/粒度小。

彻底搞明白Spring中的自动装配和Autowired

当Spring装配Bean属性时,有时候非常明确,就是需要将某个Bean的引用装配给指定属性。比如,如果我们的应用上下文中只有一个 org.mybatis.spring.SqlSessionFactoryBean 类型的Bean,那么任意一个依赖 SqlSessionFactoryBean 的其他Bean就是需要这个Bean。毕竟这里只有一个 SqlSessionFactoryBean 的Bean。

为了应对这种明确的装配场景,Spring提供了自动装配(autowiring)。与其显式的装配Bean属性,为何不让Spring识别出可以自动装配的场景。

当涉及到自动装配Bean的依赖关系时,Spring有多种处理方式。因此,Spring提供了4种自动装配策略。

Spring在 AutowireCapableBeanFactory 接口中定义了这几种策略。其中, AUTOWIRE_AUTODETECT 被标记为过时方法,在Spring3.0之后已经不再支持。

它的意思是,把与Bean的属性具有相同名字的其他Bean自动装配到Bean的对应属性中。听起来可能比较拗口,我们来看个例子。

首先,在User的Bean中有个属性 Role myRole ,再创建一个Role的Bean,它的名字如果叫myRole,那么在User中就可以使用byName来自动装配。

上面是Bean的定义,再看配置文件。

如上所述,只要属性名称和Bean的名称可以对应,那么在user的Bean中就可以使用byName来自动装配。那么,如果属性名称对应不上呢?

是的,如果不使用属性名称来对应,你也可以选择使用类型来自动装配。它的意思是,把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。

还是上面的例子,如果使用byType,Role Bean的ID都可以省去。

它是说,把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。值的注意的是, 具有相同类型的其他Bean 这句话说明它在查找入参的时候,还是通过Bean的类型来确定。

构造器中入参的类型为Role

它首先会尝试使用constructor进行自动装配,如果失败再尝试使用byType。不过,它在Spring3.0之后已经被标记为 @Deprecated 。

默认情况下,default-autowire属性被设置为none,标示所有的Bean都不使用自动装配,除非Bean上配置了autowire属性。

如果你需要为所有的Bean配置相同的autowire属性,有个办法可以简化这一操作。

在根元素Beans上增加属性 default-autowire="byType" 。

Spring自动装配的优点不言而喻。但是事实上,在Spring XML配置文件里的自动装配并不推荐使用,其中笔者认为最大的缺点在于不确定性。或者除非你对整个Spring应用中的所有Bean的情况了如指掌,不然随着Bean的增多和关系复杂度的上升,情况可能会很糟糕。

从Spring2.5开始,开始支持使用注解来自动装配Bean的属性。它允许更细粒度的自动装配,我们可以选择性的标注某一个属性来对其应用自动装配。

Spring支持几种不同的应用于自动装配的注解。

我们今天只重点关注Autowired注解,关于它的解析和注入过程,请参考笔者Spring源码系列的文章。 Spring源码分析(二)bean的实例化和IOC依赖注入

使用@Autowired很简单,在需要注入的属性加入注解即可。

不过,使用它有几个点需要注意。

默认情况下,它具有强制契约特性,其所标注的属性必须是可装配的。如果没有Bean可以装配到Autowired所标注的属性或参数中,那么你会看到 NoSuchBeanDefinitionException 的异常信息。

看到上面的源码,我们可以得到这一信息,Bean集合为空不要紧,关键 isRequired 条件不能成立,那么,如果我们不确定属性是否可以装配,可以这样来使用Autowired。

我记得曾经有个面试题是这样问的:Autowired是按照什么策略来自动装配的呢?

关于这个问题,不能一概而论,你不能简单的说按照类型或者按照名称。但可以确定的一点的是,它默认是按照类型来自动装配的,即byType。

关键点 findAutowireCandidates 这个方法。

可以看到它返回的是一个列表,那么就表明,按照类型匹配可能会查询到多个实例。到底应该装配哪个实例呢?我看有的文章里说,可以加注解以此规避。比如 @qulifier、@Primary 等,实际还有个简单的办法。

比如,按照UserService接口类型来装配它的实现类。UserService接口有多个实现类,分为 UserServiceImpl、UserServiceImpl2 。那么我们在注入的时候,就可以把属性名称定义为Bean实现类的名称。

这样的话,Spring会按照byName来进行装配。首先,如果查到类型的多个实例,Spring已经做了判断。

可以看出,如果查到多个实例, determineAutowireCandidate 方法就是关键。它来确定一个合适的Bean返回。其中一部分就是按照Bean的名称来匹配。

最后我们回到问题上,得到的答案就是:@Autowired默认使用byType来装配属性,如果匹配到类型的多个实例,再通过byName来确定Bean。

上面我们已经看到了,通过byType可能会找到多个实例的Bean。然后再通过byName来确定一个合适的Bean,如果通过名称也确定不了呢?

还是 determineAutowireCandidate 这个方法,它还有两种方式来确定。

它的作用是看Bean上是否包含@Primary注解,如果包含就返回。当然了,你不能把多个Bean都设置为@Primary,不然你会得到 NoUniqueBeanDefinitionException 这个异常。

你也可以在Bean上配置@Priority注解,它有个int类型的属性value,可以配置优先级大小。数字越小的,就被优先匹配。同样的,你也不能把多个Bean的优先级配置成相同大小的数值,否则 NoUniqueBeanDefinitionException 异常照样出来找你。

最后,有一点需要注意。Priority的包在 javax.annotation.Priority; ,如果想使用它还要引入一个坐标。

本章节重点阐述了Spring中的自动装配的几种策略,又通过源码分析了Autowired注解的使用方式。

在Spring3.0之后,有效的自动装配策略分为 byType、byName、constructor 三种方式。注解Autowired默认使用byType来自动装配,如果存在类型的多个实例就尝试使用byName匹配,如果通过byName也确定不了,可以通过Primary和Priority注解来确定。

Spring自动装配原理

其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件!

点进去,发现还有一个父依赖

这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;

以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;

springboot-boot-starter-xxx :就是spring-boot的场景启动器

SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;我们未来也可以自己自定义 starter;

默认的主启动类

但是一个简单的启动类并不简单!我们来分析一下这些注解都干了什么

@SpringBootApplication

作用:标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;

进入这个注解:可以看到上面还有很多其他注解!

@ComponentScan

这个注解在Spring中很重要 ,它对应XML配置中的元素。

作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中

@SpringBootConfiguration

作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;

我们继续进去这个注解查看

这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;

里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!

我们回到 SpringBootApplication 注解中继续看。

@EnableAutoConfiguration :开启自动配置功能

以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

点进注解接续查看:

@AutoConfigurationPackage :自动配置包

@import :Spring底层注解@import , 给容器中导入一个组件

Registrar.class 作用:将主启动类所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

这个分析完了,退到上一步,继续看

@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;

AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:

1、这个类中有一个这样的方法:

2、这个方法又调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法

3、我们继续点击查看 loadSpringFactories 方法

4、发现一个多次出现的文件:spring.factories,全局搜索它

spring.factories

我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!

可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!

所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。

结论:

不简单的方法

我最初以为就是运行了一个main方法,没想到却开启了一个服务;

SpringApplication.run分析

分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行

springApplication这个类主要做了以下四件事情:

1、推断应用的类型是普通的项目还是Web项目

2、查找并加载所有可用初始化器 , 设置到initializers属性中

3、找出所有的应用程序监听器,设置到listeners属性中

4、推断并设置main方法的定义类,找到运行的主类

run方法流程分析

spring自动装配有几种方式

有五种自动装配的方式,可以用来指导 Spring 容器用自动装配方式来进行依赖注

入。

no:默认的方式是不进行自动装配,通过显式设置 ref 属性来进行装配。第 402 页 共 485 页

byName:通过参数名 自动装配,Spring 容器在配置文件中发现 bean

的 autowire 属性被设置成 byname,之后容器试图匹配、装配和该 bean 的属

性具有相同名字的 bean。

byType::通过参数类型自动装配,Spring 容器在配置文件中发现 bean

的 autowire 属性被设置成 byType,之后容器试图匹配、装配和该 bean 的属

性具有相同类型的 bean。如果有多个 bean 符合条件,则抛出错误。

constructor:这个方式类似于 byType, 但是要提供给构造器参数,如

果没有确定的带参数的构造器参数类型,将会抛出异常。

autodetect:首先尝试使用 constructor 来自动装配,如果无法工作,

则使用 byType 方式。

SpringBoot的自动装配(一)

一、什么是SpringBoot的自动装配

? ? SpringBoot的自动装配是指:SpringBoot会自动将一些配置类的bean注册到ioc容器,我们可以在需要的地方使用@Autowired或@Resource等注解来使用它。

? ? 自动的表现形式就是我们只需要引我们享用功能的包,其他的配置完全不需要管,springboot会自动注入这些配置备案,我们直接使用就行。

? ? 自动装配也是SpringBoot的一个重要的特点,他帮我们做了很多的配置。

二、它是怎样实现的?

? ? 1、run方法

? ? ? ? 当启动一个SpringBoot项目时,本质上就是执行了地洞累中的主方法,然后执行了run方法。

????ConfigurableApplicationContext这个对象就是 ApplicationContext接口的一个子接口。

? ? 其实SpringBoot项目的启动,本质上就是一个Spring的初始化操作。

2、@SpringBootApplication

? ? 点开这个注解可以发现,这是一个组合注解,包括:

? ? ????这些注解中,前四个是JDK中的自动元注解,是用来修饰注解的注解。@ComponentScan是用来扫描路径的,如果不置顶特定的扫描路径的话,扫描的路径是当前修饰的类所在的包及其子包。而@SpringBootConfiguration这个注解的本质就是@Configuration注解。所以在这里面跟SpringBoot自动装配有关系的就只有一个了:@EnableAutoConfiguration

? ? 3、@EnableAutoConfiguration

? ??@AutoConfigurationPackage不是用来实现自动装配的,在Spring中,他是用来扫描实体类Entity等注解的。所以重点是在@Import上。

? ? @Import注解实现了AutoConfigurationImportSelector类,自动装配也是在这个类中进行了具体的实现。自动装配实现的就是该类中的selectImports方法里。通过selectImports方法,取到spring.factories文件下的一系列类名,随后将这些类自动加载至IOC容器中。

? ? 4、总结

? ? ? ? SpringBoot的自动装配也就是通过@EnableAutoConfiguration注解,加载AutoConfigurationImportSelector类中的selectImports方法,进而扫描spring.factories文件下的自动配置类,并将其装配到IOC容器的过程。

(责任编辑:IT教学网)

更多

推荐PHP+MySQL视频文章