SpringFramework-01_Spring IoC

1. SpringFramework介绍

1.1 Spring 和 SpringFramework概念

https://spring.io/projects

Spring 已经不再是一个单纯的应用框架,而是逐渐发展成为一个由多个不同子项目(模块)组成的成熟技术,例如 Spring Framework、Spring MVC、SpringBoot、Spring Cloud、Spring Data、Spring Security 等,其中 Spring Framework 是其他子项目的基础。

Spring 特指 Spring Framework,称为 Spring 框架。

Spring Framework(Spring框架)是一个开源的应用程序框架,由SpringSource公司开发,最初是为了解决企业级开发中各种常见问题而创建的。它提供了很多功能,例如:依赖注入(Dependency Injection)、面向切面编程(AOP)、声明式事务管理(TX)等。其主要目标是使企业级应用程序的开发变得更加简单和快速,并且Spring框架被广泛应用于Java企业开发领域。

1.2 SpringFramework主要功能模块

SpringFramework框架结构图:

在这里插入图片描述

功能模块功能介绍
Core Container核心容器,在 Spring 环境下使用任何功能都必须基于 IOC 容器。
AOP&Aspects面向切面编程
TX声明式事务管理。
Spring MVC提供了面向Web应用程序的集成功能。

1.3 SpringFramework 主要优势

  1. 丰富的生态系统:Spring 生态系统非常丰富,支持许多模块和库,如 Spring Boot、Spring Security、Spring Cloud 等等,可以帮助开发人员快速构建高可靠性的企业应用程序。
  2. 模块化的设计:框架组件之间的松散耦合和模块化设计使得 Spring Framework 具有良好的可重用性、可扩展性和可维护性。开发人员可以轻松地选择自己需要的模块,根据自己的需求进行开发。
  3. 简化 Java 开发:Spring Framework 简化了 Java 开发,提供了各种工具和 API,可以降低开发复杂度和学习成本。同时,Spring Framework 支持各种应用场景,包括 Web 应用程序、RESTful API、消息传递、批处理等等。
  4. 不断创新和发展:Spring Framework 开发团队一直在不断创新和发展,保持与最新技术的接轨,为开发人员提供更加先进和优秀的工具和框架。

Spring Framework 成为了一个稳定、可靠、且创新的框架,为企业级 Java 开发提供了一站式的解决方案。

Spring 使创建 Java 企业应用程序变得容易。它提供了在企业环境中采用 Java 语言所需的一切,支持 Groovy 和 Kotlin 作为 JVM 上的替代语言,并且可以根据应用程序的需求灵活地创建多种架构。从Spring Framework 6.0.6开始,Spring 需要 Java 17+。

2. Spring IoC容器和核心概念

2.1 Spring IoC容器

  • 2.1.1 SpringIoC容器介绍

    Spring IoC 容器,负责实例化、配置和组装 bean(组件)。容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。配置元数据以 XML、Java 注解或 Java 代码形式表现。它允许表达组成应用程序的组件以及这些组件之间丰富的相互依赖关系。

    在这里插入图片描述

  • 2.1.2 SpringIoC容器具体接口和实现类

    SpringIoc容器接口

    BeanFactory 接口提供了一种高级配置机制,能够管理任何类型的对象,它是SpringIoC容器标准化超接口

    ApplicationContextBeanFactory 的子接口。它扩展了以下功能:

    • 更容易与 Spring 的 AOP 功能集成
    • 消息资源处理(用于国际化)
    • 特定于应用程序给予此接口实现,例如Web 应用程序的 WebApplicationContext
      BeanFactory 提供了配置框架和基本功能,而 ApplicationContext 添加了更多特定于企业的功能。 ApplicationContextBeanFactory 的完整超集
      ApplicationContext容器实现类
    类型名简介
    ClassPathXmlApplicationContext通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
    FileSystemXmlApplicationContext通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
    AnnotationConfigApplicationContext通过读取Java配置类创建 IOC 容器对象
    WebApplicationContext专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。
  • 3.2.4 SpringIoC容器管理配置方式

    Spring IoC 容器使用多种形式的配置元数据。此配置元数据表示您作为应用程序开发人员如何告诉 Spring 容器实例化、配置和组装应用程序中的对象。

    Spring框架提供了多种配置方式:XML配置方式、注解方式和Java配置类方式

    1. XML配置方式:是Spring框架最早的配置方式之一,通过在XML文件中定义Bean及其依赖关系、Bean的作用域等信息,让Spring IoC容器来管理Bean之间的依赖关系。该方式从Spring框架的第一版开始提供支持。
    2. 注解方式:从Spring 2.5版本开始提供支持,可以通过在Bean类上使用注解来代替XML配置文件中的配置信息。通过在Bean类上加上相应的注解(如@Component, @Service, @Autowired等),将Bean注册到Spring IoC容器中,这样Spring IoC容器就可以管理这些Bean之间的依赖关系。
    3. Java配置类方式:从Spring 3.0版本开始提供支持,通过Java类来定义Bean、Bean之间的依赖关系和配置信息,从而代替XML配置文件的方式。Java配置类是一种使用Java编写配置信息的方式,通过@Configuration、@Bean等注解来实现Bean和依赖关系的配置。

2.2 Spring IoC / DI 概念

  • IoC容器

    Spring IoC 容器,负责实例化、配置和组装 bean(组件)核心容器。容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。

  • IoC(Inversion of Control)控制反转

    IoC 主要是针对对象的创建和调用控制而言的,当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由 IoC 容器来创建和管理,即控制权由应用程序转移到 IoC 容器中,也就是“反转”了控制权。这种方式基本上是通过依赖查找的方式来实现的,即 IoC 容器维护着构成应用程序的对象,并负责创建这些对象。

  • DI (Dependency Injection) 依赖注入

    DI 是指在组件之间传递依赖关系的过程中,将依赖关系在容器内部进行处理,这样就不必在应用程序代码中硬编码对象之间的依赖关系,实现了对象之间的解耦合。在 Spring 中,DI 是通过 XML 配置文件或注解的方式实现的。它提供了三种形式的依赖注入:构造函数注入、Setter 方法注入和接口注入。

3. Spring IoC实践和应用

3.1 Spring IoC / DI 实现步骤

组件交给Spring IoC容器管理,并且获取和使用的基本步骤

  1. 配置元数据(配置)
    配置元数据,既是编写交给 SpringIoC 容器管理组件的信息,配置方式有三种。
    基于 XML 的配置元数据的基本结构:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="..." [1] class="..." [2]>  
    <!-- collaborators and configuration for this bean go here -->
  </bean>

  <bean id="..." class="...">
    <!-- collaborators and configuration for this bean go here -->
  </bean>
  <!-- more bean definitions go here -->
</beans>

Spring IoC 容器管理一个或多个组件。这些组件是使用你提供给容器的配置元数据(例如,以 XML <bean/> 定义的形式)创建的。
<bean /> 标签 == 组件信息声明

  • id 属性是标识单个 Bean 定义的字符串。
  • class 属性定义 Bean 的类型并使用完全限定的类名。
  1. 实例化IoC容器
    提供给 ApplicationContext 构造函数的位置路径是资源字符串地址,允许容器从各种外部资源(如本地文件系统、Java CLASSPATH 等)加载配置元数据。
    选择一个合适的容器实现类,进行IoC容器的实例化工作:

    //实例化ioc容器,读取外部配置文件,最终会在容器内进行ioc和di动作
    ApplicationContext context = 
               new ClassPathXmlApplicationContext("services.xml", "daos.xml");
    
  2. 获取Bean(组件)
    ApplicationContext 是一个高级工厂的接口,能够维护不同 bean 及其依赖项的注册表。通过使用方法
    T getBean(String name, Class<T> requiredType) ,可以检索 bean 的实例。

    允许读取 Bean 定义并访问它们,如:

    //创建ioc容器对象,指定配置文件,ioc也开始实例组件对象
    ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
    //获取ioc容器的组件对象
    PetStoreService service = context.getBean("petStore", PetStoreService.class);
    //使用组件对象
    List<String> userList = service.getUsernameList();
    

3.2 基于 XML 配置方式组件管理

3.2.1 组件(Bean)信息声明配置(IoC)
3.2.1.1 基于无参数构造函数

当通过构造函数方法创建一个 bean(组件对象) 时,所有普通类都可以由 Spring 使用并与之兼容。也就是说,正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。只需指定 Bean 类信息。但是,默认情况下,需要一个默认(空)构造函数。

  • 准备组件类
public class HappyComponent {
    //默认包含无参数构造函数
    public void doWork() {
        System.out.println("HappyComponent.doWork");
    }
}
  • xml配置文件编写
    • 创建携带spring约束的xml配置文件
    • 编写配置文件:resources/spring-bean-01.xml
      <!-- 创建bean -->
      <bean id="happyComponent" class="com.start.ioc.HappyComponent"/>
      
      • bean标签:通过配置bean标签告诉IOC容器需要创建对象的组件信息
      • id属性:bean的唯一标识,方便获取Bean
      • class属性:组件类的全限定符
      • 注意:要求当前组件类必须包含无参数构造函数
3.2.1.2 基于静态工厂方法实例化
  • 准备组件类
public class ClientService {
  private static ClientService clientService = new ClientService();
  private ClientService() {}

  public static ClientService createInstance() {
    return clientService;
  }
}
  • xml配置文件编写
    • 创建携带spring约束的xml配置文件
    • 编写配置文件:resources/spring-bean-02.xml
      <bean id="clientService"  class="com.start.ioc.ClientService"  factory-method="createInstance"/>
      
      • class属性:指定工厂类的全限定符
      • factory-method: 指定静态工厂方法,注意,该方法必须是static方法
3.2.1.3 基于实例工厂方法实例化
  • 准备组件类
public class DefaultServiceLocator {

  private static ClientServiceImplclientService = new ClientServiceImpl();

  public ClientService createClientServiceInstance() {
    return clientService;
  }
}
  • xml配置文件编写
    • 创建携带spring约束的xml配置文件
    • 编写配置文件:resources/spring-bean-03.xml
      <!-- 将工厂类进行ioc配置 -->
      <bean id="serviceLocator" class="com.start.ioc.DefaultServiceLocator"></bean>
      <!-- 根据工厂对象的实例工厂方法进行实例化组件对象 -->
      <bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
      
      • factory-bean属性:指定当前容器中工厂Bean 的名称
      • factory-method: 指定实例工厂方法名。注意,实例方法必须是非static的
3.2.2 组件(Bean)依赖注入配置(DI)
3.2.2.1 基于构造函数的依赖注入(单个构造参数)
  • 介绍
    • 基于构造函数的 DI 是通过容器调用具有多个参数的构造函数来完成的,每个参数表示一个依赖项
  • 准备组件类
    public class UserDao {
    }
    
    public class UserService {
        private UserDao userDao;
    
        public UserService(UserDao userDao) {
            this.userDao = userDao;
        }
    }
    
    
  • 编写配置文件:resources/spring-01.xml
    <beans>
    <!-- 引用类bean声明 -->
    <bean id="userService" class="x.y.UserService">
    <!-- 构造函数引用 -->
    <constructor-arg ref="userDao"/>
    </bean>
    <!-- 被引用类bean声明 -->
    <bean id="userDao" class="x.y.UserDao"/>
    </beans>
    
    • constructor-arg标签:可以引用构造参数 ref引用其他bean的标识
3.2.2.2 基于构造函数的依赖注入(多个构造参数)
  • 准备组件类
    public class UserDao {
    }
    
    public class UserService {
        
        private UserDao userDao;
        
        private int age;
        
        private String name;
    
        public UserService(int age , String name ,UserDao userDao) {
            this.userDao = userDao;
            this.age = age;
            this.name = name;
        }
    }
    
  • 编写配置文件:resources/spring-02.xml
    <!-- 场景1: 多参数,可以按照相应构造函数的顺序注入数据 -->
    <beans>
      <bean id="userService" class="x.y.UserService">
        <!-- value直接注入基本类型值 -->
        <constructor-arg  value="18"/>
        <constructor-arg  value="张三"/>
        
        <constructor-arg  ref="userDao"/>
      </bean>
      <!-- 被引用类bean声明 -->
      <bean id="userDao" class="x.y.UserDao"/>
    </beans>
    
    
    <!-- 场景2: 多参数,可以按照相应构造函数的名称注入数据 -->
    <beans>
      <bean id="userService" class="x.y.UserService">
        <!-- value直接注入基本类型值 -->
        <constructor-arg name="name" value="张三"/>
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="age"  value="18"/>
      </bean>
      <!-- 被引用类bean声明 -->
      <bean id="userDao" class="x.y.UserDao"/>
    </beans>
    
    <!-- 场景3: 多参数,可以按照相应构造函数的角标注入数据 
               index从0开始 构造函数(0,1,2....)
    -->
    <beans>
        <bean id="userService" class="x.y.UserService">
        <!-- value直接注入基本类型值 -->
        <constructor-arg index="1" value="张三"/>
        <constructor-arg index="2" ref="userDao"/>
        <constructor-arg index="0"  value="18"/>
      </bean>
      <!-- 被引用类bean声明 -->
      <bean id="userDao" class="x.y.UserDao"/>
    </beans>
    
    • constructor-arg标签:指定构造参数和对应的值
    • constructor-arg标签:name属性指定参数名、index属性指定参数角标、value属性指定普通属性值
3.2.2.3 基于Setter方法依赖注入
  • 准备组件类
    public Class MovieFinder{
    
    }
    
    public class SimpleMovieLister {
    
      private MovieFinder movieFinder;
      
      private String movieName;
    
      public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
      }
      
      public void setMovieName(String movieName){
        this.movieName = movieName;
      }
    }
    
  • 编写配置文件:resources/spring-03.xml
    <bean id="simpleMovieLister" class="com.start.setter.SimpleMovieLister">
      <!-- setter方法,注入movieFinder对象的标识id
           name = 属性名  ref = 引用bean的id值
       -->
      <property name="movieFinder" ref="movieFinder" />
    
      <!-- setter方法,注入基本数据类型movieName
           name = 属性名 value= 基本类型值
       -->
      <property name="movieName" value="消失的她"/>
    </bean>
    
    <bean id="movieFinder" class="examples.MovieFinder"/>
    
    
    • property标签: 可以给setter方法对应的属性赋值
    • property 标签: name属性代表set方法标识、ref代表引用bean的标识id、value属性代表基本属性值
3.2.3 IoC容器创建和使用
  1. 容器实例化

    //方式1:实例化并且指定配置文件
    //参数:String...locations 传入一个或者多个配置文件
    ApplicationContext context = 
               new ClassPathXmlApplicationContext("services.xml", "daos.xml");
               
    //方式2:先实例化,再指定配置文件,最后刷新容器触发Bean实例化动作 [springmvc源码和contextLoadListener源码方式]  
    ApplicationContext iocContainer1= 
               new ClassPathXmlApplicationContext();   
    //设置配置配置文件,方法参数为可变参数,可以设置一个或者多个配置
    iocContainer1.setConfigLocations("services.xml", "daos.xml");
    //后配置的文件,需要调用refresh方法,触发刷新配置
    iocContainer1.refresh();           
    
  2. Bean对象读取

    //方式1: 根据id获取
    //没有指定类型,返回为Object,需要类型转化
    HappyComponent happyComponent = 
            (HappyComponent) iocContainer.getBean("bean的id标识");
            
    //使用组件对象        
    happyComponent.doWork();
    
    //方式2: 根据类型获取
    //根据类型获取,但是要求,同类型(当前类,或者之类,或者接口的实现类)只能有一个对象交给IoC容器管理
    //配置两个或者以上出现: org.springframework.beans.factory.NoUniqueBeanDefinitionException 问题
    HappyComponent happyComponent = iocContainer.getBean(HappyComponent.class);
    happyComponent.doWork();
    
    //方式3: 根据id和类型获取
    HappyComponent happyComponent = iocContainer.getBean("bean的id标识", HappyComponent.class);
    happyComponent.doWork();
    
    //根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,
    //只要返回的是true就可以认定为和类型匹配,能够获取到。
    
3.2.4 组件(Bean)作用域和周期方法配置
  1. 组件周期方法配置

    1. 周期方法概念
      • 可以在组件类中定义方法,然后当IoC容器实例化和销毁组件对象的时候进行调用,称为生命周期方法
      • 类似于Servlet的init/destroy方法,可以在周期方法完成初始化和释放资源等工作
    2. 周期方法声明
      public class BeanOne {
      
        //周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
        public void init() {
          // 初始化逻辑
        }
      }
      
      public class BeanTwo {
      
        public void cleanup() {
          // 释放资源逻辑
        }
      }
      
    3. 周期方法配置
      <beans>
        <bean id="beanOne" class="examples.BeanOne" init-method="init" />
        <bean id="beanTwo" class="examples.BeanTwo" destroy-method="cleanup" />
      </beans>
      
  2. 组件作用域配置

    1. Bean作用域概念

      • <bean> 标签声明Bean,只是将Bean的信息配置给SpringIoC容器
      • 在IoC容器中,这些<bean>标签对应的信息转成Spring内部 BeanDefinition 对象,BeanDefinition 对象内,包含定义的信息(id,class,属性等等)
      • BeanDefinition概念一样,SpringIoC容器可以可以根据BeanDefinition对象反射创建多个Bean对象实例,具体创建多少个Bean的实例对象,由Bean的作用域Scope属性指定
    2. 作用域可选值

      取值含义创建对象的时机默认值
      singleton在 IOC 容器中,这个 bean 的对象始终为单实例IOC 容器初始化时
      prototype这个 bean 在 IOC 容器中有多个实例获取 bean 时
    3. 作用域配置

      <!--bean的作用域 -->
      <!-- scope属性:取值singleton(默认值),bean在IOC容器中只有一个实例,IOC容器初始化时创建对象 -->
      <!-- scope属性:取值prototype,bean在IOC容器中可以有多个实例,getBean()时创建对象 -->
      <bean id="happyMachine" scope="prototype" class="com.start.ioc.HappyMachine">
          <property name="machineName" value="happyMachine"/>
      </bean>
      
3.2.5 FactoryBean特性和使用
  1. FactoryBean概念

    • FactoryBean 接口是Spring IoC容器实例化逻辑的可插拔性点
    • 用于配置复杂的Bean对象,可以将创建过程存储在FactoryBean 的getObject方法
    • FactoryBean<T> 接口提供三种方法:
      • T getObject():返回此工厂创建的对象的实例。该返回值会被存储到IoC容器。
      • boolean isSingleton():如果此 FactoryBean 返回单例,则返回 true ,否则返回 false 。默认实现返回 true
      • Class<?> getObjectType()::返回 getObject() 方法返回的对象类型,如果事先不知道类型,则返回 null
  2. FactoryBean使用场景

    1. 代理类的创建
    2. 第三方框架整合
    3. 复杂对象实例化
  3. Factorybean应用

    1. 准备FactoryBean实现类

      // 实现FactoryBean接口时需要指定泛型
      // 泛型类型就是当前工厂要生产的对象的类型
      public class HappyFactoryBean implements FactoryBean<HappyMachine> {
          
          private String machineName;
          
          public String getMachineName() {
              return machineName;
          }
          
          public void setMachineName(String machineName) {
              this.machineName = machineName;
          }
          
          @Override
          public HappyMachine getObject() throws Exception {
          
              // 方法内部模拟创建、设置一个对象的复杂过程
              HappyMachine happyMachine = new HappyMachine();
          
              happyMachine.setMachineName(this.machineName);
          
              return happyMachine;
          }
          
          @Override
          public Class<?> getObjectType() {
              // 返回要生产的对象的类型
              return HappyMachine.class;
          }
      }
      
    2. 配置FactoryBean实现类

      <!-- FactoryBean机制 -->
      <!-- 这个bean标签中class属性指定的是HappyFactoryBean,但是将来从这里获取的bean是HappyMachine对象 -->
      <bean id="happyMachine" class="com.start.ioc.HappyFactoryBean">
          <!-- property标签仍然可以用来通过setXxx()方法给属性赋值 -->
          <property name="machineName" value="iceCreamMachine"/>
      </bean>
      
    3. 测试读取FactoryBean和FactoryBean.getObject对象

      @Test
      public void testExperiment()  {
      
          ApplicationContext iocContainer = new ClassPathXmlApplicationContext("spring-bean-07.xml");
      
          //注意: 直接根据声明FactoryBean的id,获取的是getObject方法返回的对象
          HappyMachine happyMachine = iocContainer.getBean("happyMachine7",HappyMachine.class);
          System.out.println("happyMachine = " + happyMachine);
      
          //如果想要获取FactoryBean对象, 直接在id前添加&符号即可!  &happyMachine7 这是一种固定的约束
          Object bean = iocContainer.getBean("&happyMachine7");
          System.out.println("bean = " + bean);
      }
      
  4. FactoryBean和BeanFactory区别

    • FactoryBean 是 Spring 中一种特殊的 bean,可以在 getObject() 工厂方法自定义的逻辑创建Bean。是一种能够生产其他 Bean 的 Bean。FactoryBean 在容器启动时被创建,而在实际使用时则是通过调用 getObject() 方法来得到其所生产的 Bean。FactoryBean 可以自定义任何所需的初始化逻辑,生产出一些定制化的 bean。一般情况下,整合第三方框架,都是通过定义FactoryBean实现
    • BeanFactory 是 Spring 框架的基础,其作为一个顶级接口定义了容器的基本行为,例如管理 bean 的生命周期、配置文件的加载和解析、bean 的装配和依赖注入等。BeanFactory 接口提供了访问 bean 的方式,例如 getBean() 方法获取指定的 bean 实例。它可以从不同的来源(例如 Mysql 数据库、XML 文件、Java 配置类等)获取 bean 定义,并将其转换为 bean 实例。同时,BeanFactory 还包含很多子类(例如,ApplicationContext 接口)提供了额外的强大功能。
    • FactoryBean 和 BeanFactory 的区别主要在于前者是用于创建 bean 的接口,它提供了更加灵活的初始化定制功能,而后者是用于管理 bean 的框架基础接口,提供了基本的容器功能和 bean 生命周期管理。

3.3 基于 注解 方式管理 Bean

3.3.1 Bean注解标记和扫描 (IoC)
  1. 注解

    1. 组件标记注解和区别
      Spring 提供了以下多个注解,可以直接标注在 Java 类上,将它们定义成 Spring Bean

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

      @Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。

    2. 使用注解标记
      普通组件

      @Component
      public class CommonComponent {
      }	      
      

      Controller组件

      @Controller
      public class XxxController {
      }	      
      

      Service组件

      @Service
      public class XxxService {
      }	      
      

      Dao组件

      @Repository
      public class XxxDao {
      }
      
  2. 配置文件确定扫描范围
    情况1:基本扫描配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        <!-- 配置自动扫描的包 -->
        <!-- 1.包要精准,提高性能!
             2.会扫描指定的包和子包内容
             3.多个包可以使用,分割 例如: com.start.controller,com.start.service等
        -->
        <context:component-scan base-package="com.start.components"/>
      
    </beans>
    

    情况2:指定排除组件

    <!-- 情况三:指定不扫描的组件 -->
    <context:component-scan base-package="com.start.components">
        
        <!-- context:exclude-filter标签:指定排除规则 -->
        <!-- type属性:指定根据什么来进行排除,annotation取值表示根据注解来排除 -->
        <!-- expression属性:指定排除规则的表达式,对于注解来说指定全类名即可 -->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    

    情况3:指定扫描组件

    <!-- 情况四:仅扫描指定的组件 -->
    <!-- 仅扫描 = 关闭默认规则 + 追加规则 -->
    <!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
    <context:component-scan base-package="com.start.ioc.components" use-default-filters="false">
        
        <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
  3. 组件BeanName问题
    使用 XML 方式管理 bean 的时候,每个 bean 都有一个唯一标识——id 属性的值,便于在其他地方引用。
    现在使用注解后,每个组件仍然应该有一个唯一标识。

    默认情况:

    类名首字母小写就是 bean 的 id。例如:SoldierController 类对应的 bean 的 id 就是 soldierController。

    使用value属性指定:

    @Controller(value = "tianDog")
    public class SoldierController {
    }
    

    当注解中只设置一个属性时,value属性的属性名可以省略:

    @Service("smallDog")
    public class SoldierService {
    }
    
3.3.2 组件(Bean)作用域和周期方法注解

作用域配置

@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON) //单例,默认值
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) //多例  二选一
public class BeanOne {

  //周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
  @PostConstruct  //注解制指定初始化方法
  public void init() {
    // 初始化逻辑
  }
  @PreDestroy //注解指定销毁方法
  public void destroy() {
      System.out.println("JavaBean.destroy");
  }
}
3.3.3 Bean属性赋值:引用类型自动装配 (DI)
  1. 自动装配实现

    1. 前提

      参与自动装配的组件(需要装配、被装配)全部都必须在IoC容器中。

      注意:不区分IoC的方式,XML和注解都可以。

    2. @Autowired注解

      在成员变量上直接标记@Autowired注解即可,不需要提供setXxx()方法。

    3. 给Controller装配Service

      @Controller(value = "tianDog")
      public class SoldierController {
          
          @Autowired
          private SoldierService soldierService;
          
          public void getMessage() {
              soldierService.getMessage();
          }
          
      }
      
    4. 给Service装配Dao

      @Service("smallDog")
      public class SoldierService {
          
          @Autowired
          private SoldierDao soldierDao;
          
          public void getMessage() {
              soldierDao.getMessage();
          }
      }
      
  2. @Autowired注解细节

    1. 标记位置

      1. 成员变量

        与xml进行bean ref引用不同,不需要有set方法

        @Service("smallDog")
        public class SoldierService {
            
            @Autowired
            private SoldierDao soldierDao;
            
            public void getMessage() {
                soldierDao.getMessage();
            }
        }
        
      2. 构造器

        @Controller(value = "tianDog")
        public class SoldierController {
            
            private SoldierService soldierService;
            
            @Autowired
            public SoldierController(SoldierService soldierService) {
                this.soldierService = soldierService;
            }
            ……
        
      3. setXxx()方法

        @Controller(value = "tianDog")
        public class SoldierController {
        
            private SoldierService soldierService;
        
            @Autowired
            public void setSoldierService(SoldierService soldierService) {
                this.soldierService = soldierService;
            }
            ……
        
    2. 工作流程

      在这里插入图片描述

      • 首先根据所需要的组件类型到 IOC 容器中查找
        • 能够找到唯一的 bean:直接执行装配
        • 如果完全找不到匹配这个类型的 bean:装配失败
        • 和所需类型匹配的 bean 不止一个
          • 没有 @Qualifier 注解:根据 @Autowired 标记位置成员变量的变量名作为 bean 的 id 进行匹配
            • 能够找到:执行装配
            • 找不到:装配失败
          • 使用 @Qualifier 注解:根据 @Qualifier 注解中指定的名称作为 bean 的id进行匹配
            • 能够找到:执行装配
            • 找不到:装配失败
      @Controller(value = "tianDog")
      public class SoldierController {
          
          @Autowired
          @Qualifier(value = "maomiService222")
          // 根据面向接口编程思想,使用接口类型引入Service组件
          private ISoldierService soldierService;
      
  3. JSR-250注解@Resource

    @Resource注解也可以完成属性注入,和@Autowired注解有什么区别?

    • @Resource注解是JDK扩展包中的,属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)

    • @Autowired注解是Spring框架自己的。

    • @Resource注解默认根据Bean名称装配,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型装配。

    • @Autowired注解默认根据类型装配,如果想根据名称装配,需要配合@Qualifier注解一起用。

    • @Resource注解用在属性上、setter方法上。

    • @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。
      @Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【高于JDK11或低于JDK8需要引入以下依赖

      <dependency>
          <groupId>jakarta.annotation</groupId>
          <artifactId>jakarta.annotation-api</artifactId>
          <version>2.1.1</version>
      </dependency>
      
3.3.4 Bean属性赋值:基本类型属性赋值 (DI)

@Value 通常用于注入外部化属性

声明外部配置

application.properties

catalog.name=MovieCatalog

xml引入外部配置

<!-- 引入外部配置文件-->
<context:property-placeholder location="application.properties" />

@Value注解读取配置

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class CommonComponent {

    /**
     * 情况1: ${key} 取外部配置key对应的值!
     * 情况2: ${key:defaultValue} 没有key,可以给与默认值
     */
    @Value("${catalog:hahaha}")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

3.4 基于 配置类 方式管理 Bean

3.4.1 配置类和扫描注解

配置类

使用 @Configuration 注解将一个普通的类标记为 Spring 的配置类。

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

//标注当前类是配置类,替代application.xml    
@Configuration
//使用注解读取外部配置,替代 <context:property-placeholder标签
@PropertySource("classpath:application.properties")
//使用@ComponentScan注解,可以配置扫描包,替代<context:component-scan标签
@ComponentScan(basePackages = {"com.start.components"})
public class MyConfiguration {
    
}

测试创建IoC容器

// AnnotationConfigApplicationContext 根据配置类创建 IOC 容器对象
ApplicationContext iocContainerAnnotation = 
new AnnotationConfigApplicationContext(MyConfiguration.class);

总结:

@Configuration指定一个类为配置类,可以添加配置注解,替代配置xml文件

@ComponentScan(basePackages = {“包”,“包”}) 替代<context:component-scan标签实现注解扫描

@PropertySource(“classpath:配置文件地址”) 替代 <context:property-placeholder标签

配合IoC/DI注解,可以进行完整注解开发

3.4.1 @Bean定义组件

@Bean 注解用于指示方法实例化、配置和初始化要由 Spring IoC 容器管理的新对象。 @Bean 注释与 <bean/> 元素起着相同的作用。

//标注当前类是配置类,替代application.xml    
@Configuration
//引入jdbc.properties文件
@PropertySource({"classpath:application.properties","classpath:jdbc.properties"})
@ComponentScan(basePackages = {"com.start.components"})
public class MyConfiguration {

    //如果第三方类进行IoC管理,无法直接使用@Component相关注解
    //解决方案: xml方式可以使用<bean标签
    //解决方案: 配置类方式,可以使用方法返回值+@Bean注解
    @Bean
    public DataSource createDataSource(@Value("${jdbc.user}") String username,
                                       @Value("${jdbc.password}")String password,
                                       @Value("${jdbc.url}")String url,
                                       @Value("${jdbc.driver}")String driverClassName){
        //使用Java代码实例化
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        dataSource.setDriverClassName(driverClassName);
        //返回结果即可
        return dataSource;
    }
}
3.4.1 @Bean注解细节
  1. @Bean生成BeanName问题

    @Bean注解源码:

    public @interface Bean {
        //前两个注解可以指定Bean的标识
        @AliasFor("name")
        String[] value() default {};
        @AliasFor("value")
        String[] name() default {};
      
        //autowireCandidate 属性来指示该 Bean 是否候选用于自动装配。
        //autowireCandidate 属性默认值为 true,表示该 Bean 是一个默认的装配目标,
        //可被候选用于自动装配。如果将 autowireCandidate 属性设置为 false,则说明该 Bean 不是默认的装配目标,不会被候选用于自动装配。
        boolean autowireCandidate() default true;
    
        //指定初始化方法
        String initMethod() default "";
        //指定销毁方法
        String destroyMethod() default "(inferred)";
    }
    
    

    指定@Bean的名称:

    @Configuration
    public class AppConfig {
    
      @Bean("myThing") //指定名称
      public Thing thing() {
        return new Thing();
      }
    }
    

    @Bean 注释方法。使用此方法在指定为方法返回值的类型的 ApplicationContext 中注册 Bean 定义。缺省情况下,Bean 名称与方法名称相同。下面的示例演示 @Bean 方法声明:

    @Configuration
    public class AppConfig {
    
      @Bean
      public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
      }
    }
    

    等同于下面的Spring XML:

    <beans>
      <bean id="transferService" class="com.acme.TransferServiceImpl"/>
    </beans>
    
  2. @Bean 初始化和销毁方法指定
    @Bean 注解支持指定任意初始化和销毁回调方法,类似Spring XML 在 bean 元素上的 init-methoddestroy-method 属性

    public class BeanOne {
    
      public void init() {
        // initialization logic
      }
    }
    
    public class BeanTwo {
    
      public void cleanup() {
        // destruction logic
      }
    }
    
    @Configuration
    public class AppConfig {
    
      @Bean(initMethod = "init")
      public BeanOne beanOne() {
        return new BeanOne();
      }
    
      @Bean(destroyMethod = "cleanup")
      public BeanTwo beanTwo() {
        return new BeanTwo();
      }
    }
    
  3. @Bean Scope作用域
    可以指定使用 @Bean 注释定义的 bean 应具有特定范围。
    默认作用域为 singleton ,可以使用 @Scope 注释覆盖此范围

    @Configuration
    public class MyConfiguration {
    
      @Bean
      @Scope("prototype")
      public Encryptor encryptor() {
        // ...
      }
    }
    
  4. @Bean方法之间依赖
    Java配置类实现:
    方案1:
    直接调用方法返回 Bean 实例:在一个 @Bean 方法中直接调用其他 @Bean 方法来获取 Bean 实例,也是通过IoC容器获取对应的Bean

    @Configuration
    public class JavaConfig {
    
        @Bean
        public HappyMachine happyMachine(){
            return new HappyMachine();
        }
    
        @Bean
        public HappyComponent happyComponent(){
            HappyComponent happyComponent = new HappyComponent();
            //直接调用方法即可! 
            happyComponent.setHappyMachine(happyMachine());
            return happyComponent;
        }
    
    }
    

    方案2:

    参数引用法:通过方法参数传递 Bean 实例的引用来解决 Bean 实例之间的依赖关系

    import com.start.ioc.HappyComponent;
    import com.start.ioc.HappyMachine;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    @Configuration
    public class JavaConfig {
    
        @Bean
        public HappyMachine happyMachine(){
            return new HappyMachine();
        }
    
        /**
         * 可以直接在形参列表接收IoC容器中的Bean
         *    情况1: 直接指定类型即可
         *    情况2: 如果有多个bean,(HappyMachine 名称 ) 形参名称等于要指定的bean名称!
         *           例如:
         *               @Bean
         *               public Foo foo1(){
         *                   return new Foo();
         *               }
         *               @Bean
         *               public Foo foo2(){
         *                   return new Foo()
         *               }
         *               @Bean
         *               public Component component(Foo foo1 / foo2 通过此处指定引入的bean)
         */
        @Bean
        public HappyComponent happyComponent(HappyMachine happyMachine){
            HappyComponent happyComponent = new HappyComponent();
            //赋值
            happyComponent.setHappyMachine(happyMachine);
            return happyComponent;
        }
    
    }
    
3.4.1 @Import扩展

@Import 注释允许从另一个配置类加载 @Bean 定义

@Configuration
public class ConfigA {

  @Bean
  public A a() {
    return new A();
  }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

  @Bean
  public B b() {
    return new B();
  }
}

在实例化上下文时不需要同时指定 ConfigA.classConfigB.class ,只需显式提供 ConfigB

public static void main(String[] args) {
  ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

  // now both beans A and B will be available...
  A a = ctx.getBean(A.class);
  B b = ctx.getBean(B.class);
}

3.5 三种配置方式总结

3.5.1 XML方式配置总结
  1. 所有内容写到xml格式配置文件中
  2. 声明bean通过<bean标签
  3. <bean标签包含基本信息(id,class)和属性信息 <property name value / ref
  4. 引入外部的properties文件可以通过<context:property-placeholder
  5. IoC具体容器实现选择ClassPathXmlApplicationContext对象
3.5.2 XML+注解方式配置总结
  1. 注解负责标记IoC的类和进行属性装配
  2. xml文件依然需要,需要通过<context:component-scan标签指定注解范围
  3. 标记IoC注解:@Component,@Service,@Controller,@Repository
  4. 标记DI注解:@Autowired @Qualifier @Resource @Value
  5. IoC具体容器实现选择ClassPathXmlApplicationContext对象
3.5.3 完全注解方式配置总结
  1. 完全注解方式指的是去掉xml文件,使用配置类 + 注解实现
  2. xml文件替换成使用@Configuration注解标记的类
  3. 标记IoC注解:@Component,@Service,@Controller,@Repository
  4. 标记DI注解:@Autowired @Qualifier @Resource @Value
  5. <context:component-scan标签指定注解范围使用@ComponentScan(basePackages = {“com.atguigu.components”})替代
  6. <context:property-placeholder引入外部配置文件使用@PropertySource({“classpath:application.properties”,“classpath:jdbc.properties”})替代
  7. <bean 标签使用@Bean注解和方法实现
  8. IoC具体容器实现选择AnnotationConfigApplicationContext对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值