首先明确一点:@Autowired @Qualifier 属于 Spring 定义的注解,@Resource 属于 JSR-250 规范定义的注解。以上注解 Spring 都支持。文章仅代表个人观点,如有不正之处,欢迎批评指正。
根据 bean 类型进行对象注入。@Autowired 注解始于 Spring2.5。
包路径:org.springframework.beans.factory.annotation
@Autowired 注解的源码:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* Defaults to {@code true}.
*/
boolean required() default true;
}
从源码中可以看出,@Autowired 修饰的对象包含:构造函数、方法、参数、字段、注解类型。
它包含一个 required 属性,用来声明注解的对象是否是必须的,默认值为true。
通过 @Retention(RetentionPolicy.RUNTIME) 元注解,我们可以得知,@Autowired 可以在 JVM 运行被其他代码读取和使用。
我们可以通过 @Autowired 根据 bean 类型进行对象注入,如果我们想要根据 bean 名称注入,该怎么办呢?Spring 提供了 @Qualifier 注解,通过它进行 bean 名称注入。@Qualifier 注解始于 Spring2.5。
包路径:org.springframework.beans.factory.annotation
@Qualifier 注解的源码:
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default "";
}
从源码中可以看出,@Qualifier 修饰的对象范围包含:字段、方法、参数、类型、注解类型,它修饰的对象范围与 @Autowired 的范围有所不同。
它包含一个 value 属性,用来指定注入的 bean 的名称,默认值为空。
通过 @Retention(RetentionPolicy.RUNTIME) 元注解,我们也可以得知,@Qualifier 可以在 JVM 运行被其他代码读取和使用。
通过上面的讲解我们了解到:@Autowired 可以根据类型注入对象,@Qualifier 可以根据 bean 名称注入对象。那 @Resource 的作用是什么呢?@Resource 属于 JSR-250 规范定义的注解,支持根据类型和名称进行对象注入。@Resource 注解始于 jdk1.6。
包路径:javax.annotation (从包路径也可以看出该注解不属于 Spring)
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
/**
* The JNDI name of the resource. For field annotations,
* the default is the field name. For method annotations,
* the default is the JavaBeans property name corresponding
* to the method. For class annotations, there is no default
* and this must be specified.
*/
String name() default "";
/**
* The name of the resource that the reference points to. It can
* link to any compatible resource using the global JNDI names.
*
* @since 1.7, Common Annotations 1.1
*/
String lookup() default "";
/**
* The Java type of the resource. For field annotations,
* the default is the type of the field. For method annotations,
* the default is the type of the JavaBeans property.
* For class annotations, there is no default and this must be
* specified.
*/
Class type() default java.lang.Object.class;
/**
* The two possible authentication types for a resource.
*/
enum AuthenticationType {
CONTAINER,
APPLICATION
}
/**
* The authentication type to use for this resource.
* This may be specified for resources representing a
* connection factory of any supported type, and must
* not be specified for resources of other types.
*/
AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
/**
* Indicates whether this resource can be shared between
* this component and other components.
* This may be specified for resources representing a
* connection factory of any supported type, and must
* not be specified for resources of other types.
*/
boolean shareable() default true;
/**
* A product specific name that this resource should be mapped to.
* The name of this resource, as defined by the name
* element or defaulted, is a name that is local to the application
* component using the resource. (It's a name in the JNDI
* java:comp/env
namespace.) Many application servers
* provide a way to map these local names to names of resources
* known to the application server. This mapped name is often a
* global JNDI name, but may be a name of any form.
*
* Application servers are not required to support any particular
* form or type of mapped name, nor the ability to use mapped names.
* The mapped name is product-dependent and often installation-dependent.
* No use of a mapped name is portable.
*/
String mappedName() default "";
/**
* Description of this resource. The description is expected
* to be in the default language of the system on which the
* application is deployed. The description can be presented
* to the Deployer to help in choosing the correct resource.
*/
String description() default "";
}
从源码中可以看出,@Resource 修饰的对象范围包含:类型、字段、方法,它修饰的对象范围与 @Autowired、@Qualifier 的范围有所不同。
它包含属性如下:
通过 @Retention(RetentionPolicy.RUNTIME) 元注解,我们也可以得知,@Resource 可以在 JVM 运行被其他代码读取和使用。
关于 @Resource name 和 type 的说明:@Resource默认按根据 name 进行注入的。
###1、既不指定 name 属性,也不指定 type 属性###
Spring 会根据属性名称(userMapper1、userMapper2)从上下文中找到唯一匹配的 bean 进行装配。如果没有找到符合的bean,则回退为一个原始类型进行进行查找,如果找到就注入。
示例:
@Resource
private UserMapper userMapper1;
@Resource
private UserMapper userMapper2;
配置:
如上,没有指定 @Resource 的 name 和 type,此时 Spring 会根据 userMapper1 userMapper2 查找 bean,没有找到,之后 Spring 会根据这两个对象的类型查找 bean,通过配置文件我们可以发现,存在两个 UserMapper 类型的 bean,这时 Spring 报错,提示expected single matching bean but found 2:
。
如果 @Resource 注解修饰的是方法,默认会从 set 方法中截取相应的属性名称,之后会根据属性名称查找 bean,如下:
@Resource
public UserMapper setUserMapper(){
return this.userMapper;
}
Spring 会从setUserMapper
方法中截取userMapper
作为属性名称,之后会根据属性名称查找 bean。
###2、只指定 name 属性,不指定 type 属性###
Spring 会根据 name 从上下文中找到唯一匹配的 bean 进行装配。
###3、不指定 name 属性,只指定 type 属性###
Spring 会根据 type 从上下文中找到唯一匹配的 bean 进行装配,找不到或者找到多个,都会抛出异常。
###4、既指定 name 属性,又指定 type 属性###
Spring 会根据 name 和 type 属性从上下文中找到唯一匹配的 bean 进行装配,找不到则抛出异常。