Spring使用注解进行对象装配(DI)的操作

 更新时间:2023年07月28日 11:18:02   作者:韵秋梧桐  
获取Bean对象也叫做对象装配,就是把对象取出来放到某个类中,有时候也叫对象注入,这篇文章主要介绍了Spring使用注解进行对象装配(DI),需要的朋友可以参考下

一. 什么是对象装配

获取 Bean 对象也叫做对象装配,就是把对象取出来放到某个类中,有时候也叫对象注入。对象装配(对象注入)的实现方法以下 3 种:

  • 属性注入 ,就是将对象注入到某个类的一个属性当中。
  • 构造方法注入 ,就是通过构造方法来将对象注入到类中。
  • Setter 注入 ,通过 SetXXX 系列方法将对象注入到类中。

常见有关对象注入的注解有两个,一个是@Autowired,另外一个是@Resource

🍂它们两者的区别如下:

  • 出身不同:@Autowired 是由Spring提供的,而 @Resource 是JDK提供的。
  • 查找顺序不同:从容器中获取对象时 @Autowired 先根据类型再根据名称查询,而 @Resource 先根据名称再根据类型查询。
  • 使⽤时设置的参数不同:@Resource 支持更多的参数设置(有 7 个),如name、type等,而@Autowired只支持设置required一个参数,用来设置注入 Bean 的时候该 Bean 是否必须存在(true/false)。

img

img

  • 依赖注入支持不同:@Autowired 支持属性注入,构造方法注入和 Setter 注入,而 @Resource 只支持属性注入和 Settter 注入,但是不支持构造方法注入。
  • 对 IDEA 的兼容性支持不同:使用 @Autowired 在 IDEA 旗舰版下可能会有误报(设置required即可);而 @Resource 不存在误报的问题。

二. 三种注入方式

1. 属性注入

属性注入只需要在需要注入对象的属性上加上 @Autowired 或者 @Resource 注解就可以了,这里以 @Autowired 为例。

首先来看第一种情况,待注入的同类对象只有一个,此时我们直接使用 @Autowired 注解就好,不必设置参数,例如我们在UserController类里面注入UserService对象。

下面UserService的结构,先使用 @Service 将 Bean 存放到 Spring 中:

属性注入:

package com.tr.demo.controller;
import com.tr.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
    //属性注入
    @Autowired
    private UserService userService;
    public void sayHi() {
        userService.sayHi();
    }
}

此时我们就可以在启动类中,使用上下文对象来获取到UserController对象,通过执行UserController对象的sayHi方法来进而调用到注入的UserService对象中的sayHi方法了,此时的UserService对象就不是我们自己new出来的了。

import com.tr.demo.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        UserController usercontroller =  context.getBean("userController", UserController.class);
        usercontroller.sayHi();
    }
}

运行结果:

img

上面说的是同类对象只有一个的情况,而如果存在多个同类对象,我们就得通过参数来告知容器我们要注入哪一个对象,不告知就会报错。

比如我们将多个User对象添加到容器中,如下:

package com.tr.demo.model;
// User 结构
public class User {
    private int id;
    private String name;
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
package com.tr.demo.model;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class UserBeans {
    // 使用方法注解添加多个 User 对象到容器中
    @Bean("user1")
    public User user1(){
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
    @Bean("user2")
    public User user2(){
        User user = new User();
        user.setId(2);
        user.setName("李四");
        return user;
    }
    @Bean("user3")
    public User user3(){
        User user = new User();
        user.setId(3);
        user.setName("王五");
        return user;
    }
}

而在UserController2类中需要注入User对象,此时我们运行程序就会报错:

package com.tr.demo.controller;
import com.tr.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController2 {
    @Autowired
    private User user;
    public void sayHi() {
        System.out.println("Hello, " + user);
    }
}

我们试着以同样的方法来调用sayHi方法:

运行结果:

@Autowired 依赖注入流程首先是先根据类型从容器中获取对象,如果只能获取到一个,那么就直接将此对象注入到当前属性上;如果能获取到多个对象,此时会使用 BeanName 进行匹配,而我们添加到 Spring 中的对象是没有一个叫user的,所以程序就报错了。

img

此时就需要我们来告知容器我们需要哪一个具体的 Bean,要获得目标对象主要有下面三种方法:

1️⃣方法1:将属性的变量名设置为你需要的那个BeanName就可以了,后面的构造方法与 Setter 注入同理,将形参名设置成与BeanName相同即可。

img

img

2️⃣方法2:@Autowired 注解与 @Qualifier 注解配合使用,设置 @Qualifier 的value参数为BeanName即可,要注意 @Qualifier 注解不能修饰方法,只能修饰变量。

img

img

3️⃣方法3:将 @Autowired 注解替换成 @Resource 注解的,并将它name参数值设置为BeanName即可。

img

img

2. 构造方法注入

在构造方法加上 @Autowired 注解就可,要注意 @Resource 注解是不支持构造方法注入的,我们就直接演示如何获取取多个同类对象中的其中一个了,还是用上面添加到容器中的多个 User 对象。

方法1:将构造方法形参名设置为user1

package com.tr.demo.controller;
import com.tr.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController3 {
    private User user;
    @Autowired
    public UserController3(User user1) {
        this.user = user1;
    }
    public void sayHi() {
        System.out.println("Hello, " + user);
    }
}

启动类就不贴代码了,一样的,运行结果如下:

img

方法2:@Autowired 搭配 @Qualifier

package com.tr.demo.controller;
import com.tr.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
@Controller
public class UserController4 {
    private User user;
    @Autowired
    public UserController4(@Qualifier(value = "user2") User user) {
        this.user = user;
    }
    public void sayHi() {
        System.out.println("Hello, " + user);
    }
}

运行结果:

img

对了,如果一个类中只有一个构造方法,@Autowired 是可以省略的,演示一下:

package com.tr.demo.controller;
import com.tr.demo.model.User;
import org.springframework.stereotype.Controller;
@Controller
public class UserController5 {
    private User user;
    public UserController5(User user3) {
        this.user = user3;
    }
    public void sayHi() {
        System.out.println("Hello, " + user);
    }
}

此时仍然是可以成功注入对象。

img

如果有多个构造方法,要注意此时是不能省略 @Autowired 的,会导致会注入对象失败。

package com.tr.demo.controller;
import com.tr.demo.model.User;
import org.springframework.stereotype.Controller;
@Controller
public class UserController6 {
    private User user;
    public UserController6(User user1) {
        this.user = user1;
    }
    public UserController6() {
        System.out.println("调用无参构造");
    }
    public void sayHi() {
        System.out.println("Hello, " + user);
    }
}

此时可以看到注入对象失败了,输出的结果是null

img

当然此时加上 @Autowired 注解就能正常注入了,就不做展示了。

3. Setter注入

Setter 注入就是在 setXXX 系列方法上加上 @Resource 或者 @Autowired 进行注入,和构造方法注入大同小异,简单演示一下。

package com.tr.demo.controller;
import com.tr.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
@Controller
public class UserController7 {
    private User user;
    @Autowired
    public void setUser(@Qualifier(value = "user2") User user) {
        this.user = user;
    }
    public void sayHi() {
        System.out.println("Hello, " + user);
    }
}

启动类和运行结果:

img

这里这里第一行输入的是因为启动程序会将上面写的UserController6也添加到容器中,UserController6的无参构造方法是我们自定义的。

三. 三种注入方式的优缺点

在早期的 Spring 版本中,官方推荐使用的 Setter 注入,最开始说的原因就是不符合单一设计原则吧,而现在比较新的 Spring 版本(Sring 4.x 之后)中,官方最使用推荐的又是构造方法注入了,说法是因为它的通用性最好。

🎯属性注入

优点:

  • 使用起来简单方便

缺点:

  • 无法注入到一个final修饰的变量,因为 final 修饰的变量只有两种赋值方式,一是直接赋值,二是通过构造方法进行赋值,而属性注入这两种方式都不能满足。

img

2.通用性问题,属性注入只能在 IoC 容器中使用,在非 IoC 容器中是不可⽤的。

3.更容易违背单一设计原则,简单理解就是注入方式越简单,滥用的概率越大,就比如在数据持久层有一个针对用户操作的类,本来这个类就只是注入用户相关操作的依赖就行了,但由于属性注入使用起来成本不高,程序猿就多注了一些依赖去完成了一些和用户操作无关的内容,这就违背了单一设计原则了。

🎯Setter 注入

优点:

  • 通常情况下,setXXX 系列的方法中只会设置一个属性,就更符合单一设计原则。

缺点:

  • 同样的,也不能注入到一个 final 修饰的变量中。

img

  • 注入的对象是可能被修改的,因为 setXXX 系列的方法随时都有可能被调用导致注入的 Bean 就被修改了。

🎯构造方法注入

优点:

可以注入到一个被 final 修饰的变量。

img

  • 注入对象不会被修改,因为构造方法只会在对象创建时执行一次,不存在注入对象被随时修改的情况。
  • 可以保证注入对象的完全初始化,因为构造方法是在对象创建之前执行的。
  • 通用性最好,因为不管你怎么写 Java 代码,创建实例对象时都要执行构造方法吧。

缺点:

  • 相较于属性注入,写法更加复杂,如果有多个注⼊会显得⽐较臃肿,但出现这种情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了。
  • 使用构造注入,无法解决循环依赖的问题。

四. 综合练习

在 Spring 项⽬中,通过 main ⽅法获取到 Controller 类,调⽤ Controller ⾥⾯通过注⼊的⽅式调⽤ Service 类,Service 再通过注⼊的⽅式获取到 Repository 类,Repository 类⾥⾯有⼀个⽅法构建⼀ 个 User 对象,返回给 main ⽅法。Repository ⽆需连接数据库,使⽤伪代码即可。

首先要清楚的是在 main 方法中是不能使用依赖注入的,因为类的静态部分是在 Spring 注入之前的加载的,仔细想一下,在类加载时就要使用一个还没注入的对象这是不现实的。

所以我们要在 main 中执行的是将扫描路径中的类添加到 Spring 中,对象的注入要在 mian 方法所在类的外部去实现。

img

运行结果:

img

到此这篇关于Spring使用注解进行对象装配(DI)的文章就介绍到这了,更多相关spring对象装配内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 完美解决MybatisPlus插件分页查询不起作用总是查询全部数据问题

    完美解决MybatisPlus插件分页查询不起作用总是查询全部数据问题

    这篇文章主要介绍了解决MybatisPlus插件分页查询不起作用总是查询全部数据问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • Maven3种打包方式中maven-assembly-plugin的使用详解

    Maven3种打包方式中maven-assembly-plugin的使用详解

    这篇文章主要介绍了Maven3种打包方式中maven-assembly-plugin的使用,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • 基于resty security的Api权限控制与事务支持

    基于resty security的Api权限控制与事务支持

    这篇文章主要为大家介绍了基于resty security的Api权限控制与事务支持让数据操作处于事务控制下,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-03-03
  • java 从int数组中获取最大数的方法

    java 从int数组中获取最大数的方法

    这篇文章主要介绍了java 从int数组中获取最大数的方法,需要的朋友可以参考下
    2017-02-02
  • 解读Spring框架中常用的设计模式

    解读Spring框架中常用的设计模式

    这篇文章主要介绍了解读Spring框架中常用的设计模式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • java实现简单汽车租赁系统

    java实现简单汽车租赁系统

    这篇文章主要为大家详细介绍了java实现简单汽车租赁系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-01-01
  • spring整合atomikos实现分布式事务的方法示例

    spring整合atomikos实现分布式事务的方法示例

    本文整合了一个spring和atomikos的demo,并且通过案例演示说明atomikos的作用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • java中你的项目应该如何正确分层

    java中你的项目应该如何正确分层

    这篇文章主要介绍了java中你的项目应该如何正确分层,业务分层对于代码规范是比较重要,决定着以后的代码是否可复用,感兴趣的可以了解一下
    2021-04-04
  • Java static关键字详细解析

    Java static关键字详细解析

    这篇文章主要介绍了Java static关键字详细解析,java中的static关键字主要用于内存管理,可以用在变量、方法、代码块和嵌套类中。更多相关介绍,需要的小伙伴可以参考一下
    2022-08-08
  • Java生产者消费者模式实例分析

    Java生产者消费者模式实例分析

    这篇文章主要介绍了Java生产者消费者模式,结合实例形式分析了java生产者消费者模式的相关组成、原理及实现方法,需要的朋友可以参考下
    2019-03-03

最新评论