@Autowired、@Resource、@Inject和@Service

一、区别总结

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:对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。

三、注解的效应

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

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

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

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

1️⃣看一个不使用注解的 Spring 示例,在这个示例的基础上,改成注解版本的,这样也能看出使用与不使用注解之间的区别,先定义一个老师:

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

再定义一个学生:

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

然后定义一个学校:

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 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" />

这是最初始的 .xml 配置。

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.java 也很简练,getter/setter 都可以去掉:

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

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

xml优先原则
值得注意的是,假如 .xml 文件 bean 里面有 property,而 School.java 里面却去掉了属性的 getter/setter,并使用@Autowired注解标注这两个属性会怎么样?答案是 Spring 会按照 xml 优先的原则去 School.java 中寻找这两个属性的 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();
 }
}

注意 @Qualifier 注解括号里面的必须是 bean 的名字,即 Person 接口实现类的类名首字母小写如“doctor”,否则会报错。

3️⃣@Resource

@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.java为例,Teacher.java和Student.java同理:

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

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

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

如果不想用这种形式怎么办,就想让School.java在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.java 了。

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

注意:
假如 school 包下有 Teacher、wg 包下也有 Teacher,它们二者都加了 @Service 注解,那么在 School.java 中即使明确表示要引用的是 wg 包下的 Teacher,程序运行的时候依然会报错。这是因为,两个 Teacher 都使用 @Service 注解标注,意味着两个 bean 的名字都是“teacher”,那么在 School.java 中自动装配的是哪个 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]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容

  • 2.1 我们的理念是:让别人为你服务 IoC是随着近年来轻量级容器(Lightweight Container)的...
    好好学习Sun阅读 2,702评论 0 11
  • Spring容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof阅读 2,799评论 1 24
  • 1.Spring整体架构 1)核心容器(Core Container) Core模块,主要包含了Spring框架基...
    Sponge1128阅读 1,057评论 0 1
  • 参考W3C Spring教程 Spring致力于J2EE应用的各种解决方案,而不仅仅专注于某一层解决方案。可以说S...
    王侦阅读 1,153评论 0 6
  • 还记得没上初中前,我觉得我对生活的感知是很强的。 我记得小的时候,很喜欢我家门口的那条巷子,因为它坑坑洼洼的,在每...
    桐雨羲阅读 2,089评论 2 8