@Autowired、@Resource、@Inject和@Service

本文详细介绍了Spring框架中的@Autowired、@Resource和@Inject注解的区别和使用方法。@Autowired按照类型匹配,默认按ByName,可通过@Qualifier指定名称。@Resource默认按ByName,其次按ByType,适用于J2EE环境。@Inject遵循JSR330规范,按类型匹配,配合@Named实现ByName。此外,还探讨了@Service在减少XML配置中的作用,以及注解在提升代码可读性和内聚性方面的优势。

一、区别总结

1️⃣

  1. @Autowired 是 Spring 自带的,通过AutowiredAnnotationBeanPostProcessor类实现的依赖注入,Spring 属于第三方的。
  2. @Resource 是 JSR250 规范的实现,J2EE 的注解,在javax.annotation包下,根据导入注解的包名就可以知道。J2EE 是 Java 自己的,建议使用 @Resource 注解,以减少代码和 Spring 之间的耦合。
  3. @Inject 是 JSR330 规范实现的,需要导入javax.inject.Inject jar包,才能实现注入。

2️⃣

  1. @Autowired 可以作用在 constructor、method、parameter、annotation_type、field 上。
  2. @Resource 可以作用 method、field、type 上。
  3. @Inject 可以作用 constructor、method、field 上。

3️⃣

  1. @Autowired 默认按照(byType)进行bean匹配。如果有多个类型一样的 Bean 候选者,需要配合 @Qualifier 按照(byName)进行装配。指定名称后,如果 Spring IOC 容器中没有对应的组件 bean 抛出 NoSuchBeanDefinitionException。也可以将该注解属性 required 配置为 false,找不到相应 bean 的时候,系统不再抛异常。

  2. ①@Resource 默认按照(byName)方式进行自动装配,找不到再按 type 去匹配。如果有多个类型一样的 Bean 候选者,则可以通过 name 属性指定进行注入。
    ②指定了 name 或者 type 则根据指定的类型去匹配 bean。
    ③指定了 name 和 type 则根据指定的 name 和 type 去匹配 bean,任何一个不匹配都会报错。

  3. @Inject 是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合 @Named。

4️⃣@Autowired、@Inject 用法基本一样。不同的是 @Autowired 有一个 request 属性。

二、什么是注解(Annotation)

注解是 JDK1.5 及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。注解是以@注解名在代码中存在的。根据注解参数的个数,可以将注解分为:标记注解、单值注解、完整注解三类。它们都不会直接影响到程序的语义,只是作为注解(标识)存在,可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问。另外,可以在编译时选择代码里的注解是否只存在于源代码级,或者它也能在 class 文件、或者运行时中出现(SOURCE/CLASS/RUNTIME)。

  • @Component:通用注解,可标注任意类为Spring组件。如果一个 Bean 不知道属于哪个层,可以使用 @Component 标注。
  • @Repository:对应持久层即 Dao 层,主要用于数据库相关操作。
  • @Service:对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
  • @Controller:对应SpringMVC控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。

三、注解的效应

传统的 Spring 做法是使用 xml 文件来对 bean 进行注入或者是配置AOP事务,这么做有两个缺点:

  1. 如果所有的内容都配置在 .xml 文件中,会导致 .xml 文件过大;如果按需求分开 .xml 文件,又会导致 .xml 文件过多。总之这会使得配置文件的可读性与可维护性变得很低。
  2. 开发中,在 .java 文件和 .xml 文件之间不断切换,是一件麻烦的事。同时这种思维上的不连贯也会降低开发的效率。

为了解决这两个问题,Spring 引入了注解,通过@注解名的方式,让注解与 Java Bean 紧密结合,既大大减少了配置文件的体积,又增加了 Java Bean 的可读性与内聚性。

四、@Autowired、@Resource、@Inject和@Service

先列不使用注解的 Spring 示例,在该示例的基础上,改成注解版本的,也能看出使用与不使用注解之间的区别。

1️⃣定义老师 Teacher

public class Teacher{
 private String teacherName = "TW";
 public String toString() {
    return "TeacherName:" + teacherName;
 }
}

再定义学生 Student

public class Student{
 private String studentName = "SL";
 public String toString() {
  return "StudentName:" + studentName;
 }
}

然后定义学校 School

public class School{
 private Teacher teacher;
 private Student student;
 public void setTeacher(Teacher teacher){
  this.teacher = teacher;
 }
 public void setStudent(Student student){
  this.student = student;
 }
 public Teacher getTeacher(){
  return teacher;
 }
 public Student getStudent(){
  return student;
 }
 public String toString(){
  return teacher + "\n" + student;
 }
}

Spring的配置文件如下最初始的 .xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<bean id="school" class="com.wg.bean.School" >
    <property name="teacher" ref="teacher" />
    <property name="student" ref="student" />
</bean>
<bean id="teacher" class="com.wg.uu.Teacher" />
<bean id="student" class="com.wg.uu.Student" />

2️⃣@Autowired 自动装配

其作用是替代 Java 代码里面的 getter/setter 与 bean 属性中的 property。如果私有属性需要对外提供的话,getter 应当予以保留。引入 @Autowired,Spring 配置文件如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
 3     xmlns="http://www.springframework.org/schema/beans"  
 4     xmlns:context="http://www.springframework.org/schema/context"  
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6         http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
 7         http://www.springframework.org/schema/context
 8         http://www.springframework.org/schema/context/spring-context-4.2.xsd">
 9     
10     <context:component-scan base-package="com.wg" />
11     
12     <bean id="school"  class="com.wg.bean.School" />
13     <bean id="teacher" class="com.wg.uu.Teacher" />
14     <bean id="student" class="com.wg.uu.Student" />
15     
16 </beans>

注意第10行,为了实现 bean 的自动载入,必须配置 Spring 的扫描器。在 base-package 指明一个包:

<context:component-scan base-package=“com.wg”/>

表明com.wg包及其子包中,如果某个类带有特定的注解@Component@Repository@Service@Controller,就会将该对象作为 Bean 注入进 Spring 容器。有了context:component-scan,另一个context:annotation-config标签就可以移除掉,因为已经被包含进去了。
context:component-scan提供两个子标签:context:include-filtercontext:exclude-filter。各代表引入和排除的过滤。

看到第12行,原来 school 里面应当注入两个属性 teacher、student,现在不需要了。再看下,School,getter/setter 都可以去掉:

public class School{
 @Autowired
 private Teacher teacher;
 @Autowired
 private Student student;
 public String toString(){
  return teacher + "\n" + student;
 }
}

说明:当 Spring 发现@Autowired时,将自动在代码上下文中找到与其匹配(默认是类型匹配)的 Bean,并自动注入到相应的地方去。

xml优先原则
值得注意的是,假如 .xml 文件 bean 里面有 property,而 School 里面却去掉了属性的 getter/setter,并使用@Autowired标注这两个属性会怎么样?答案是 Spring 会按照 xml 优先的原则去 School 中寻找这两个属性的 getter/setter,导致的结果就是初始化 bean 报错。

再假如此时把 .xml 文件的13行、14行两行再给去掉,运行会抛出异常:
Exception in thread “main” org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘School’: Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.zxt.uu.Teacher com.zxt.bean.School.teacher; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.wg.uu.Teacher] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) …
因为,@Autowired要去寻找的是一个 Bean,Teacher 和 Student 的 Bean 定义都给去掉了,自然就不是一个 Bean 了,Spring 容器找不到也很好理解。那么,如果属性找不到又不想让 Spring 容器抛出异常,而就是显示 null,可以吗?可以,其实异常信息里面也给出了提示,就是将@Autowired的 required 属性设置为 false 即可:

public class School{
 @Autowired(required = false)
 private Teacher teacher;
 @Autowired(required = false)
 private Student student;
 public String toString(){
  return teacher + "\n" + student;
 }
}

此时,找不到 teacher、student 两个属性,Spring 容器不再抛出异常而是认为这两个属性为 null。

@Autowired接口注入----->@Qualifier

上面仅仅只是注入一个 Java 类,比较简单。那么如果有一个接口,有多个实现,Bean 里引用的是接口名,又该如何?比如有一个 Person 接口:

public interface Person{
 public String personName();
}

两个实现类 Doctor 和 Police:

@Service
public class Doctor implements Person{
 public String docName(){
  return "Doctor person";
 }
}
@Service
public class Police implements Person{
 public String personName(){
  return "Police person";
 }
}

写一个 PersonFactory,引用 Person:

@Service
public class PersonFactory{
 @Autowired
 private Person person;
 public String toString(){
  return person.personName();
 }
}

Person 接口有两个实现类,Spring 并不知道应当引用哪个实现类,代码报错。这种情况通常有两个解决办法:

  1. 删除其中一个实现类,Spring 会自动去 base-package 下寻找 Person 接口的实现类,发现 Person 接口只有一个实现类,便会直接引用这个实现类。
  2. 实现类就是有多个该怎么办?此时可以使用 @Qualifier 注解:
@Service
public class PersonFactory{
 @Autowired
 @Qualifier("doctor")
 private Person person;
 public String toString(){
 return person.personName();
 }
}

注意:

  1. @Qualifier 括号里必须是 bean 的名字。bean 有指定名,必须是指定名,匹配大小写。
  2. bean 没有额外定义名称时,默认首字母小写类名。
  3. 如果类名前两个或以上字母都为大写,那么 bean 的名字就是类名,匹配大小写。

3️⃣@Resource

作用与 @Autowired 非常相似。先看一下 @Resource:

@Service
public class School{
 @Resource(name = "teacher")
 private Teacher teacher;
 @Resource(type = Student.class)
 private Student student;
 public String toString(){
  return teacher + "\n" + student;
 }
}

4️⃣@Inject

  1. @Inject 是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject jar包,才能实现注入。
  2. @Inject 可以作用 constructor、method、field 上。
  3. @Inject 是根据类型进行自动装配的。如果需要按名称进行装配,则需要配合 @Named;

简单示例:

@Inject
private Car car;

指定加入BMW组件:

@Inject
@Named("BMW")
private Car car;

@Named 的作用类似 @Qualifier

5️⃣@Service

使用@Service,可以更加简化.xml文件配置。因为Spring的配置文件里面还有12行~14行三个bean,应用Spring配置文件里面一个自动扫描的标签,可以把这三个bean也给去掉,增强Java代码的内聚性并进一步减少配置文件。先看一下配置文件:

<context:component-scan base-package="com.wg" />

配置文件看起来特别清爽。下面以 School 为例,Teacher 和 Student 同理:

@Service 
public class School{ 
 @Autowired 
 private Teacher teacher; 
 @Autowired 
 private Student student; 
 public String toString(){ 
  return teacher + "\n" + student; 
 }
} 

这样,School 在 Spring 容器中存在的形式就是“school”,即可以通过ApplicationContext的getBean("school")方法来得到 School。@Service,其实做了两件事情:

  1. 声明 School 是一个 bean。这点很重要,因为 School 是一个 bean,其他的类才可以使用 @Autowired 将 School 作为一个成员变量自动注入。
  2. School 在 bean 中的 id 是“school”,即类名且首字母小写。

如果不想用这种形式怎么办,就想让 School 在 Spring 容器中的名字叫做“School”,可以的:

@Service
@Scope("prototype")
public class School{
 @Autowired
 private Teacher teacher;
 @Autowired
 private Student student;
 public String toString(){
  return "TeacherName:" + teacher + "\nStudentName:" + student;
 }
}

如此就可以通过ApplicationContext的getBean(“School”)方法来得到 School 了。

Spring Bean 默认是单例的,如果不想使用单例,可以在 xml 文件里面配置 bean 的 scope 属性。注解也一样,配置 @Scope 即可,默认是“singleton”即单例,“prototype”表示原型即每次都会new一个新的出来。

注意:
假如 school 包下有 Teacher,wg 包下也有 Teacher,它们二者都加了@Service,那么在School 类中即使明确表示要引用的是 wg 包下的 Teacher,程序运行的时候依然会报错。这是因为,两个 Teacher 都使用 @Service 标注,意味着两个 Bean 的名字都是“teacher”,那么在 School 类中自动装配的是哪个 Teacher 呢?不明确,因此 Spring 容器会抛出BeanDefinitionStoreException,Caused by:
org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name ‘teacher’ for bean class [com.wg.uu.Teacher] conflicts with existing, non-compatible bean definition of same name and class [com.wg.school.Teacher]

<think>首先,用户的问题是:我想理解不同Java注解在依赖注入中的区别。请问Java@Autowired@Inject@Resource注解的区别。 根据系统级指令,我需要: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1]。 - 回答结构清晰,帮助用户逐步解决问题,保证回答真实可靠。 - 参考站内引用:引用[1]到[5]是关于Spring注解的,但用户的问题是关于Java注解在依赖注入中的区别,具体是@Autowired, @Inject, @Resource。 用户提供的引用: - [^1]: 关于@Autowired@Inject的区别。 - [^2]: 处理这些注解的实现类。 - [^3]: 文章目录,@Autowired, @Resource, @Qualifier, @Inject。 - [^4]: Java中三个注解的区别。 - [^5]: 示例代码。 我需要基于这些引用一般知识来回答,因为指令说“上述引用仅供参考”,但我应该参考用户上一次的问题我的回答。用户这是第一次提问,所以我需要基于引用标准知识。 关键点: - @Autowired: Spring框架特定的注解。 - @Inject: Java EE标准注解,基于JSR-330。 - @Resource: Java EE标准注解,基于JSR-250。 区别: 1. **来源**: - @Autowired: Spring框架的一部分。 - @Inject: Java标准(JSR-330),由CDI支持。 - @Resource: Java标准(JSR-250),用于资源注入。 2. **注入方式**: - @Autowired: 默认按类型注入。如果有多个bean,需要配合@Qualifier或按名称。 - @Inject: 类似@Autowired,按类型注入,是Java标准。 - @Resource: 默认按名称注入,名称通过name属性指定。 3. **处理方式**: - Spring中,@Autowired由AutowiredAnnotationBeanPostProcessor处理。 - @Inject由同一个处理器处理(因为@Inject是标准)。 - @Resource由CommonAnnotationBeanPostProcessor处理。 4. **其他特性**: - @Autowired可以用于字段、构造函数、方法。 - @Inject类似。 - @Resource可以用于字段方法。 - @Autowired@Inject支持可选注入(required=false)。 - @Resource有name属性指定bean名称。 结构回答: 1. **介绍**:简要解释依赖注入这些注解的作用。 2. **区别**:逐步比较每个注解的来源、注入方式、使用场景等。 3. **示例**:提供简单代码示例(如果需要,但用户引用中有示例)。 4. **总结**:总结关键区别。 5. **生成相关问题**:在最后添加相关问题。 使用中文。 数学表达式:用户问题可能不需要数学,但如果有,就用LaTeX格式。例如,如果提到公式。 引用:在回答中,当引用特定段落时,添加引用标识。例如,基于引用[1],@Autowired@Inject行为类似。 回答结构: - 标题:Java@Autowired@Inject@Resource注解的区别 - 正文: - 依赖注入概述 - 每个注解的详细解释 - 比较表格或列表 - 示例 - 相关问题 确保引用自然添加。</think>### Java@Autowired@Inject@Resource注解的区别 在Java开发中,依赖注入(Dependency Injection, DI)是一种常见的设计模式,用于解耦组件之间的依赖关系,提高代码的可测试性可维护性。Spring框架提供了多个注解来简化依赖注入的实现,括`@Autowired`、`@Inject``@Resource`。这些注解虽然功能相似,但在来源、注入方式、处理机制适用场景上存在显著差异。下面我将逐步解释它们的区别,帮助您清晰理解。 #### 1. **来源标准** - **@Autowired**:这是Spring框架特有的注解,属于`org.springframework.beans.factory.annotation`。它不遵循任何Java EE标准,而是Spring自定义的实现[^1][^4]。 - **@Inject**:这是一个Java标准注解,基于JSR-330(Java Dependency Injection规范)。它由`javax.inject`提供,是CDI(Contexts and Dependency Injection)的一部分,旨在提供跨框架的统一依赖注入支持[^1][^3]。 - **@Resource**:这也是Java标准注解,基于JSR-250(Common Annotations规范)。它由`javax.annotation`提供,主要用于资源注入(如数据库连接或JNDI资源),但也可用于一般依赖注入[^2][^4]。 简单来说,`@Autowired`是Spring专属,而`@Inject``@Resource`是Java EE标准注解,具有更好的跨框架兼容性。 #### 2. **注入方式匹配规则** 依赖注入的核心是Bean的匹配方式,即如何找到并注入依赖对象。以下是每个注解的默认行为: - **@Autowired**: - 默认按**类型(Type)** 匹配:Spring容器会查找与字段或参数类型匹配的Bean。如果存在多个相同类型的Bean,则需要配合`@Qualifier`注解指定名称,否则会抛出异常。 - 支持按名称注入:如果字段或参数名与Bean名称一致,Spring会优先使用名称匹配(但这不是默认行为,需显式配置)。 - 可选注入:通过`required=false`属性,可以标记依赖为可选(即如果找不到Bean,不报错)。 - 示例代码: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired // 按类型注入UserRepository private UserRepository userRepo; } ``` - **@Inject**: - 行为与`@Autowired`几乎相同:默认按**类型**匹配,也支持`@Qualifier`(或JSR-330的`@Named`)来指定名称。 - 主要区别:`@Inject`是Java标准,不依赖Spring框架,因此更适用于非Spring环境(如Jakarta EE或Quarkus)。在Spring中,它被`AutowiredAnnotationBeanPostProcessor`处理,与`@Autowired`共享相同的执行路径[^1][^2]。 - 示例代码: ```java import javax.inject.Inject; import javax.inject.Named; public class OrderService { @Inject // 按类型注入PaymentProcessor @Named("creditCardProcessor") // 使用@Named指定Bean名称 private PaymentProcessor processor; } ``` - **@Resource**: - 默认按**名称(Name)** 匹配:Spring容器会优先根据字段或参数名查找Bean(例如,字段名`userRepo`对应Bean名`userRepo`)。如果名称不匹配,则回退到按类型匹配。 - 支持`name`属性:可以直接指定Bean名称(如`@Resource(name="myBean")`),避免歧义。 - 不支持可选注入:没有`required`属性,如果找不到Bean,会抛出异常。 - 示例代码: ```java import javax.annotation.Resource; public class ProductService { @Resource(name = "inventoryRepo") // 显式指定Bean名称 private InventoryRepository repo; } ``` #### 3. **处理机制Spring支持** - **处理类**: - `@Autowired``@Inject`由同一个Spring处理器处理:`AutowiredAnnotationBeanPostProcessor`。这意味着在Spring中,它们的行为高度一致,按优先顺序匹配:类型 → 限定符 → 名称[^1][^2]。 - `@Resource`由另一个处理器处理:`CommonAnnotationBeanPostProcessor`,它专注于JSR-250标准注解[^2][^4]。 - **适用场景**: - 使用`@Autowired`:推荐在纯Spring项目中使用,因为它与Spring生态集成更深(如支持`@Qualifier`SpEL表达式)。 - 使用`@Inject`:适合需要跨框架兼容性的场景(例如,从Spring迁移到Jakarta EE),或在非Spring容器中。 - 使用`@Resource`:常用于资源注入(如数据库连接池),或在需要强名称匹配时简化配置。 #### 4. **总结对比** 下表总结了关键区别: | 特性 | @Autowired | @Inject | @Resource | |--------------------|---------------------------|---------------------------|---------------------------| | **来源** | Spring框架专属 | Java标准 (JSR-330) | Java标准 (JSR-250) | | **默认匹配方式** | 按类型 | 按类型 | 按名称 | | **支持名称指定** | 需配合@Qualifier | 需配合@Named | 直接通过name属性 | | **可选注入** | 支持 (required=false) | 支持 | 不支持 | | **处理类** | AutowiredAnnotationBeanPostProcessor | 同@Autowired | CommonAnnotationBeanPostProcessor | | **最佳适用场景** | Spring项目 | 跨框架项目 | 资源注入或名称优先匹配 | #### 5. **实际应用建议** - 在Spring项目中,优先使用`@Autowired`,因为它与Spring特性(如AOP或Profile)无缝集成。 - 如果需要标准化代码,减少框架依赖,选择`@Inject`或`@Resource`。 - 注意:所有注解都支持字段、构造函数方法注入。但避免过度使用,以免增加配置复杂性[^3][^5]。 通过以上分析,您应该能清晰区分这些注解。如果您有具体代码场景,可以提供更多细节,我可以给出针对性建议。 ### 相关问题 1. 如何在Spring中处理多个相同类型的Bean的注入问题? 2. @Qualifier@Named注解在实际项目中如何配合使用? 3. 依赖注入与手动注入(如使用new关键字)相比有哪些优缺点? 4. 在非Spring框架(如Jakarta EE)中,如何实现依赖注入? 5. Spring Boot中这些注解的使用有什么变化或最佳实践?[^3][^4]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JFS_Study

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值