【Spring】IoC容器

分手后的思念是犯贱 2024-03-24 06:21 92阅读 0赞
  • 控制反转(IoC)

控制反转是一种思想,控制反转是为了降低程序耦合度,提高程序扩展力。

控制反转,反转的是什么?

将对象的创建权利交出去,交给第三方容器负责。

将对象和对象之间关系的维护权交出去,交给第三方容器负责。

控制反转这种思想如何实现呢?

DI(Dependency Injection):依赖注入实现了控制反转的思想。指Spring创建对象的过程中,将对象依赖属性通过配置进行注入


依赖注入常见的实现方式包括两种:

第一种:set注入

第二种:构造注入

所以结论是:IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现。

Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。


  • IoC容器在Spring的实现

Spring 的 IoC 容器就是 IoC思想的一个落地的产品实现。IoC容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建IoC 容器。Spring 提供了IoC 容器的两种实现方式:

1、BeanFactory

这是 IoC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。

2、ApplicationContext

BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。

3、ApplicationContext的主要实现类


























类型名 简介
ClassPathXmlApplicationContext 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
FileSystemXmlApplicationContext 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
ConfigurableApplicationContext ApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。
WebApplicationContext 专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。

一、基于注解管理Bean(☆)

从 Java 5 开始,Java 增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。

Spring 从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML 配置。

Spring 通过注解实现自动装配的步骤如下:

  1. 引入依赖
  2. 开启组件扫描
  3. 使用注解定义 Bean
  4. 依赖注入

1、搭建子模块 spring6-ioc-annotation

1.1、搭建模块

搭建方式如:spring6-ioc-xml

1.2、引入配置文件

  1. <dependencies>
  2. <!--spring context依赖-->
  3. <!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
  4. <dependency>
  5. <groupId>org.springframework</groupId>
  6. <artifactId>spring-context</artifactId>
  7. <version>6.0.3</version>
  8. </dependency>
  9. <!--junit5测试-->
  10. <dependency>
  11. <groupId>org.junit.jupiter</groupId>
  12. <artifactId>junit-jupiter-api</artifactId>
  13. </dependency>
  14. <!--log4j2的依赖-->
  15. <dependency>
  16. <groupId>org.apache.logging.log4j</groupId>
  17. <artifactId>log4j-core</artifactId>
  18. <version>2.19.0</version>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.apache.logging.log4j</groupId>
  22. <artifactId>log4j-slf4j2-impl</artifactId>
  23. <version>2.19.0</version>
  24. </dependency>
  25. </dependencies>

1.3、开启组件扫描

Spring 默认不使用注解装配 Bean,因此我们需要在 Spring 的 XML 配置中,通过 context:component-scan 元素开启 Spring Beans的自动扫描功能。开启此功能后,Spring 会自动从扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  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-3.0.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd">
  9. <!--
  10. Spring 默认不使用注解装配 Bean,
  11. 因此我们需要在 Spring 的 XML 配置中,通过 <context:component-scan> 元素开启 Spring Beans的自动扫描功能。
  12. 开启此功能后,Spring 会自动从扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。
  13. -->
  14. <!--开启组件扫描功能-->
  15. <context:component-scan base-package="com.cj.spring6"></context:component-scan>
  16. </beans>

ps:在使用 context:component-scan 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束。

情况一:最基本的扫描方式

  1. <context:component-scan base-package="com.cj.spring6"></context:component-scan>

情况二:指定要排除的组件

  1. <context:component-scan base-package="com.cj.spring6">
  2. <!-- context:exclude-filter标签:指定排除规则 -->
  3. <!--
  4. type:设置排除或包含的依据
  5. type="annotation",根据注解排除,expression中设置要排除的注解的全类名
  6. type="assignable",根据类型排除,expression中设置要排除的类型的全类名
  7. -->
  8. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  9. <!--<context:exclude-filter type="assignable" expression="com.cj.spring6.controller.UserController"/>-->
  10. </context:component-scan>

情况三:仅扫描指定组

  1. <context:component-scan base-package="com.cj" use-default-filters="false">
  2. <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
  3. <!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
  4. <!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
  5. <!--
  6. type:设置排除或包含的依据
  7. type="annotation",根据注解排除,expression中设置要排除的注解的全类名
  8. type="assignable",根据类型排除,expression中设置要排除的类型的全类名
  9. -->
  10. <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  11. <!--<context:include-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
  12. </context:component-scan>

1.4、使用注解定义 Bean


























注解 说明
@Component 该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。
@Repository 该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Service 该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Controller 该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

场景一:@Autowired注入

单独使用@Autowired注解,默认根据类型装配。【默认是byType】

  1. package org.springframework.beans.factory.annotation;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Documented
  10. public @interface Autowired {
  11. boolean required() default true;
  12. }

源码中有两处需要注意:

第一处:该注解可以标注在哪里?构造方法上、方法上、形参上、属性上、注解上

第二处:该注解有一个required属性,默认值是true,表示在注入的时候要求被注入的Bean必须是存在的,如果不存在则报错。如果required属性设置为false,表示注入的Bean存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错。

场景一:属性注入

创建UserDao接口

  1. package com.atguigu.spring6.dao;
  2. public interface UserDao {
  3. public void print();
  4. }

创建UserService接口

  1. package com.atguigu.spring6.service;
  2. public interface UserService {
  3. public void out();
  4. }

创建UserServiceImpl实现类

  1. package com.atguigu.spring6.service.impl;
  2. import com.atguigu.spring6.dao.UserDao;
  3. import com.atguigu.spring6.service.UserService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Service;
  6. @Service
  7. public class UserServiceImpl implements UserService {
  8. @Autowired
  9. private UserDao userDao;
  10. @Override
  11. public void out() {
  12. userDao.print();
  13. System.out.println("Service层执行结束");
  14. }
  15. }

创建UserController类

  1. package com.atguigu.spring6.controller;
  2. import com.atguigu.spring6.service.UserService;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Controller;
  5. @Controller
  6. public class UserController {
  7. @Autowired
  8. private UserService userService;
  9. public void out() {
  10. userService.out();
  11. System.out.println("Controller层执行结束。");
  12. }
  13. }

测试:

  1. package com.atguigu.spring6.bean;
  2. import com.atguigu.spring6.controller.UserController;
  3. import org.junit.jupiter.api.Test;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.context.ApplicationContext;
  7. import org.springframework.context.support.ClassPathXmlApplicationContext;
  8. public class UserTest {
  9. private Logger logger = LoggerFactory.getLogger(UserTest.class);
  10. @Test
  11. public void testAnnotation(){
  12. ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
  13. UserController userController = context.getBean("userController", UserController.class);
  14. userController.out();
  15. logger.info("执行成功");
  16. }
  17. }

测试结果:

357140cd809d417196d2c998fd383877.png

以上构造方法和setter方法都没有提供,经过测试,仍然可以注入成功。


场景二:set注入

修改UserServiceImpl类

  1. package com.atguigu.spring6.service.impl;
  2. import com.atguigu.spring6.dao.UserDao;
  3. import com.atguigu.spring6.service.UserService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Service;
  6. @Service
  7. public class UserServiceImpl implements UserService {
  8. private UserDao userDao;
  9. @Autowired
  10. public void setUserDao(UserDao userDao) {
  11. this.userDao = userDao;
  12. }
  13. @Override
  14. public void out() {
  15. userDao.print();
  16. System.out.println("Service层执行结束");
  17. }
  18. }

#

修改UserController类

  1. package com.atguigu.spring6.controller;
  2. import com.atguigu.spring6.service.UserService;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Controller;
  5. @Controller
  6. public class UserController {
  7. private UserService userService;
  8. @Autowired
  9. public void setUserService(UserService userService) {
  10. this.userService = userService;
  11. }
  12. public void out() {
  13. userService.out();
  14. System.out.println("Controller层执行结束。");
  15. }
  16. }

场景三:构造方法注入

修改UserServiceImpl类

  1. package com.atguigu.spring6.service.impl;
  2. import com.atguigu.spring6.dao.UserDao;
  3. import com.atguigu.spring6.service.UserService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Service;
  6. @Service
  7. public class UserServiceImpl implements UserService {
  8. private UserDao userDao;
  9. @Autowired
  10. public UserServiceImpl(UserDao userDao) {
  11. this.userDao = userDao;
  12. }
  13. @Override
  14. public void out() {
  15. userDao.print();
  16. System.out.println("Service层执行结束");
  17. }
  18. }

修改UserController类

  1. package com.atguigu.spring6.controller;
  2. import com.atguigu.spring6.service.UserService;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Controller;
  5. @Controller
  6. public class UserController {
  7. private UserService userService;
  8. @Autowired
  9. public UserController(UserService userService) {
  10. this.userService = userService;
  11. }
  12. public void out() {
  13. userService.out();
  14. System.out.println("Controller层执行结束。");
  15. }
  16. }

场景四:形参上注入

修改UserServiceImpl类

  1. package com.atguigu.spring6.service.impl;
  2. import com.atguigu.spring6.dao.UserDao;
  3. import com.atguigu.spring6.service.UserService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Service;
  6. @Service
  7. public class UserServiceImpl implements UserService {
  8. private UserDao userDao;
  9. public UserServiceImpl(@Autowired UserDao userDao) {
  10. this.userDao = userDao;
  11. }
  12. @Override
  13. public void out() {
  14. userDao.print();
  15. System.out.println("Service层执行结束");
  16. }
  17. }

修改UserController类

  1. package com.atguigu.spring6.controller;
  2. import com.atguigu.spring6.service.UserService;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Controller;
  5. @Controller
  6. public class UserController {
  7. private UserService userService;
  8. public UserController(@Autowired UserService userService) {
  9. this.userService = userService;
  10. }
  11. public void out() {
  12. userService.out();
  13. System.out.println("Controller层执行结束。");
  14. }
  15. }

场景五:只有一个构造函数,无注解

修改UserServiceImpl类

  1. package com.atguigu.spring6.service.impl;
  2. import com.atguigu.spring6.dao.UserDao;
  3. import com.atguigu.spring6.service.UserService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.beans.factory.annotation.Qualifier;
  6. import org.springframework.stereotype.Service;
  7. @Service
  8. public class UserServiceImpl implements UserService {
  9. @Autowired
  10. private UserDao userDao;
  11. public UserServiceImpl(UserDao userDao) {
  12. this.userDao = userDao;
  13. }
  14. @Override
  15. public void out() {
  16. userDao.print();
  17. System.out.println("Service层执行结束");
  18. }
  19. }

ps:当有参数的构造方法只有一个时,@Autowired注解可以省略。

说明:有多个构造方法时呢?大家可以测试(再添加一个无参构造函数),测试报错


场景六:@Autowired注解和@Qualifier注解联合

添加dao层实现

  1. package com.atguigu.spring6.dao.impl;
  2. import com.atguigu.spring6.dao.UserDao;
  3. import org.springframework.stereotype.Repository;
  4. @Repository
  5. public class UserDaoRedisImpl implements UserDao {
  6. @Override
  7. public void print() {
  8. System.out.println("Redis Dao层执行结束");
  9. }
  10. }

测试:测试异常。

错误信息中说:不能装配,UserDao这个Bean的数量等于。

ps:怎么解决这个问题呢?当然要byName,根据名称进行装配了。

解决方案:

修改UserServiceImpl类

  1. package com.atguigu.spring6.service.impl;
  2. import com.atguigu.spring6.dao.UserDao;
  3. import com.atguigu.spring6.service.UserService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Service;
  6. @Service
  7. public class UserServiceImpl implements UserService {
  8. @Autowired
  9. @Qualifier("userDaoImpl") // 指定bean的名字
  10. private UserDao userDao;
  11. @Override
  12. public void out() {
  13. userDao.print();
  14. System.out.println("Service层执行结束");
  15. }
  16. }

总结

@Autowired注解可以出现在:属性上、构造方法上、构造方法的参数上、setter方法上。

当带参数的构造方法只有一个,@Autowired注解可以省略。

@Autowired注解默认根据类型注入。如果要根据名称注入的话,需要配合@Qualifier注解一起使用。

发表评论

表情:
评论列表 (有 0 条评论,92人围观)

还没有评论,来说两句吧...

相关阅读

    相关 SpringIOC容器

    1.什么是IOC IOC(Inversion of Control):其思想翻转资源获取的方向,传统的资源查找方法要求组件向容器发起请求查找资源,作为回应,容器适时

    相关 一步一步实现SpringIoc容器

    IoC(Inverse of Controll控制反转):指的是对象的创建方式进行了反转,传统的开发方式是程序员自己 new 对象,IoC就是将这一过程进行了反转,程序员不需要

    相关 SpringIOC 容器

    spring是可以解决对象创建以及对象之间依赖关系的一种框架。   通过添加模块,添加不同功能 1. Spring Core  spring的核心功能: IOC容器