首页 > 其他分享 >DI依赖注入详解

DI依赖注入详解

时间:2024-11-28 23:59:11浏览次数:9  
标签:依赖 DI parts bean 详解 import 注解 UserService 注入

DI依赖注入

声明了一个成员变量(对象)之后,在该对象上面加上注解@AutoWired注解,那么在程序运行时,该对象自动在IOC容器中寻找对应的bean对象,并且将其赋值给成员变量,完成依赖注入。

@AutoWired依赖注入的常见方式

1.属性注入

直接使用@AutoWired进行属性注入:

@Autowired
private UserService userService;

这是最简单的依赖注入方式,其优点是:代码简洁,可以方便快速的开发。其缺点是:直接使用属性注入,隐藏了各类之间的依赖关系:Controller是依赖了Service的,但是在类的结构层面无法看出二者的关联。还有可能会破坏类的封装性:按照封装性的解释来看,我们需要将成员属性设置为私有,并对外提供对应的set/get方法;但是直接使用属性注入,没有对外提供set方法,直接对其赋值,在底层是通过反射对其进行赋值的,实际上是破坏了面向对象的封装性原则的。

2.构造函数注入

通过构造函数的方式完成对成员变量的依赖注入:

private final UserService userService;
@Autowired
public UserController(UserService userService) {
    this.userService = userService;
}

相对于属性注入而言,构造函数注入就能够清晰的看到各类之间的依赖关系,并且基于构造函数注入,可以将userService设置为final,更加安全。但是假如是该类依赖了多个其他的类,都交给IOC容器管理,那么在书写构造方法注入的时候,构造方法的参数将十分多,构造方法将十分臃肿。

注意:如果该类只有一个构造函数,那么该构造函数上的@Autowired注解可以省略

3.setter注入

通过set方法完成对成员变量的依赖注入:

private UserService userService;
@Autowired
public void setUserService(UserService userService) {
    this.userService = userService;
}

优点和构造方法注入类似(但是不能将成员变量设置为final),缺点是需要额外提供set方法,编码繁琐。这种setter注入在开发中基本上不会使用。

在项目开发中,Spring官方推荐构造函数注入,因为其很好的保证了面向对象的特性,并且安全性得到很好的保障;但是在开发中大部分项目都喜欢使用属性注入,因为其简洁、方便开发。所以说要根据项目的具体要求而判断,在简洁性和规范性之间进行取舍。(setter注入基本不用)

使用@AutoWired注解的注意事项

类型注入

@Autowired注解在进行依赖注入时,默认是依据类型进行注入操作。然而,倘若存在需要注入的对象有多个对应的 bean实例时,就会引发错误:

这是第一个UserService的bean

package com.wzb.service.impl;

import com.wzb.dao.UserDao;
import com.wzb.pojo.User;
import com.wzb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Service层实现类
 *
 */
@Component("newName")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;
    // 获取用户数据的代码不能写在此处,类的成员变量初始化是在类的实例化阶段进行的,此时可能@AutoWired注入还未完成,导致Null
    // private final List<String> lines = userDao.findUser();

    public List<User> findUser() {
        List<String> lines = userDao.findUser();
        List<User> userList = lines.stream().map(line -> {
            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts[0]);
            String username = parts[1];
            String password = parts[2];
            String name = parts[3];
            Integer age = Integer.parseInt(parts[4]);
            LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            return new User(id, username, password, name, age, updateTime);
        }).collect(Collectors.toList());
        return userList;
    }
}

这是第二个UserService对象的bean

package com.wzb.service.impl;

import com.wzb.dao.UserDao;
import com.wzb.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 用于测试用的bean
 * 
 */
@Component
public class UserServiceImpl2 implements UserService{

    @Autowired
    private UserDao userDao;
    
    public List<User> findUser() {
        List<String> lines = userDao.findUser();
        List<User> userList = lines.stream().map(line -> {
            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts[0]) + 200;
            String username = parts[1];
            String password = parts[2];
            String name = parts[3];
            Integer age = Integer.parseInt(parts[4]);
            LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            return new User(id, username, password, name, age, updateTime);
        }).collect(Collectors.toList());
        return userList;
    }
}

此时Controller中需要依赖UserService这个类,但是IOC容器中有两个这个对象的bean,此时如果直接启动,程序将会直接报错:

 报错信息:

根据报错信息的描述来看,是因为UserController需要一个bean(也就是UserService的实现类),但是在IOC容器中找到了两个该对象的bean实例,不知道应该注入哪一个,所以说就报错了。并且还在Action中提供了一个解决方法:使用@Primary、@Qualifier注解。这证明了不能直接将同一个对象的多个bean加入IOC容器管理,如果一个对象有多个bean都需要给IOC容器管理,那么就需要使用其他注解,来成功找到需要的bean并注入。

解决方法

@Primary

假如需要注入的对象在IOC容器中存在多个bean实例,那么就可以在想要被注入的bean上添加注解@Primary:

使用@Primary注解,指定注入的UserService bean实例是UserServiceImpl2,将服务启动结合前端页面查看结果:

 

 

发现用户的id都加了200,说明此时注入的UserService实例bean是UserServiceImpl2,@Primary注解成功指定了注入的bean实例。

@Qualifier

@Qualifier注解需要配合@AutoWired注解使用,@Qualifier注解是在声明对象时使用,可以通过注解名(默认是类名小写)指定需要注入的bean实例对象。

通过@Qualifier注解和@AutoWired注解结合使用,指定需要注入UserService的bean实例是UserServiceImpl(注意,bean名字是类名小写):

 

 

发现通过@Qualifier注解和@AutoWired注解结合使用,成功将UserService需要注入的bean实例指定为了UserServiceImpl。

重要一点

此时UserServiceImpl2上面的@Primary注解还在,但是注入的是UserServiceImpl,说明@Qualifiler的优先级高于@Primary。

@Resource

@Resource注解是在声明对象时使用,单独使用,通过注解名指定需要注入的bean实例:

@Resource注解不是Spring提供的,是JavaEE规范中提供的,使用时需要指定name = "bean名",同样,此时UserServiceImpl2的@Primary注解还在,但是注入的是@Resource注解指定的bean实例,所以说@Resource注解的优先级也高于@Primary注解。

但是假如将@Resource和@Qualifier注解一起用:

 

 

其注入的bean是@Resource注解指定的,说明@Resource的优先级高于@Qualifier;可能是因为原生JavaEE的优先级高于SpringBoot框架的缘故。

@Resource和@AutoWired都可以实现依赖注入,其二者区别主要有两点:1.@AutoWired是Spring框架提供的,而@Resource是JavaEE提供的,二者的出处不同;2.@AutoWired是默认按照类型注入的,但@Resource是默认按照bean的名称进行注入的。

总的而言,假如说一个类在IOC容器中存在多个bean实例,那么无法直接使用,因为不知道该选择哪个bean进行注入,需要添加注解指定需要注入哪个bean。

标签:依赖,DI,parts,bean,详解,import,注解,UserService,注入
From: https://blog.csdn.net/Aishangyuwen/article/details/144123490

相关文章

  • BFS和Dijkstra结合
    Description数据结构与算法实验题SinsofaSolarEmpireP6★实验任务正如你所知道的s_sin是一个贪玩的不得了的小P孩QAQ,你也知道他最近很喜欢玩一个叫做太阳帝国的原罪的策略游戏去年他已经和疯狂的AI交战了整整一年。而现在,战斗的序幕又要拉开了。在某个星球上,该星球由......
  • Java中的“多态“详解
    多态(Polymorphism)是面向对象编程(OOP)中的一个核心概念,它允许同一个接口或方法在不同对象上具有不同的实现方式。多态性使得程序在运行时可以根据对象的实际类型来决定调用哪个方法,从而提高代码的灵活性和可扩展性。在Java中,多态主要通过以下几种方式实现:1.方法重载(编译时多态......
  • 端口状态详解
    TCP端口监听的状态TCP协议是一种面向连接的协议,它建立连接之前需要通过三次握手(3-wayhandshake),连接建立后进行数据传输,最后通过四次挥手(4-wayhandshake)关闭连接。TCP协议的状态机相对复杂,包含多个不同的状态,每个状态表示连接的不同生命周期。常见的TCP端口监听状态包括以下......
  • 在ModelArts Studio基于Llama3-8B模型实现新闻自动分类
    应用场景在数字化时代,新闻的生成与传播速度不断刷新记录。在ModelArtsStudio大模型即服务平台(下面简称为MaaS),使用Llama3-8B模型框架可以实现新闻自动分类,能够高效处理和分类大量新闻内容。该解决方案可以应用于如下场景:新闻门户网站:自动将新闻内容归类到相应板块,如科技、......
  • Paper Reading: Relating instance hardness to classifcation performance in a d
    目录研究动机文章贡献实例空间分析ISA框架实例空间构造足迹分析单个数据集的ISA硬度度量指标算法和性能评估特征选择实例空间表示和足迹实验结果案例研究:对于COVIDprognosis数据集的ISA分析案例研究:使用ISA检测COMPAS数据集算法偏差案例分析:使用ISA分析标签噪声数据......
  • KGD和Die之间的区别和联系
    基本概念解释Die(晶粒):晶粒是从晶圆(wafer)上切割下来的单个芯片单位。它是一个未封装的微小电路块,通常是矩形或正方形的形状。每个晶粒由半导体材料(如硅)制成,包含了特定的电路结构,能够执行一定的电性功能。通常,晶粒在晶圆上是通过光刻和其他工艺制作出来的,它本身还不能直接使用,需要进......
  • Linux安装Redis并配置开机自启
    我这边是CentOS7系统的,然后我准备安装Redis6.2首先是去官网下载Redis安装包,可以看到这里面有所有的安装包,不会下载的可以直接找我要(Redis官方下载地址)下载下来后我们把它传到服务器,我这边是放在了/usr/local目录下,然后我们给它解压tar-zxvfredis-6.2.13.tar.gz......
  • yolov8目标检测_结果文件(run/detrct/train)详解
    笔者这里是自己学习yolov8结果文件时的笔记心得分享给各位读者,可能也有很多不足之处,希望大家批评指正,共同成长!在YOLOv8的训练过程中,训练结果会存储在runs/detect/train目录下,其中包含多个文件和子文件夹。这些文件记录了训练的过程和结果,便于后续的评估和分析。结果文件......
  • Spring Events 最新详解(spring4.2前后变化)
    事件驱动设计模式,也可能通过Spring来实现。围绕事件的三个角色:事件(Event)事件发布者(Publisher)事件监听者(Listener)文章内容:SpringEvent.jpg1.Demo-01:Spring4.2版本前在Spring4.2之前,Event需要继承ApplicationEvent类。Publisher需要注入类:ApplicationEventPublishe......
  • 手把手教你从头编写 PDF – 第 4 部分:DIY空白页
    上一篇:手把手教你从头编写PDF–第3部分:创建您自己的非工作PDF准备好了吗?您即将亲手制作出属于自己的、完全空白的、单页PDF文档!在开始这个值得纪念的时刻之前,有必要先了解一些关于PDF文档正文的重要信息。正文部分包含所有描述您在PDF查看器中看到内容的对象。为......