当Spring装配Bean属性时,有时候非常明确,就是需要将某个Bean的引用装配给指定属性。比如,如果我们的应用上下文中只有一个 orgmybatisspringSqlSessionFactoryBean 类型的Bean,那么任意一个依赖 SqlSessionFactoryBean 的其他Bean就是需要这个Bean。毕竟这里只有一个 SqlSessionFactoryBean 的Bean。
为了应对这种明确的装配场景,Spring提供了自动装配(autowiring)。与其显式的装配Bean属性,为何不让Spring识别出可以自动装配的场景。
当涉及到自动装配Bean的依赖关系时,Spring有多种处理方式。因此,Spring提供了4种自动装配策略。
Spring在 AutowireCapableBeanFactory 接口中定义了这几种策略。其中, AUTOWIRE_AUTODETECT 被标记为过时方法,在Spring30之后已经不再支持。
它的意思是,把与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。不过,它在Spring30之后已经被标记为 @Deprecated 。
默认情况下,default-autowire属性被设置为none,标示所有的Bean都不使用自动装配,除非Bean上配置了autowire属性。
如果你需要为所有的Bean配置相同的autowire属性,有个办法可以简化这一 *** 作。
在根元素Beans上增加属性 default-autowire="byType" 。
Spring自动装配的优点不言而喻。但是事实上,在Spring XML配置文件里的自动装配并不推荐使用,其中笔者认为最大的缺点在于不确定性。或者除非你对整个Spring应用中的所有Bean的情况了如指掌,不然随着Bean的增多和关系复杂度的上升,情况可能会很糟糕。
从Spring25开始,开始支持使用注解来自动装配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的包在 javaxannotationPriority; ,如果想使用它还要引入一个坐标。
本章节重点阐述了Spring中的自动装配的几种策略,又通过源码分析了Autowired注解的使用方式。
在Spring30之后,有效的自动装配策略分为 byType、byName、constructor 三种方式。注解Autowired默认使用byType来自动装配,如果存在类型的多个实例就尝试使用byName匹配,如果通过byName也确定不了,可以通过Primary和Priority注解来确定。
背景:
SpringBoot日常开发中不需要接触原生>
在本文中,我们将详细介绍从BeanFactory中获取bean的多种方式。
简单地说,正如方法的名称所表达的, getBean() 负责从Spring IOC容器中获取bean实例。
首先,让我们定义一些用于测试的Spring bean。创建spring IOC容器有多种方式,但是在本文中,我们将使用基于注释的Java配置:
我们创建了两个bean。 Lion 具有默认的单例作用域。Tiger被显式地设置为 prototype 。另外,我们为每个bean定义了名称,这些名称将在后边的实例中使用。
BeanFactory提供了getBean()方法的5个方法,我们将在下面的小节中研究。
让我们看看如何使用名称获取 Lion Bean实例:
在此方法中,我们根据bean名称获取bean,如果在spring ico容器中存在和bean,则返回 Object 类的实例。否则,抛出如下异常NoSuchBeanDefinitionException。
主要的缺点是,在获取bean之后,我们必须将它转换为所需的类型。如果返回的bean的类型与我们期望的不同,则可能会产生异常。
假设我们试图用“tiger”这个名字来得到“lion”。当我们将结果转换为lion时,它会抛出一个ClassCastException:
在这里,我们需要指定所请求bean的名称和类型:
与31的方法相比,此方法更安全,因为我们可以编译阶段就发现错误而不是在运行阶段。
使用 getBean() 的第三种方式, 仅指定bean类型就足够了
在这种情况下,我们需要 特别注意可能存在的歧义 :
在上面的示例中,由于 Lion 和 Tiger都 实现了 Animal 接口,因此仅指定类型不足以明确确定结果。因此,我们得到一个 NoUniqueBeanDefinitionException 。即在同一个IOC 容器中,如果有相同类型的多个bean,则不能通过类型获取bean。
除了bean名称,我们还可以传递构造函数参数:
这个方法有点不同,因为它只适用于具有原型作用域的bean。
在单例的情况下,我们将得到BeanDefinitionStoreException异常。
因为原型bean,每次从spring ioc容器中获取bean都会返回一个新创建的实例,所以我们可以在调用getBean()时动态地提供构造函数参数:
正如我们所看到的,根据我们在请求bean时指定的第二个参数,每个Tiger都有不同的名称。
此方法类似于上一个方法,但是我们需要将类型而不是名称作为第一个参数传递:
与34相似, 此方法仅适用于具有原型作用域的bean 。
getBean()尽管是在BeanFactory接口中定义的,但是getBean()方法大部分是通过ApplicationContext访问。通常,我们在应用程序中不希望直接使用getBean()方法。
Bean应该由容器管理。如果我们想使用它们中的一个,我们应该依赖依赖注入,而不是直接调用ApplicationContextgetBean()。这样,我们就可以避免将应用程序逻辑与与框架相关的细节混合在一起。
在本快速教程中,我们从 BeanFactory 接口浏览了 getBean() 方法的所有实现,并描述了每种方法的优缺点。
以上就是关于彻底搞明白Spring中的自动装配和Autowired全部的内容,包括:彻底搞明白Spring中的自动装配和Autowired、[Kotlin]SpringBoot 获取原生HTTP Request内容实例化model、理解Spring中的getBean()等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)