装配的概念。《spring实战》中给装配下了一个定义:创建应用对象之间协作关系的行为称为装配。也就是说当一个对象的属性是另一个对象时,实例化时,需要为这个对象属性进行实例化。这就是装配。
自动装配:也就是 **** 会在容器中自动的查找,并自动的给 **** 装配及其关联的属性
Spring
提供了 4
种自动装配策略
依赖注入的本质就是装配,装配是依赖注入的具体行为
在传统的使用 xml 文件装配 bean 是一件很繁琐的事情,而且还需要找到对应类型的 bean 才能装配,一旦 bean 很多,就不好维护了。为了解决这种问题。自动装配就是开发人员不必知道具体要装配哪个 bean 的引用,这个识别的工作会由 spring 来完成。与自动装配配合的还有“自动检测”,这个动作会自动识别哪些类需要被配置成 bean,进而来进行装配
因此也可以这样理解:自动装配是为了将依赖注入“自动化”的一个简化配置的 *** 作
3.基于xml中显示配置 3.1.no当使用autowire="no"时,不使用自动装配,依然需要手动装配,需要显式指定某个bean的引用
<bean id="userService" class="com.beans.UserServcie" autowire="no">
<property name="userDao" ref="userDao">property>
bean>
3.2.byName
public class Car {
String color;
public void setColor(String color) {
this.color = color;
}
}
public class Person {
Car car;
public void setCar1(Car car) {
this.car = car;
}
}
<bean id="person" class="com.dream.pojo.Person" autowire="byName"/>
<bean id="car1" class="com.dream.pojo.Car">
<property name="color" value="red"/>
bean>
3.3.byType
把与 **** 的属性具有相同类型的其他 **** 自动装配到 **** 的对应属性中
<bean id="person" class="com.dream.pojo.Person" autowire="byType"/>
3.4.constructor
把与 **** 的构造器入参具有相同类型的其他 **** 自动装配到 **** 构造器的对应入参中。值的注意的是,具有相同类型的其他 bean
这句话说明它在查找入参的时候,还是通过 bean
的类型来确定
public class Person {
Car car;
public Person(Car car){
this.car = car;
}
}
<bean id="person" class="com.dream.pojo.Person" autowire="constructor"/>
默认的自动装配策略
默认情况下,default-autowire 属性被设置为 none,标示所有的 bean 都不使用自动装配,除非 bean 上配置了 autowire 属性
<beans default-autowire="byType"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.beans.UserDao">bean>
<bean id="userService2" class="com.beans.UserServcie" autowire="byName">bean>
beans>
userService1的autowire="byName"则会使用byName装配。
4.使用注解实现自动装配spring2.5之后提供了注解方式的自动装配。但是要使用这些注解,需要在配置文件中配置
。只有加上这一配置,才可以使用注解进行自动装配,默认情况下基于注解的装配是被禁用的。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
beans>
4.1@Autowired 注解
<bean id="person" class="com.dream.pojo.Person"/>
<bean id="car" class="com.dream.pojo.Car"/>
使用 @Autowired
它有几个点需要注意
默认情况下,它具有强制契约特性,其所标注的属性必须是可装配的。如果没有 **** 可以装配到 **** 所标注的属性或参数中,那么你会看到 **** 的异常信息
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
// 查找Bean
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
// 如果拿到的 Bean 集合为空,且 isRequired 为 true,就抛出异常
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(type, "", descriptor);
}
return null;
}
}
看到上面的源码,我们可以得到这一信息,bean
集合为空不要紧,关键 isRequired
条件不能成立,如果成立就会抛异常。那么,如果我们不确定属性是否可以装配,可以这样来使用 Autowired
@Autowired(required = false)
UserService userService;
4.1.2装配策略
我记得曾经有个面试题是这样问的:Autowired
是按照什么策略来自动装配的呢?关于这个问题,不能一概而论,你不能简单的说按照类型或者按照名称。但可以确定的一点的是,它默认是按照类型来自动装配的,即 byType
-
默认按照类型装配
关键点
findAutowireCandidates
这个方法
protected Map<String, Object> findAutowireCandidates(
String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
// 获取给定类型的所有 bean 名称,里面实际循环所有的 beanName,获取它的实例
// 再通过 isTypeMatch 方法来确定
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
// 根据返回的 beanName,获取其实例返回
for (String candidateName : candidateNames) {
if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
result.put(candidateName, getBean(candidateName));
}
}
return result;
}
可以看到它返回的是一个列表,那么就表明,按照类型匹配可能会查询到多个实例。到底应该装配哪个实例呢?我看有的文章里说,可以加注解以此规避。比如 @qulifier、@Primary
等,实际还有个简单的办法
比如,按照 UserService
接口类型来装配它的实现类。UserService
接口有多个实现类,分为 UserServiceImpl、UserServiceImpl2
。那么我们在注入的时候,就可以把属性名称定义为 bean
实现类的名称
@Autowired
UserService UserServiceImpl2;
这样的话,spring
会按照 byName
来进行装配。首先,如果查到类型的多个实例,spring
已经做了判断
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
// 按照类型查找 bean 实例
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
// 如果 bean 集合为空,且 isRequired 成立,就抛出异常
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(type, "", descriptor);
}
return null;
}
// 如果查找的 bean 实例大于 1 个
if (matchingBeans.size() > 1) {
// 找到最合适的那个,如果没有合适的,也抛出异常
String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (primaryBeanName == null) {
throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(primaryBeanName);
}
return matchingBeans.get(primaryBeanName);
}
}
可以看出,如果查到多个实例,determineAutowireCandidate
方法就是关键。它来确定一个合适的 bean
返回。其中一部分就是按照 bean
的名称来匹配
protected String determineAutowireCandidate(Map<String, Object> candidateBeans,
DependencyDescriptor descriptor) {
// 循环拿到的 bean 集合
for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
String candidateBeanName = entry.getKey();
Object beanInstance = entry.getValue();
// 通过 matchesBeanName 方法来确定 bean 集合中的名称是否与属性的名称相同
if (matchesBeanName(candidateBeanName, descriptor.getDependencyName())) {
return candidateBeanName;
}
}
return null;
}
最后我们回到问题上,得到的答案就是:*** 默认使用 **** 来装配属性,如果匹配到类型的多个实例,再通过 **** 来确定 ***。
总结:
常用的自动装配注解有以下几种:@Autowired,@Resource,@Inject,@Qualifier,@Named。@Autowired注解是byType类型的,这个注解可以用在属性上面,setter方面上面以及构造器上面。使用这个注解时,就不需要在类中为属性添加setter方法了。但是这个属性是强制性的,也就是说必须得装配上,如果没有找到合适的bean能够装配上,就会抛出异常。这时可以使用required=false来允许可以不被装配上,默认值为true。当required=true时,@Autowired要求必须装配,但是在没有bean能装配上时,就会抛出异常:NoSuchBeanDefinitionException,如果required=false时,则不会抛出异常。另一种情况是同时有多个bean是一个类型的,也会抛出这个异常。此时需要进一步明确要装配哪一个Bean,这时可以组合使用@Qualifier注解,值为Bean的名字即可。@Qualifier注解使用byName进行装配,这样可以在多个类型一样的bean中,明确使用哪一个名字的bean来进行装配。@Qualifier注解起到了缩小自动装配候选bean的范围的作用。注意:@Autowired注解是spring提供的,所以会依赖spring的包。
还有一个byType的注解@Inject,与@Autowired注解作用一样,也是byType类型,而且是java ee提供的,完全可以代替@Autowired注解,但是@Inject必须是强制装配的,没有required属性,也就是不能为null,如果不存在匹配的bean,会抛出异常。
@Autowired与@Qualifier可以组合使用,@Inject也有一个组合的注解,就是@Named注解,与@Qualifier作用一样,也是byName,但是不是spring的,是java ee标准的。
这样就出现了两套自动装配的注解组合,@Autowired与@Qualifier是spring提供的,@Inject与@Named是java ee的。
但是@Qualifier注解在java ee中也有一样,作用与spring的@Qualifier注解一模一样,只是所在的包不一样。不过建议大家使用spring的。最后还有一个@Resouce注解, 这个注解也是java ee的,也是byName类型的,原理同@Qualifier和@Named是一样的。
@Resource和@Autowired
-
都是用来自动装配的,都可以放在属性字段上
-
实现方式不同
-
@Autowired默认通过bytype的方式实现,如果有多个类型,则通过byname实现,如果两个都找不到,就报错!
-
@Resource默认通过byname的方式实现,如果找不到名字,则通过bytype实现,如果两个都找不到,就报错!
-
-
执行的顺序不同:
-
@Autowired默认通过bytype的方式实现
-
@Resource默认通过byname的方式实现
-
-
类型重复的话,如果名字不是默认的(如cat1而没有默认的cat)
-
@Autowired配合@Qualifier(value = “cat1”)使用
-
@Resource直接使用@Resource(name = “cat1”)
-
需开启注解扫描
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.dream.pojo"/>
beans>
@Component //等价于
@Scope("prototype")//
public class Person {
@Autowired
Car car;
}
@Component //等价于
public class Car {
@Value("red") //等价于
String color;
}
衍生的注解
@Component有几个衍生的注解,我们在web开发中,会按照mvc三层架构分层!
-
dao【@Repository】
-
service 【@Service】
-
controller 【@Controller】
这四个注解功能都是一样的,都是代表将某个注册类注入到Spring中,装配Bean
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)