【SpringBoot】Spring IOC && DI && 五大注解 && Bean && 扫描路径 && 依赖注入

文章目录

  • [Ⅰ. 什么是 `IOC` 和 `DI`❓❓❓](#Ⅰ. 什么是 IOCDI❓❓❓)
  • [Ⅱ. 五大注解](#Ⅱ. 五大注解)
  • [Ⅲ. 注解 `@Bean`](#Ⅲ. 注解 @Bean)
  • [Ⅳ. 扫描路径 `@ComponentScan`](#Ⅳ. 扫描路径 @ComponentScan)
  • [Ⅴ. 依赖注入](#Ⅴ. 依赖注入)
    • [一、三种注入方式 `@Autowired`](#一、三种注入方式 @Autowired)
      • [① 属性注入](#① 属性注入)
      • [② 构造方法注入](#② 构造方法注入)
      • [③ `Setter`方法注入](#③ Setter方法注入)
      • 三种注入方式的区别
    • [二、`@Autowired` 存在的问题](#二、@Autowired 存在的问题)
      • [① `@Primary`](#① @Primary)
      • [② `@Qualifier`](#② @Qualifier)
      • [③ `@Resource`⭐⭐⭐](#③ @Resource⭐⭐⭐)

Ⅰ. 什么是 IOCDI❓❓❓

IOC 全称是 Inversion of Control,中文叫 "控制反转"。

简单的说,就是把对象的创建和依赖关系的维护交给框架管理,而不是自己在代码中 new 出来

IOC 是一种设计思想而注解 DI 是实现 IOC 的方式之一

IOC 的好处如下所示:

优点 说明
解耦 组件之间不直接 new,依赖由框架注入
易测试 方便用 mock 对象进行单元测试
灵活替换 想换实现类,只改配置或注解,不改业务代码
更清晰的架构 各层职责分明,依赖关系可视化

此外,Spring 容器管理的对象,称为 Bean 对象。

@Component@AutowiredSpring 框架中非常核心的两个注解,是实现 IOC(控制反转)DI(依赖注入) 的关键工具。

  • @Component:一个类级注解,用于将普通的 Java 类声明为 Spring 管理的 Bean
  • @Autowired:一个非类级注解,用于自动注入依赖对象,而无需手动 new

如何从 Spring 容器中获取对象❓❓❓

可以从 ApplicationContext 中的 getBean() 方法获取,如下所示:

java 复制代码
public static void main(String[] args) {
    // 在启动类中拿到ApplicationContext对象
    ApplicationContext context = SpringApplication.run(Application.class, args);
    
    // 第一种传参方式
    UserComponent bean1 = context.getBean(UserComponent.class);
    bean1.func();
    
    // 第二种传参方式(需要强转)
    UserConfig bean2 = (UserConfig)context.getBean("userConfig");
    bean2.func();
}

getBean() 方法,实际上是 ApplicationContext实现了 BeanFactory 接口 得到的,并且在其基础上添加了对国际化支持、资源访问支持、以及事件传播等方面的支持! 此外,getBean() 对于 Bean 的名称约定如下所示:

  • 以小写字母开头,然后使用小驼峰的格式 。如类名为:UserController,则 Bean 名为:userController
  • 特殊情况:当第一个和第二个字母为大写时,则保留原始的大小写 。如类名为:UController,则 Bean 名为 UController

Ⅱ. 五大注解

注解 作用 常见使用位置
@Component 通用组件,标识该类为 Bean 工具类、业务组件
@Controller 控制层,接收前端请求 MVC 控制器类
@Service 业务逻辑层 Service
@Repository 数据持久层 DAO 类,MyBatis Mapper
@Configuration 配置类 用于代替 XML 配置

常见的目录结构规范如下所示:

javascript 复制代码
src
└── main
    └── java
        └── com.example.project
            ├── controller    # 控制器层(@Controller / @RestController)
            ├── service       # 业务逻辑层(@Service)
            ├── dao/mapper    # 数据访问层(@Repository / @Mapper)
            ├── model/entity  # 实体类(POJO/DTO/DO/VO)
            └── config        # 配置类(@Configuration)

注意:五大注解均可以起别名!

Ⅲ. 注解 @Bean

@Bean 告诉 spring这个方法返回的对象要放进 IoC 容器(ApplicationContext)里,成为可被任何其他 Bean 注入的组件

💥注意事项:

  1. @Bean 要配合五大注解使用,不能单独使用

  2. Spring 中,默认情况下 Bean 的作用域是 singleton (单例)的,即整个 Spring 容器中只存在一个该 Bean 实例

  3. 对于同一个类,要定义多个 Bean 对象的话,需要对不同的方法进行注解,然后使用 ApplicationContext 对象的 getBean() 方法中传入 Bean 名称的方式进行获取,对应的 Bean 名称是对应方法的名称 ,当然也可以进行重命名,默认重命名的属性是 name,如下所示

    java 复制代码
       @Component
       public class UserComponent {
           @Bean("{u3}")
           public User u1() {
               return new User();
           }
       
           @Bean
           public User u2() {
               return new User();
           }
       }
       
       public static void main(String[] args) {
           ApplicationContext context = SpringApplication.run(Application.class, args);
           
           // 通过 Bean 名获取,不然 Spring 会报错,识别不出要哪个对象
           // 并且可以对 Bean 对象进行重命名!
           User u1 = (User)context.getBean("u3");
           User u2 = (User)context.getBean("u2");
           System.out.println(u1);
           System.out.println(u2);
       }
       
       // 运行结果:
       com.liren.ioc.model.User@abbe000
       com.liren.ioc.model.User@3f81621c

Ⅳ. 扫描路径 @ComponentScan

Spring 默认扫描的范围是 SpringBoot 启动类所在包及其子包,如下图所示,一般也推荐直接把启动类放到项目目录中!

但是如果需要放在特定包内,还需要访问其它非子包的包内,则需要使用 @ComponentScan 来添加要扫描的包,如下所示,当然也可以 {} 配置多个包路径

java 复制代码
@ComponentScan("com.liren.ioc.service") // 指定扫描com.liren.ioc.service包中的内容
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Ⅴ. 依赖注入

一、三种注入方式 @Autowired

① 属性注入

这种方式虽然不是官方最推荐的,但却是日常开发最常用的。

java 复制代码
@Controller
public class UserController {
    @Autowired
    private UserService userService;

    public void func() {
        System.out.println("UserController");
        userService.func();
    }
}

② 构造方法注入

💥注意: 如果类只有一个构造方法,那么 @Autowired 注解可以省略;如果类中有多个构造方法,那么需要添加上 @Autowired 来明确指定到底使用哪个构造方法。

java 复制代码
@Controller
public class UserController {
    private UserService userService;
    
    // 默认构造方法
    public UserController() {}
    
    // 如果有默认构造方法,那么不显式写上@Autowired的话,会去调用默认构造方法
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void func() {
        System.out.println("UserController");
        userService.func();
    }
}

Setter方法注入

java 复制代码
@Controller
public class UserController {
    private UserService userService;

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

    public void func() {
        System.out.println("UserController");
        userService.func();
    }
}

三种注入方式的区别

  1. 属性注入
    1. 优点: 简洁,使用方便
    2. 缺点:
      1. 只能用于 IOC 容器,如果是非 IOC 容器不可用,并且只有在使用的时候才会出现空指针异常
      2. 不能注入一个 Final 修饰的属性
  2. 构造函数注入 (Spring 4.X 推荐)
    1. 优点:
      • 可以注入 final 修饰的属性
      • 注入的对象不会被修改
      • 依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法。
      • 通用性好,构造方法是 JDK 支持的,所以更换任何框架,它都是适用的
    2. 缺点:
      • 注入多个对象时,代码会比较繁琐
  3. Setter 注入 (Spring 3.X 推荐)
    1. 优点: 方便在类实例之后,重新对该对象进行配置或者注入
    2. 缺点:
      • 不能注入一个 final 修饰的属性
      • 注入对象可能会被改变,因为 setter 方法可能会被多次调用,就有被修改的风险

二、@Autowired 存在的问题

当同一个类存在多个 Bean 时,使用 @Autowired 会存在问题,如下所示:

如何解决上述问题呢❓❓❓Spring 提供了以下几种解决方案:

  • @Primary
  • @Qualifier
  • @Resource

@Primary

当存在多个相同类型的 Bean 注入时,加上 @Primary 注解,来确定默认的实现!

这个注解加在要被指定注入的 Bean 对象上,如下所示:

java 复制代码
@Component
public class UserComponent {
    @Primary
    @Bean
    public User u3() {
        return new User();
    }

    @Bean
    public User u4() {
        return new User();
    }
}

@Qualifier

注意该注解不能单独使用,需要配合 @Autowired 才行

java 复制代码
@Controller
public class UserController2 {
    @Qualifier("u3") // 指定对应Bean的名称
    @Autowired
    private User user;

    public void func() {
        System.out.println("UserController2");
    }
}

@Resource⭐⭐⭐

该注解可以单独使用,不过需要显式用属性 name 来指定对应的 Bean 对象!

java 复制代码
@Controller
public class UserController2 {
    @Resource(name = "u3") // 需要显式写一下name来指定
    private User user;

    public void func() {
        System.out.println("UserController2");
    }
}

需要注意的是,@Resource JDK 自带的,支持更多的参数设置 ,而 @Autowired Spring 框架提供的,没有前者功能那么多!

@Autowired 的装配顺序如下图所示:

相关推荐
William_cl2 小时前
【CSDN 精品专栏】ASP.NET Razor 变量输出 @变量名:从入门到避坑,新手也能写对!
java·数据库·asp.net
尤物程序猿2 小时前
spring的监听器的几种使用方式
java·数据库·spring
老华带你飞2 小时前
学生请假管理|基于springboot 学生请假管理系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端·spring
毕设源码-钟学长2 小时前
【开题答辩全过程】以 基于java的点餐猫在线个性化点餐系统的设计与实现为例,包含答辩的问题和答案
java·开发语言
一 乐3 小时前
校务管理|基于springboot + vueOA校务管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
摇滚侠3 小时前
面试实战 问题三十四 对称加密 和 非对称加密 spring 拦截器 spring 过滤器
java·spring·面试
xqqxqxxq3 小时前
Java 集合框架之线性表(List)实现技术笔记
java·笔记·python
L0CK3 小时前
RESTful风格解析
java