@Autowired、@Resource和@Inject注解的区别
Java SpringBoot @Autowired @Resource @Inject
在Spring中依赖注入可以使用@Autowired
、@Resource
和@Inject
来完成,并且在一般的使用中是可以相互替换的(注意是一般),不过三者还是有区别,他们三者的区别如下:
@Autowired
- Spring本身替换的注解(org.springframework.beans.factory.annotation.Autowired),需要导入Spring相应的jar包才能使用
- 可以标注的位置:构造器、方法、方法参数、变量域和注解上面
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
- 在Spring容器解析
@Autowired
注解时,使用的后置处理器为AutowiredAnnotationBeanPostProcessor
,在这个后置处理的注释中有这么一段:
{@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessor}
* implementation that autowires annotated fields, setter methods, and arbitrary
* config methods. Such members to be injected are detected through annotations:
* by default, Spring's {@link Autowired @Autowired} and {@link Value @Value}
* annotations.
*
* <p>Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation,
* if available, as a direct alternative to Spring's own {@code @Autowired}.
@Autowired
注解有一个required
属性,当指定required
属性为false
时,意味着在容器中找相应类型的bean,如果找不到则忽略,而不报错(这一条是两个注解所没有的功能)。- 默认优先按照类型去容器中找对应的组件,找到就赋值,如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找,如果组件id对象的bean不存在,而且
required
属性为true
,就报错。 - 支持
@Primary
注解,让Spring进行自动装配的时候,默认使用首选的bean。
@Resource
- JSR250规范提供的注解(javax.annotation.Resource),不需要导入格外的包,这个注解在JDK的rt.jar包中
- 可以标注的位置:
TYPE
(表示可以标注在接口、类、枚举),FIELD
(变量域)和METHOD
(方法)上面。
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
String name() default "";
String lookup() default "";
Class<?> type() default java.lang.Object.class;
enum AuthenticationType {
CONTAINER,
APPLICATION
}
AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
boolean shareable() default true;
String mappedName() default "";
String description() default "";
}
- 在Spring容器解析
@Resource
注解时,使用的后置处理器为CommonAnnotationBeanPostProcessor
,在这个后置处理的注释中有这么一段:
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
* that supports common Java annotations out of the box, in particular the JSR-250
* annotations in the {@code javax.annotation} package. These common Java
* annotations are supported in many Java EE 5 technologies (e.g. JSF 1.2),
* as well as in Java 6's JAX-WS.
*
* <p>This post-processor includes support for the {@link javax.annotation.PostConstruct}
* and {@link javax.annotation.PreDestroy} annotations - as init annotation
* and destroy annotation, respectively - through inheriting from
* {@link InitDestroyAnnotationBeanPostProcessor} with pre-configured annotation types.
*
* <p>The central element is the {@link javax.annotation.Resource} annotation
* for annotation-driven injection of named beans, by default from the containing
* Spring BeanFactory, with only {@code mappedName} references resolved in JNDI.
* The {@link #setAlwaysUseJndiLookup "alwaysUseJndiLookup" flag} enforces JNDI lookups
* equivalent to standard Java EE 5 resource injection for {@code name} references
* and default names as well. The target beans can be simple POJOs, with no special
* requirements other than the type having to match.
- 默认是按照组件名称进行装配的
为什么说@Autowired
是根据类型,@Resource
是根据组件名称,下面使用代码来进行解释:
@Component
public class Student {
private String num = "1";
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
}
@Configuration
@ComponentScan(basePackages = {"it.cast.autowired"})
public class Config {
@Bean
public Student student1(){
Student student = new Student();
student.setNum("2");
return student;
}
}
@Component
public class Teacher {
@Resource //注意这里使用的@Resource注解,Spring支持注入Map、Conllent类型的属性变量
private Map<String,Student> student;
public Map<String, Student> getStudent() {
return student;
}
public void setStudent(Map<String, Student> student) {
this.student = student;
}
}
public class Test01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Teacher teacher = context.getBean(Teacher.class);
System.out.println(teacher.getStudent());
}
}
//打印结果:
//Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'student' is expected to be of type 'java.util.Map' but was actually of type 'it.cast.autowired.Student'
// at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:392)
// at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
// at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeanByName(AbstractAutowireCapableBeanFactory.java:452)
// at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:527)
// at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:497)
// at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:637)
// at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:180)
// at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
// at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:322)
// ... 12 more
- 可以看到使用
@Resource
标注的Map<String,Student>
类型的时报错,为什么报错?因为Map类型的变量名为student,容器中已经存在了名称为student的bean,其类型为Student,所以会报错,大致步骤为:根据组件名称student去Spring容器中获取相应的Bean,在获取之后,会将获取到的Bean赋值给相应的属性。
如果此时将@Resource
换成@Autowired
时,其打印结果又会如何?
@Component
public class Student {
private String num = "1";
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
}
@Configuration
@ComponentScan(basePackages = {"it.cast.autowired"})
public class Config {
@Bean
public Student student1(){
Student student = new Student();
student.setNum("2");
return student;
}
}
@Component
public class Teacher {
@Autowired //注意这里使用的@Autowired注解,Spring支持注入Map、Conllent类型的属性变量
private Map<String,Student> student;
public Map<String, Student> getStudent() {
return student;
}
public void setStudent(Map<String, Student> student) {
this.student = student;
}
}
public class Test01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Teacher teacher = context.getBean(Teacher.class);
System.out.println(teacher.getStudent());
}
}
//其打印结果:
//{student=it.cast.autowired.Student@61009542, student1=it.cast.autowired.Student@77e9807f}
可以看到系统并没有报错,根据上面两组代码的对比,可以得出以下结论:
success
当使用@Resource
注解时,是根据组件名称进行查找,当使用@Autowired
注解时,是根据类型进行查找的。
- 支持
@Primary
注解,不过首先按照会按照名称进行注入bean,如果Spring IOC容器中没有该Bean,则按照@Primary
注解标注的bean进行装配。
下面验证@Resource
默认是按照组件名称进行装配的和持支@Primary
注解的:
@Configuration
@ComponentScan({"it.cast.resouce"})
public class ResourceConfig {
@Primary //标有Primary注解,使用@Autowired@Inject注解注解时,优先被加载
@Bean
public Y y1(){
Y y = new Y();
y.setI(0);
return y;
}
}
@Component
public class X {
@Resource //这里使用的是@Resource注解,该注解默认按照组件名称进行装配的,所以会优先加载id为y的bean
private Y y;
public Y getY() {
return y;
}
public void setY(Y y) {
this.y = y;
}
}
@Component
public class Y {
private int i = 2;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
测试一下使用@Resource
注解的打印结果:
@Test
public void ResourceConfigTest(){
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(ResourceConfig.class);
X bean = context.getBean(X.class);
System.out.println(bean.getY().getI());
}
//输出结果为:
// 2
//从而验证了@Resource默认按照名称进行加载
此时,将@Resource
注解的属性名称换成y12,这个bean在容器里面没有的
@Configuration
@ComponentScan({"it.cast.resouce"})
public class ResourceConfig {
@Primary //标有Primary注解,使用@Autowired@Inject注解注解时,优先被加载
@Bean
public Y y1(){
Y y = new Y();
y.setI(0);
return y;
}
}
@Component
public class X {
@Resource //这里使用的是@Resource注解,该注解默认按照组件名称进行装配的,所以会优先加载id为y12的bean,
private Y y12; //如果找不到则按Primary注解标注的bean进行注入
public Y getY() {
return y12;
}
public void setY(Y y) {
this.y12 = y;
}
}
@Component
public class Y {
private int i = 2;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
测试一下使用@Resource
注解的打印结果:
@Test
public void ResourceConfigTest(){
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(ResourceConfig.class);
X bean = context.getBean(X.class);
System.out.println(bean.getY().getI());
}
//输出结果为:
// 0
//由于没有找到id为y12的bean,所以注入了使用@Primary标注的bean,
//而且整个程序没有报错,所以验证了@Resource支持@Primary注解
此时,将@Resource
注解换成@Autowired
注解的打印结果:
@Configuration
@ComponentScan({"it.cast.resouce"})
public class ResourceConfig {
@Primary //标有Primary注解,使用@Autowired@Inject注解注解时,优先被加载
@Bean
public Y y1(){
Y y = new Y();
y.setI(0);
return y;
}
}
@Component
public class X {
@Autowired
private Y y; //此时不管名称是y还是y12,都会使用标有Primary注解的bean
public Y getY() {
return y;
}
public void setY(Y y) {
this.y = y;
}
}
@Component
public class Y {
private int i = 2;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
测试一下使用@Autowired
注解的打印结果:
@Test
public void ResourceConfigTest(){
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(ResourceConfig.class);
X bean = context.getBean(X.class);
System.out.println(bean.getY().getI());
}
//输出结果为:
// 0
//从而验证了@Autowired支持@Primary注解
@Inject
- JSR330规范提供的注解(javax.inject.Inject),主要导入javax.inject包才能使用
- 可以标注的位置:方法、构造器和变量域中
@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {} //该注解中没有任何属性
- 在Spring容器解析
@Inject
注解时,使用的后置处理器和@Autowired
是一样的,都是AutowiredAnnotationBeanPostProcessor
。 - 由于
@Inject
注解没有属性,在加载所需bean失败时,会报错。
info
除了上面的不同点之后,**@Inject**
和**@Autowired**
完全等价。
总结
在Spring Boot(或更广泛地,在Spring框架中),@Autowired
、@Resource
和@Inject
注解都用于依赖注入,但它们之间存在一些关键的区别。以下是这些注解的简短总结:
-
@Autowired
@Autowired
是Spring框架特有的注解,用于自动装配Spring容器中的bean。- 它可以应用于构造函数、字段、setter方法或配置方法上。
- 当Spring看到
@Autowired
时,它会尝试按类型匹配bean。如果找到多个相同类型的bean,则会抛出异常,除非指定了@Qualifier
注解来明确指定所需的bean。 @Autowired
要求必须有一个匹配的bean存在,除非设置了required=false
,此时如果没有找到匹配的bean,则字段会被设置为null
(对于非基本类型和String类型)。@Autowired
是Spring框架推荐的依赖注入方式之一,因为它提供了更灵活的依赖解析策略。
-
@Resource
@Resource
是JSR-250标准的一部分,Spring框架也支持这个注解。- 它既可以按名称(name)也可以按类型(type)装配bean,但默认情况下,它会先按名称装配。
@Resource
注解提供了两个属性:name
和type
,可以显式地指定要注入的bean的名称或类型。- 如果同时指定了
name
和type
,并且找到了匹配的bean,那么会按name
进行装配。 @Resource
在没有找到匹配的bean时,会抛出异常(除非是在Java EE 5+环境中,并且设置了shareable
属性为true
,但这种情况在Spring Boot中不常见)。
-
@Inject
@Inject
是JSR-330标准的一部分,用于依赖注入。Spring框架支持这个注解,并允许它作为@Autowired
的替代方案。- 它主要按类型进行装配,但如果bean名称与字段名或setter方法名相匹配,则也可以按名称装配(这取决于具体的实现)。
@Inject
和@Autowired
非常相似,但在一些方面有所不同,比如@Inject
不支持required
属性(因为它是JSR-330标准的一部分,而不是Spring特有的)。- 使用
@Inject
的一个主要原因是,它使得代码更加独立于Spring框架,从而更易于在遵循JSR-330标准的其他DI容器之间迁移。
总的来说,在Spring Boot项目中,@Autowired
是最常用的依赖注入注解,因为它简单且灵活。然而,在某些情况下,你可能更倾向于使用@Resource
或@Inject
,特别是当你需要更明确的装配策略或希望代码更易于迁移到其他DI容器时。
评论区(0)