Spring-IOC容器(个人学习笔记)

Spring入门

Spring Framework 概述

  • Spring Framework 6.0开始,Spring需要Java 17+
  • 支持GroovyKotlin等基于JVM的语言。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Spring 是什么意思

  • Spring指的是什么?
    • 大多数情况下,当人们说“Spring”时,他们指的是整个项目家族。
    • 本参考文档关注的是基础:Spring Framework本身。
  • Spring框架是多模块的,可以选择需要的模块。
    • 核心是核心容器的模块,包括配置模型和依赖注入机制。
    • 除此之外,Spring框架还为不同的应用程序架构提供了基础支持,包括消息传递、事务性数据和持久性,以及web
    • 它还包括基于servletSpring MVC web框架,以及并行的Spring WebFlux响应式web框架。

resource中创建一个Spring配置文件,命名为coconut.xml:

本章中相关术语介绍

元数据

  • 元数据是配置数据的数据
    • 传统的xml配置
    • 注解配置
    • Java配置

Context上下文

  • Context

    • ContextIoC容器的具体实现。
    • 它负责创建和管理Bean对象,并通过依赖注入的方式将这些对象注入到需要使用它们的地方。
    • Context可以通过配置文件或注解的方式来定义Bean对象的创建和依赖关系,开发者只需要在配置文件或注解中声明需要的Bean对象以及它们之间的依赖关系,Context就会负责实例化和管理这些对象。
    • 常用的Context包括:
    ApplicationContext最常用的Context类型
    ApplicationContextSpring中最常用的Context类型,它是BeanFactory的子接口,提供了更多的功能,如国际化、事件发布等。常用的ApplicationContext实现类有
    AnnotationConfigApplicationContext基于注解的ApplicationContext实现。
    ClassPathXmlApplicationContext基于XML配置文件的ApplicationContext实现,配置文件需要位于类路径下,可以是项目的源代码目录或者类路径下的任何子目录。
    FileSystemXmlApplicationContext基于XML配置文件的ApplicationContext实现,配置文件可以位于文件系统中的任何位置,需要提供配置文件的绝对路径。
    ApplicationContext的子接口ConfigurableApplicationContext
    ConfigurableApplicationContextApplicationContext的子接口,提供了更多的配置和管理方法。常用的ConfigurableApplicationContext实现类有
    AnnotationConfigApplicationContext基于注解的ConfigurableApplicationContext实现
    ClassPathXmlApplicationContext基于XML配置文件的ConfigurableApplicationContext实现,配置文件需要位于类路径下,可以是项目的源代码目录或者类路径下的任何子目录。
    FileSystemXmlApplicationContext基于XML配置文件的ConfigurableApplicationContext实现,配置文件可以位于文件系统中的任何位置,需要提供配置文件的绝对路径。
    ApplicationContext的子接口WebApplicationContext
    WebApplicationContextApplicationContext的子接口,用于在Web应用程序中使用。常用的WebApplicationContext实现类有
    AnnotationConfigWebApplicationContext基于注解的WebApplicationContext实现。
    XmlWebApplicationContext基于XML配置文件的WebApplicationContext实现。
    GenericApplicationContextApplicationContext的子类,是一个通用的ApplicationContext实现,可以用于任何环境。

Bean

  • Bean
    • 由IoC容器创建和管理的对象,被称为Bean

因此,可以说ContextIoC容器的具体表现形式,它实现了IoC的核心原理,即控制反转。通过使用Context,开发者可以将对象的创建和管理交由容器来完成,从而实现了对象之间的解耦和灵活性。

Spring的核心模块有哪些

了解即可,本章并不会将全部展开讲述

Bean容器:核心功能是管理和控制Java对象的生命周期,通过IoC容器实现对象的创建、装配和管理。Bean容器是Spring框架的核心组件,负责管理和维护应用程序中的Bean对象。

IoC(控制反转):通过IoC容器管理对象的生命周期,将对象的创建、装配和管理的控制权交给容器,使得应用程序的各个组件解耦,提高了代码的灵活性和可维护性。

DI(依赖注入):通过依赖注入,容器将对象所需的依赖关系自动注入到对象中,降低了对象之间的耦合度,提高了代码的可测试性和可重用性。

AOP(面向切面编程):通过AOP,可以将应用程序的横切关注点(如事务管理、日志记录)从核心业务逻辑中分离出来,以模块化的方式实现这些关注点。

SpEL(Spring表达式语言):提供了一种强大的表达式语言,可以在运行时动态地计算和访问对象的属性值,用于配置和处理Spring应用程序。

事件机制:通过事件机制,可以在应用程序中发布和监听事件,实现模块之间的松耦合通信。

国际化支持:提供了国际化和本地化的支持,可以根据用户的语言环境加载相应的资源文件。

核心工具类:提供了一些常用的工具类,如字符串处理、集合操作、反射等,方便开发人员进行开发和调试。

Spring核心技术(IOC技术)

org.springframework.beans.factory包提供了管理和操作bean的基本功能,包括以编程的方式。

org.springframework.context包添加了ApplicationContext接口,它扩展了BeanFactory接口,此外还扩展了其他接口,以更面向应用程序框架的风格提供额外的功能。

IoC容器概述(beans和context)

  • IoC核心思想
    • 对象的依赖关系不再由对象自己控制,而是由容器来控制
  • IoC容器
    • 是管理bean的容器
      • bean是被IoC容器管理的JavaBean
    • 会根据元数据来实例化、配置和组装bean
    • 在对象创建时就提供给对象所需要的依赖
  • Spring IoC容器
    • ApplicationContext接口
    • ClassPathXmlApplicationContext实例
    • FileSystemXmlApplicationContext实例
  • 如何使用Ioc容器
    • 导入Spring
    • 创建IoC容器
    • 配置元数据,实例化、配置和组装bean
    • 使用IoC容器取得bean

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

导入 Spring

想使用Spring的技术,首先需要导入Spring包。


<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.9</version>
</dependency>

创建 IoC容器

我们这里采用ClassPathXmlApplicationContext来创建IoC实例。

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        //
        ApplicationContext context = new ClassPathXmlApplicationContext("iocXML/InitBean.xml");

    }
}

创建元数据配置文件

通过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">

</beans>

创建一个JavaBean,这是我们想让IoC实例化并管理的类

public class Student {
    String name;
    int age;
}

在配置文件中添加这个bean,让IoC容器读取并管理。


<bean name="student" class="com.coconut.ioc.iocTest.pojo.Student"/>

从IoC容器中获取bean

这里我们直接从IoC容器中获取bean,而不直接采用new的方式。

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("iocXML/InitBean.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student);
    }
}

实际上,这里得到的Student对象是由Spring通过反射机制帮助我们创建的。

bean概述

在上节,我们通过一个简单的实例,来实例化、组装和管理bean。这节我们会学习更多实例化、组装和管理bean的知识。

学习路线

  • 在容器本身内,这些bean定义表示为BeanDefinition对象,其中包含以下元数据(以及其他信息):
    • 包限定的类名:通常是定义的bean的实际实现类。
    • Bean行为配置元素,它声明Bean在容器中的行为(作用域、生命周期回调等等)。
    • 对bean执行其工作所需的其他bean的引用。这些引用也称为协作者或依赖项。
    • 要在新创建的对象中设置的其他配置设置—例如,池的大小限制或要在管理连接池的bean中使用的连接数。

此元数据转换为组成每个bean定义的一组属性。下表描述了这些属性:

属性解释
class实例化bean
id/name/alias命名bean
scope bean的范围
Constructor-arg 基于构造函数的依赖注入
property 基于setter的依赖注入
autowire 自动装配
Lazy initialization mode 延迟初始化的bean
Initialization method 初始化回调
Destruction method 破坏回调

实例化bean(class)

通过class属性来指定实例化bean。

一:使用构造方法实例化(默认采用无参构造):容器通过反射来调用构造方法。
public class Student {
    private String name;
    private Integer age;

    public Student() {

    }
}
<!--在定义bean时,应该确保id和name都是唯一的。-->
<beans>
    <!--在定义bean时,应该确保id和name都是唯一的。-->
    <bean id="student" class="com.coconut.ioc.iocTest.pojo.Student"/>
    <bean name="student" class="com.coconut.ioc.iocTest.pojo.Student"/>
</beans>
二:使用静态工厂方法进行实例化
/*StudentFactory类*/
public class StudentFactory {
    private static Student student = new Student();

    private StudentFactory() {
    }

    public static Student createStudentInstance() {
        return student;
    }
}
<!--使用class属性指定包含静态工厂方法的类,并使用名为factory-method的属性指定工厂方法本身的名称。-->
<bean id="student" class="com.coconut.ioc.iocTest.factory.StudentFactory" factory-method="createStudentInstance"/>
三:通过实例工厂方法进行实例化
public class StudentLocator {

    private static StudentService studentService = new StudentServiceImpl();

    public StudentService createStudentServiceInstance() {
        return studentService;
    }
}

<!--使用factory-bean属性指定包含创建对象实例方法的类,使用factory-method属性设置工厂方法本身的名称。-->
<!-- 工厂bean,它包含一个名为createInstance()的方法 -->
<beans>
    <!--通过实例工厂实例化-->
    <bean id="serviceLocator" class="com.coconut.ioc.iocTest.locator.StudentLocator">
        <!-- 注入这个定位器bean所需的任何依赖 -->
    </bean>
    <!-- 通过工厂bean创建的bean -->
    <bean id="studentService" factory-bean="serviceLocator" factory-method="createStudentServiceInstance"/>
</beans>
  • 初始化bean测试
    • 演示构造函数实例化
    • 静态工厂实例化
    • 实例工厂实例化
public class AppTest {
    /**
     * 实例化bean测试
     * 构造函数实例化
     * 静态工厂实例化
     * 实例工厂实例化
     */
    @Test
    public void InitBeanTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("iocXML/InitBean.xml");
        /*构造函数实例化*/
        Student student1 = (Student) applicationContext.getBean("student1");
        System.out.println(student1);
        /*静态工厂实例化*/
        Student student2 = (Student) applicationContext.getBean("student2");
        System.out.println(student2);
        /*实例工厂实例化*/
        StudentService studentService = (StudentServiceImpl) applicationContext.getBean("studentService");
        System.out.println(studentService);
    }
}

命名bean(id,name,alias)

在上节,我们学习了实例化bean的三种方法,这节我们将学习如何给bean命名

  • 命名bean

    • id属性
    • name属性
    • alias属性
  • 通过各种名称取bean测试

    • 三种命名方式都必须唯一
    • 三种命名方式都能取得bean
    • alias的设置略微不同
public class AppTest() {
    /**
     * 通过各种名称取bean
     * 三种命名方式都必须唯一
     */
    @Test
    public void NameBeanTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("iocXML/NameBean.xml");
        /*通过name取bean*/
        Student student = (Student) applicationContext.getBean("student");
        System.out.println(student);
        /*通过alias取名*/
        Student student1 = (Student) applicationContext.getBean("student1");
        System.out.println(student1);
    }
}

<beans>
    <!--在定义bean时,应该确保id和name和alias都是唯一的。-->
    <!--通过构造方法实例化-->
    <bean id="student1" class="com.coconut.ioc.iocTest.pojo.Student"/>
    <alias name="student1" alias="student"/>
</beans>
  • 什么情况下需要命名bean
    • 如果没有显式地提供名称或id,容器将为该bean生成唯一的名称。您不需要为bean提供name或id。
    • 如果您希望通过使用ref元素或Service Locator样式查找来通过名称引用该bean,则必须提供一个名称。
    • 不提供名称的动机与使用内部bean和自动装配协作器有关。

bean的范围(scope)

当您创建bean定义时,您创建了一个秘诀,用于创建由该bean定义定义的类的实际实例。

下表描述了支持的作用域:

范围                          描述

singleton                    (默认)将单个bean定义限定为每个Spring IoC容器的单个对象实例。

prototype                    将单个bean定义作用于任意数量的对象实例。

request                      将单个bean定义限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有自己的bean实例,该实例是在单个bean定义的基础上创建的。仅在web感知的Spring ApplicationContext上下文中有效。

session                      将单个bean定义作用于HTTP会话的生命周期。仅在web感知的Spring ApplicationContext上下文中有效。

application                  将单个bean定义作用于ServletContext的生命周期。仅在web感知的Spring ApplicationContext上下文中有效。

websocket                    将单个bean定义作用于WebSocket的生命周期。仅在web感知的Spring ApplicationContext上下文中有效。

  • bean的范围测试:
    • 单例模式只创建一次bean,后面都会复用这个bean
    • 原型模式取一次bean,就会创建一次bean
public class AppTest() {
    /**
     * bean的范围测试
     * 单例和原型(每次创建的bean不是同一个对象)
     */
    @Test
    public void ScopeBeanTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("iocXML/ScopeBean.xml");
        Student student1 = (Student) applicationContext.getBean("student1");
        Student student2 = (Student) applicationContext.getBean("student1");
        System.out.println(student1);
        System.out.println(student2);
        Student student3 = (Student) applicationContext.getBean("student2");
        Student student4 = (Student) applicationContext.getBean("student2");
        System.out.println(student3);
        System.out.println(student4);
        /*
        可以看出,单例创建bean,student1和student2的内存地址是一样的
        而原型创建bean,student3和student4内存地址不同
        */
    }
}

<beans>
    <!--单例bean和多例bean-->
    <bean id="student1" class="com.coconut.ioc.iocTest.pojo.Student" scope="singleton"/>
    <bean name="student2" class="com.coconut.ioc.iocTest.pojo.Student" scope="prototype"/>
</beans>

依赖注入(DI)

依赖注入理解

依赖注入的定义:对象仅通过构造函数参数、工厂方法的参数或在对象实例被构造后从工厂方法返回后设置的属性来定义它们的依赖项。然后容器在创建bean时注入这些依赖项。

简单理解:从new创建对象并赋值属性,转变成容器创建对象时就赋值属性。

基于构造函数依赖注入(constructor-arg)

基于构造函数的依赖注入是通过容器调用构造函数来实现的,调用构造函数时传入一些参数,每个参数代表一个依赖项

基于构造函数注入


public class Student {
    private String name;
    private Integer age;
    private Teacher teacher;

    public Student(String name, Integer age, Teacher teacher) {
        this.name = name;
        this.age = age;
        this.teacher = teacher;
    }
}

public class Teacher {
    private String name;

    public Teacher() {
    }

    public Teacher(String name) {
        this.name = name;
    }
}


<beans>
    <!--按照参数名称注入-->
    <bean id="teacherByName" class="com.coconut.ioc.iocTest.pojo.Teacher">
        <constructor-arg name="name" value="椰奶奶"/>
    </bean>

    <bean id="coconutByName" class="com.coconut.ioc.iocTest.pojo.Student">
        <constructor-arg name="name" value="椰奶"/>
        <constructor-arg name="age" value="19"/>
        <constructor-arg name="teacher" ref="teacherByName"/>
    </bean>

    <!--按照类型注入-->
    <bean id="teacherByType" class="com.coconut.ioc.iocTest.pojo.Teacher">
        <constructor-arg type="java.lang.String" value="椰奶奶"/>
    </bean>

    <bean id="coconutByType" class="com.coconut.ioc.iocTest.pojo.Student">
        <constructor-arg type="java.lang.String" value="椰奶"/>
        <constructor-arg type="java.lang.Integer" value="19"/>
        <constructor-arg ref="teacherByType"/>
    </bean>

    <!--按照顺序注入-->
    <bean id="teacherByIndex" class="com.coconut.ioc.iocTest.pojo.Teacher">
        <constructor-arg index="0" value="椰奶奶"/>
    </bean>

    <bean id="coconutByIndex" class="com.coconut.ioc.iocTest.pojo.Student">
        <constructor-arg index="0" value="椰奶"/>
        <constructor-arg index="1" value="19"/>
        <constructor-arg index="2" ref="teacherByIndex"/>
    </bean>
</beans>

测试用例

public class AppTest() {
    @Test
    public void DIBeanTest() {
        /*通过构造方法注入依赖*/
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("iocXML/DIBean.xml");
        Student coconut1 = (Student) applicationContext.getBean("coconutByName");
        System.out.println(coconut1);
        Student coconut2 = (Student) applicationContext.getBean("coconutByType");
        System.out.println(coconut2);
        Student coconut3 = (Student) applicationContext.getBean("coconutByIndex");
        System.out.println(coconut3);
    }
    /*Student{name = 椰奶, age = 19, teacher = Teacher{name = 椰奶奶}}*/
}
基于setter的依赖注入(property)

基于setter的依赖注入是由容器在调用无参数构造函数或无参数静态工厂方法来实例化bean之后调用bean的setter方法来完成的。

基于setter注入引用数据类型和基本数据类型


@Data
public class Student {
    private String name;
    private Integer age;
    private Teacher teacher;
}

@Data
public class Teacher {
    private String name;
}

<beans>
    <!--按照setter名注入,如果没有set方法,则无法注入-->
    <bean id="teacherSetterName" class="com.coconut.ioc.iocTest.pojo.Teacher">
        <property name="name" value="椰奶奶"/>
    </bean>

    <bean id="coconutSetterName" class="com.coconut.ioc.iocTest.pojo.Student">
        <property name="name" value="椰奶"/>
        <property name="age" value="19"/>
        <property name="teacher" ref="teacherSetterName"/>
    </bean>
</beans>

基于setter注入集合


@Data
public class MyClass {
    private Map<String, Integer> valueMap;
    private Map<Integer, Student> refMap;
    private List<Integer> valueList;
    private List<Student> refList;
    private Properties properties;
}

<bean name="myClass" class="com.coconut.ioc.iocTest.pojo.MyClass">
    <!--注入数据类型的List-->
    <property name="valueList">
        <list>
            <value>1</value>
            <value>2</value>
            <value>3</value>
        </list>
    </property>
    <!--注入数据类型的Map-->
    <property name="valueMap">
        <map>
            <entry key="one" value="1"/>
            <entry key="two" value="2"/>
            <entry key="three" value="3"/>
        </map>
    </property>
    <!--注入引用数据类型的List-->
    <property name="refList">
        <list>
            <ref bean="coconutByType"/>
            <ref bean="coconutByIndex"/>
            <ref bean="coconutByName"/>
        </list>
    </property>
    <!--注入引用数据类型的Map-->
    <property name="refMap">
        <map>
            <entry key="1" value-ref="coconutByName"/>
            <entry key="2" value-ref="coconutByIndex"/>
            <entry key="3" value-ref="coconutByType"/>
        </map>
    </property>
    <!--注入properties集合-->
    <property name="properties">
        <props>
            <prop key="1">椰奶</prop>
            <prop key="2">咖啡</prop>
            <prop key="3">饮料</prop>
        </props>
    </property>
</bean>
        <!--Class{
        valueMap = {one=1, two=2, three=3},
         refMap = {1=Student{name = 椰奶, age = 19, teacher = Teacher{name = 椰奶奶}}, 2=Student{name = 椰奶, age = 19, teacher = Teacher{name = 椰奶奶}}, 3=Student{name = 椰奶, age = 19, teacher = Teacher{name = 椰奶奶}}}, 
         valueList = [1, 2, 3], 
         refList = [Student{name = 椰奶, age = 19, teacher = Teacher{name = 椰奶奶}}, Student{name = 椰奶, age = 19, teacher = Teacher{name = 椰奶奶}}, Student{name = 椰奶, age = 19, teacher = Teacher{name = 椰奶奶}}],
          properties = {1=椰奶, 2=咖啡, 3=饮料}}-->
关于依赖注入使用场景(未理解,感觉有误)
  • 大多数Spring用户并不直接使用这些类(即通过编程方式)

  • 而是用@Component、@Controller等注解的类,或者基于java的@Configuration类中的@Bean方法。

  • 然后,这些源在内部转换为BeanDefinition实例,并用于加载整个Spring IoC容器实例。

自动装配(autowire)

在注解开发中更常用

Spring容器可以自动连接协作bean之间的关系。通过检查ApplicationContext的内容,可以让Spring自动为您的bean解析协作。

您可以为每个bean指定自动装配,从而可以选择要自动装配的bean。下表描述了四种自动装配模式:

模式             解释
no              (默认值)没有自动装配。Bean引用必须由ref元素定义。对于大型部署,不建议更改默认设置,因为明确指定协作者可以提供更好的控制和清晰度。在某种程度上,它记录了系统的结构。

byName          根据属性名称自动装配。Spring会查找与需要自动连接的属性同名的bean。例如,如果一个bean定义被设置为autowire by name,并且它包含一个master属性(也就是说,它有一个setMaster(..)方法),Spring会查找一个名为master的bean定义,并使用它来设置属性。

byType          如果容器中只存在一个属性类型的bean,则让属性自动连接。如果存在多个,则会抛出致命异常,这表明您不能为该bean使用byType自动织入。如果没有匹配的bean,什么也不会发生(属性没有设置)。

constructor     类似于byType,但适用于构造函数的参数。如果容器中没有constructor参数类型的bean,则会引发致命错误。

<beans>
    <!--根据属性名寻找名字和属性名一致的依赖项-->
    <bean id="coconutByName" class="com.coconut.ioc.iocTest.pojo.Student" autowire="byName"/>
    <!--根据类型寻找类型一致的依赖项,如果有多个则不能使用-->
    <bean id="coconutByType" class="com.coconut.ioc.iocTest.pojo.Student" autowire="byType"/>
</beans>

延迟初始化(Lazy initialization)

延迟初始化,会使IoC容器在第一次请求时创建bean实例,而不是在启动时创建。


<beans>
    <bean id="lazyStudent" class="com.coconut.ioc.iocTest.pojo.LazyStudent" lazy-init="true"/>
    <bean id="student" class="com.coconut.ioc.iocTest.pojo.Student"/>
</beans>

初始化回调(Initialization)和销毁回调(Destruction)

public class Student {
    public Student() {

    }

    public void init() {
        System.out.println("student初始化");
    }

    public void destroy() {
        System.out.println("student销毁");
    }
}

<beans>
    <bean id="student" class="com.coconut.ioc.iocTest.pojo.Student" init-method="init" destroy-method="destroy"/>
</beans>

测试用例

public class AppTest() {
    @Test
    public void callBackBeanTest() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("iocXML/CallBackBean.xml");
        Student student = (Student) applicationContext.getBean("student");
        /*主动销毁容器*/
        applicationContext.close();
    }
}

方法注入(后续补充)

容器扩展点(后续补充)

基于JSR 250 的元注解开发

  • 什么是JSR?
    • JSRJava Specification Requests的缩写,意思是Java 规范提案。

实例化bean

@Component
  • @Component
    • 用于标识一个类为一个组件,该类会被Spring自动扫描并注册为一个bean

@Repository
public class StudentDaoImpl implements StudentDao {

}

注意事项:
1.@Repository@Service@Controller@Component的特化(分别在持久层、服务层和表示层)。
2.使用特例化的注解是更好的选择。@Repository已经被支持作为持久层中自动异常转换的标记。
3.@Mapper也是@Component的特化,不过它是来自Mybatis框架自身对Spring的支持

命名bean

默认情况下会自动命名,例如下方StudentImpl被自动命名为studentImpl


@Repository
public class StudentImpl implements Student {
    // ...
}

也可以设置属性value来手动命名,在只有一个参数时,可以省略value


@Repository("student")
public class StudentImpl implements Student {
    // ...
}

@Scope

如下使用@Scope注解


@Component
public class FactoryMethodComponent {

    @Bean
    @Scope("prototype")
    public TestBean prototypeInstance(InjectionPoint injectionPoint) {
        return new TestBean("prototypeInstance for " + injectionPoint.getMember());
    }
}

注意事项:
要为范围解析提供自定义策略,不应该依赖于基于注释的方法,您可以实现ScopeMetadataResolver接口。
请确保包含一个默认的无参数构造函数。然后,用户可以在配置扫描器时提供完全限定的类名,
如下面的例子所示,其中包含注释和bean定义:


@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
    // ...
}

自动装配

@Autowired
  • @Autowired
    • 可以在字段、方法和构造函数参数上使用。
    • 默认:按照类型查找(ByType)
笔记:
1.可以使用JSR 330 的@Inject注释代替@Autowired注释,后面会讲述JSR330标准。
2.如果有多个构造函数可用并且没有主要构造函数,必须至少对其中一个构造函数注释,以指示IoC容器使用哪一个实例化。

3.设置允许为空
3.1将required属性设置为false表示相应的属性对于自动装配而言是可选的,如果不能自动装配,该属性将被忽略。这允许为属性分配默认值,这些默认值可以通过依赖注入选择性地覆盖。
@Autowired(value="false")
public void setMovieFinder(MovieFinder movieFinder) {
...
}
3.2或者您可以通过 Java 8 表达特定依赖项的非必需性质java.util.Optional。
@Autowired
public void setMovieFinder(Optional
<MovieFinder> movieFinder) {
    ...
    }
    3.3或者采用@Nullable,在参数之前注释
    @Autowired
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {

    }
@Primary(调整优先顺序)
  • @Autowired配合@Primary对基于按类型的自动装配进行微调

  • 当只有一个主要候选实例时,只用@Primary是一种有效的按类型自动装配方法


@Configuration
public class MovieConfiguration {
    /*@Primary指示当多个 bean 候选自动装配到单值依赖项时,应优先考虑特定的 bean。如果候选者中恰好存在一个主 bean,则它成为自动装配值。*/
    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() {
    }

    @Bean
    public MovieCatalog secondMovieCatalog() {
    }

    // ...
}

<bean class="example.SimpleMovieCatalog" primary="true"><!-- 注入此 bean 所需的任何依赖项 --></bean>
@Qualifier(指定名称装配)
  • @Autowired配合@Qualifier来指定一个名称的Bean进行注入
public class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...
}

<bean class="example.SimpleMovieCatalog">
    <qualifier value="main"/>
</bean>
使用泛型作为隐式自动装配限定符
public class AppTest {
    @Autowired
    private Store<String> s1; // <String> qualifier, injects the stringStore bean

    @Autowired
    private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean   
}
@Resource
  • @Resource(“名称”)
    • 可以在字段、方法和构造函数参数上使用。
    • 默认:按照名称查找(ByName)
    • 相当于@AutoWired + @Qualifier组合使用
笔记
在没有明确指定名称的@Resource使用情况下
1.@Resource会先根据属性名来寻找bean
2.如果没有寻找到对应bean
3.@Resource会先根据类型名来寻找bean
@Value
  • @Value
    • 用于注入外部属性
    • 使用方式@Value(“${属性名}”)
      详细请查看以下示例:

@Configuration
@PropertySource("classpath:jdbc.properties")
public class AppConfig {
    @Value("${jdbc.driverClassName}")
    private String driverClassName;

    @Value("${jdbc.url}")
    private String url;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        return dataSource;
    }

    @PostConstruct
    public void registerDriver() throws ClassNotFoundException {
        Class.forName(driverClassName);
    }
}

以及以下application.properties文件:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test

@Lazy

添加@Lazy注解,能将bean设置为延迟初始化

@PostConstruct和@PreDestroy

初始化回调和销毁回调

public class callbackTest {

    @PostConstruct
    public void init() {
        // 初始化回调
    }

    @PreDestroy
    public void destroy() {
        // 销毁回调
    }
}

JSR 330 新增的元注解开发

@Configuration

类路径扫描和托管组件

@Configuration注解是JSR-330规范中的一部分。

Spring可以自动检测构造型类,并向ApplicationContext注册相应的BeanDefinition实例。

例如,下面两个类适合这种自动检测:


@Service
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

@Repository
public class JpaMovieFinder implements MovieFinder {
    // implementation elided for clarity
}

基于java的注解@ComponentScan,JSR 330 是采用的xml扫描


@Configuration
@ComponentScan("org.example")
public class AppConfig {
    // ...
}

用于替代

<?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
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.example"/>

</beans>

@Inject

@Inject注释代替@Autowired注释

想要使用@Inject注解需要导入包


<dependency>
    <groupId>jakarta.inject</groupId>
    <artifactId>jakarta.inject-api</artifactId>
    <version>2.0.0</version>
</dependency>
  • @Inject
    • @Autowired一致,可以在字段、方法和构造函数参数上使用。
      • @Inject没有Require属性,默认设置为自动装配是可选的
    • 额外可以注入点声明为Provider,允许按需访问较短作用域的bean,或者通过调用Provider.get()延迟访问其他bean。
    • 设置允许为空的方式与@Autowired一致

可以通过@Named注解指定装入bean

import jakarta.inject.Inject;
import jakarta.inject.Named;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

@Named和@ManageBean

  • @Named@ManagedBean
    • 可以作为@Component注解的标准等价物
import jakarta.inject.Inject;
import jakarta.inject.Named;

@Named("movieListener")  // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

JSR-330标准注释的局限性

使用标准注释时,你应该知道有些重要的特性是不可用的,如下表所示:

Spring组件模型元素与JSR-330变体的对比

Spring                      jakarta.inject.*        	jakarta.inject 限制与注释
    
@Autowired                  @Inject                     @Inject没有`required`属性。可以和Java 8的Optional一起使用。

@Component                  @Named / @ManagedBean       JSR-330没有提供一个可组合的模型,只是提供了一种识别命名组件的方法。

@Scope("singleton")         @Singleton                  Spring容器中声明的JSR-330 bean默认是单例的。为了使用单例之外的作用域,应该使用Spring的@Scope注解。

@Qualifier                  @Qualifier / @Named         jakarta.inject.Qualifier只是一个用于构建自定义限定符的元注解。具体的字符串限定符(比如Spring的@Qualifier带值)可以通过jakarta.inject.Named关联起来。

@Value                      -

@Lazy                       -
    
ObjectFactory               Provider                    jakarta.inject.Provider是Spring的ObjectFactory的直接替代品,只是有一个更短的get()方法名。它也可以与Spring的@Autowired结合使用,或者与无注解的构造函数和setter方法结合使用。

基于Java的容器配置

基本概念:@Bean和@Configuration

  • @Bean

    • 要声明一个bean,您可以使用注释对方法进行注释@Bean,由 Spring IoC 容器管理。
    • 默认情况下,bean名称与方法名称相同。
  • @Configuration

    • 注释一个类@Configuration表明它的主要目的是作为 bean 定义的来源。
    • 此外,类允许通过调用同一类中的@Configuration其他方法来定义 bean 间依赖关系。
    • 使用@ComponentScan注解添加一个自动扫描,来告诉Spring需要在哪些包中查找我们提供@Component声明的Bean

在Spring的Java配置支持中,核心组件是使用@Configuration注解的类和使用@Bean注解的方法。

@Bean注解用于指示一个方法实例化、配置和初始化一个新对象,该对象将由Spring IoC容器进行管理。对于熟悉Spring的<beans/> XML配置的人来说,@Bean注解的作用与<bean/>元素相同。您可以在任何Spring @Component上使用@Bean注解的方法。然而,它们通常与@Configuration bean一起使用。

使用@Configuration注解对一个类进行注解表示其主要目的是作为bean定义的来源。此外,@Configuration类允许通过调用同一类中的其他@Bean方法来定义bean之间的依赖关系。最简单的@Configuration类如下所示:


@Configuration
public class AppConfig {

    @Bean
    public MyServiceImpl myService() {
        return new MyServiceImpl();
    }
}

等效于


<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

当在没有使用@Configuration注解的类中声明@Bean方法时,它们被称为以“lite”模式进行处理。在@Component甚至普通类中声明的@Bean方法被视为“lite”,其包含类的主要目的与@Bean方法是一种额外的奖励。例如,服务组件可以通过每个适用组件类上的额外@Bean方法向容器公开管理视图。在这种情况下,@Bean方法是一种通用的工厂方法机制。
与完整的@Configuration不同,lite @Bean方法不能声明bean之间的依赖关系。相反,它们操作其包含组件的内部状态,并且可以选择操作它们声明的参数。因此,这样的@Bean方法不应调用其他@Bean方法。每个这样的方法实际上只是一个特定bean引用的工厂方法,没有任何特殊的运行时语义。这里的积极副作用是在运行时不需要应用CGLIB子类化,因此在类设计方面没有任何限制(即,包含类可以是final等)。
在常见的情况下,@Bean方法应在@Configuration类中声明,以确保始终使用“full”模式,并将跨方法引用重定向到容器的生命周期管理。这可以防止同一个@Bean方法在常规的Java调用中意外被调用,从而有助于减少在“lite”模式下操作时难以追踪的细微错误。

理解:在"lite" @Bean模式中,@Bean方法不能声明bean之间的依赖关系。相反,它们只能操作其所属组件的内部状态和声明的参数。这意味着如果两个bean不在同一个@Configuration类中,它们之间就没有直接的依赖关系。
在"lite" @Bean模式中,每个@Bean方法实际上只是创建特定bean实例的工厂方法,而不会涉及到特殊的运行时语义。这种模式的一个积极的副作用是在运行时不需要应用CGLIB子类化,因此对类设计没有任何限制,包含类可以是final类或具有其他限制。
如果您需要在不同的@Bean方法之间建立依赖关系,以确保它们按正确的顺序进行初始化和注入,您应该将这些@Bean方法放在同一个@Configuration类中。这样,它们将被视为完整的@Configuration模式,可以利用Spring容器的生命周期管理和依赖注入机制。

如何理解@Component,@Configuration@Bean
@Configuration 注解提供了更多的灵活性和功能,可以支持更复杂的配置场景。在 @Configuration 类中,您可以使用其他注解来配置 Bean,例如 @Bean@ComponentScan@Import 等。您还可以使用条件注解,根据特定条件来决定是否创建某个 Bean。此外,@Configuration 类还可以通过实现 ImportBeanDefinitionRegistrar 接口或使用 @Import 注解来导入其他配置类。
另一方面,@Component 注解是一个通用的注解,用于标记一个类为组件类,表示它是应用程序的一个普通组件。@Component 注解相对简单,不提供像 @Configuration 那样的高级配置功能。它通常用于简单的 Bean 的创建,无需特殊的配置。
总结起来,@Configuration 注解适用于更复杂的配置场景,可以支持更多的配置选项和功能。而 @Component 注解则适用于简单的组件类,用于将类实例化为一个 Bean,但提供的配置选项有限。

使用Bean注解

  • @Bean
    • 要声明一个bean,您可以使用注释对方法进行注释@Bean,由 Spring IoC 容器管理。
    • 默认情况下,bean名称与方法名称相同。
    • @Bean注解通常用于配置类中,用于声明一个方法,返回值为一个对象,该对象会被注册为一个bean。例如:

@Configuration
public class AppConfig {

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

完全等同于如下xml配置


<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
接受生命周期回调
  • 接受生命周期回调
    • 使用注释定义的任何类都@Bean支持常规生命周期回调,并且可以使用JSR-250 中的@PostConstruct和注释。

    • 还完全支持常规的 Spring生命周期回调。如果 bean 实现InitializingBean、DisposableBean或Lifecycle,它们各自的方法将由容器调用。

    • 还完全支持标准接口集ware(例如BeanFactoryAware、 BeanNameAware、 MessageSourceAware、 ApplicationContextAware等)。

    • 注释@Bean支持指定任意初始化和销毁回调方法,如以下示例所示:

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();
    }
}
笔记
默认情况下,使用Java配置定义的具有公开的close或shutdown方法的bean会自动被调用到销毁回调函数中。如果你有一个公开的关闭或关闭方法,并且你不希望它在容器关闭时被调用,你可以在你的bean定义中添加@Bean(destroyMethod = "")来禁用默认(推断)模式。

下面的例子展示了如何防止数据源的自动销毁回调:
public class Test() {
    @Bean(destroyMethod = "")
    public DataSource dataSource() throws NamingException {
        return (DataSource) jndiTemplate.lookup("MyDS");
    }
}

使用实例化Spring容器AnnotationConfigApplicationContext

  • AnnotationConfigApplicationContext实现类
    • 不仅能够接受 @Configuration类作为输入
    • 而且能够接受普通@Component类和使用 JSR-330 元数据注释的类。
通过构造函数参数指定configuration类

在实例化AnnotationConfigApplicationContext时,可以使用@Configuration类作为参数。

这样就可以使用完全无xml的Spring容器,如下面的例子所示:

public class App() {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
    }
}

@Configuration
public class AppConfig {

}
通过编程方式指定configuration类
public class App() {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(AppConfig.class);
        ctx.refresh();
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
    }
}

编写基于 Java 的配置

Spring 的基于 Java 的配置功能允许您编写注释,这可以降低配置的复杂性。

使用@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.class和,只需要显式提供,如以下示例所示:ConfigB.classConfigB

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

        // bean A和B可以使用……
        A a = ctx.getBean(A.class);
        B b = ctx.getBean(B.class);
    }
}
@ComponentScan

使用@ComponentScan注解添加一个自动扫描,来告诉Spring需要在哪些包中查找我们提供@Component声明的Bean。

如果要添加多个包进行扫描,可以使用@ComponentScans来批量添加。这里我们演示将bean包下的所有类进行扫描:


@Configuration
@ComponentScan("com.coconut.ioc.iocTest")
public class AppConfig {
    @Bean
    public User user() {
        User user = new User();
        user.setName("Tom");
        return user;
    }
}

环境抽象(后续补充)

ApplicationContext的其他功能(后续补充)

  • 为了以更具框架性的方式增强BeanFactory功能,Spring的context包还提供了以下功能:

    • 通过MessageSource接口以i18n方式访问消息。

    • 通过ResourceLoader接口访问资源,如URL和文件。

    • 通过ApplicationEventPublisher接口向实现ApplicationListener接口的Bean发布事件。

    • 通过HierarchicalBeanFactory接口加载多个(分层)上下文,让每个上下文专注于一个特定的层,例如应用程序的Web层。

BeanFactory的应用程序接口(后续补充)

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值