万字博客带你了解Spring Framework 的全貌

news2025/8/12 18:32:29

1.写在前面

我之前出过一个系列介绍的是spring的源码的系列,但是由于当时个人的水平有限,有些地方介绍的也是模棱两可的,打算重启这块内容,上次只是介绍其中的一部分,例如国际化,事件等等这块的源码都没有介绍,还有事务这块内容。spring现在也不单单是一个简简单单的只是spring framework,它有很广的生态,但是这些所有的生态都是基于spring framework来的,今天我们就来全览一下spring的内容,同时介绍一下IOC,以及spring是如何实现IOC的,还有就是spring Bean 的一些基础的知识。

2.核心特性

在这里插入图片描述

  • IOC容器 (IOC container)
  • Spring 事件(Events)
  • 资源管理(Resources)
  • 国际化(i18n)
  • 校验(Validation)
  • 数据绑定(Data Binding)
  • 类型转换(Type Conversion)
  • Spring 表达式 (Spring Express Language)
  • 面向切面编程(AOP)

3.数据存储

  • JDBC
  • 事务抽象(Transactions)
  • Dao 支持(Dao Suppurt)
  • O/R映射(O/R Mapping)
  • XML编列(XML Marshalling)

4.Web技术

  • Web Servlet 技术栈
    • Spring MVC
    • WebSocket
    • SockJS
  • Web Reactive 技术栈
    • Spring WebFlux
    • WebClient
    • WebSocket

5.技术整合

  • 远程调用(Remoting)
  • Java消息服务(JMS)
  • Java连接架构(JCA)
  • Java管理扩展(JMX)
  • Java邮件客户端(Email)
  • 本地任务(Tasks)
  • 本地调度(Scheduling)
  • 缓存抽象(Caching)
  • Spring 测试(Testing)

6.测试

  • 模拟对象(Mock Objects)
  • TestContext框架 (TestContext Framework)
  • Spring MVC测试(Spring MVC Test)
  • Web测试客户端(WebTestClient)

7.Spring 版本特性

在这里插入图片描述

8.Spring 模块化设计

在这里插入图片描述

9.Spring对Java语言特性运用

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

10.Spring对JDK API的实践

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

11.Spring对Java EE API整合

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

12.Spring的编程模型

在这里插入图片描述

13.什么是IOC?

In software engineering, inversion of control (IoC) is a programming principle. IoC inverts the flow of control as compared to traditional control flow. In IoC, custom-written portions of a computer program receive the flow of control from a generic framework. A software architecture with this design inverts control as compared to traditional procedural programming: in traditional programming, the custom code that expresses the purpose of the program calls into reusable libraries to take care of generic tasks, but with inversion of control, it is the framework that calls into the custom, or task-specific, code.

在软件工程中,控制反转(IOC)是编程原理。与传统的对照流相比,IOC将控制流动。在IOC中,计算机程序的定制写入部分从通用框架接收控制流。与传统的程序编程相比,具有此设计的软件体系结构将控制权反转:在传统编程中,将程序的目的表达为可重复使用的库以处理通用任务,但通过控制反转,它是框架该调用到自定义或特定任务的代码中。

14.IOC发展简介

  • IOC的简史
    • 1983年, Richard E. Sweet 在《The Mesa Programming Environment》 中提出“ Hollywood Principle” ( 好莱坞原则)
    • 1988年, Ralph E. Johnson & Brian Foote 在《Designing Reusable Classes》 中提出“ Inversion of control” ( 控制反转)
    • 1996年, Michael Mattsson 在《Object-Oriented Frameworks, A survey of methodological issues》 中将“ Inversion of control” 命名为 “ Hollywood principle”
    • 2004年, Martin Fowler 在《Inversion of Control Containers and the Dependency Injection pattern》 中提出了自己对 IoC 以及 DI 的理解
    • 2005年, Martin Fowler 在 《InversionOfControl》 对 IoC 做出进一步的说明

15.IOC 主要实现策略

Implementation techniques(实现技术) 小节的定义:

  • In object-oriented programming, there are several basic techniques to implement inversion of control(在面向对象编程中,有几种实现控制反转的基本技术).
  • These are:
    • Using a service locator pattern(使用服务定位器模式)
    • Using dependency injection, for example (例如,使用依赖项注入)
      • Constructor injection (构造函数注入)
      • Parameter injection (参数注入)
      • Setter injection (setter 注入)
      • Interface injection (接口注入)
    • Using a contextualized lookup (使用上下文化查找)
    • Using template method design pattern (采用模板方法设计模式)
    • Using strategy design pattern (使用策略设计模式)

《Expert One-on-One™ J2EE™ Development without EJB™》 提到的主要实现策略:IoC is a broad concept that can be implemented in different ways. There are two main types:(IO是一个广泛的概念,可以以不同的方式实现。主要有两种类型)

  • Dependency Lookup: The container provides callbacks to components, and a lookup context. This is the EJB and Apache Avalon approach. It leaves the onus on each component to use container APIs to look up resources and collaborators. The Inversion of Control is limited to the container invoking callback methods that application code can use to obtain resources.
  • 依赖查找:容器提供对组件的回调和查找上下文。 这是 EJB 和 Apache Avalon 的方法。 它让每个组件都有责任使用容器 API 来查找资源和协作者。 控制反转仅限于调用应用程序代码可用于获取资源的回调方法的容器。
  • Dependency Injection: Components do no look up; they provide plain Java methods enabling the container to resolve dependencies. The container is wholly responsible for wiring up components,passing resolved objects in to JavaBean properties or constructors. Use of JavaBean properties is called Setter Injection; use of constructor arguments is called Constructor Injection.
  • 依赖注入:组件不查找; 它们提供普通的 Java 方法,使容器能够解决依赖关系。 容器完全负责连接组件,将已解析的对象传递给 JavaBean 属性或构造函数。 使用 JavaBean 属性称为 Setter Injection; 使用构造函数参数称为构造函数注入。

16.IOC 容器的职责

在 Overview 小节中提到:

  • Inversion of control serves the following design purposes:(控制反转服务于以下设计目的:)
  • To decouple the execution of a task from implementation.(将任务的执行与实现分离。)
  • To focus a module on the task it is designed for.(使模块专注于它设计的任务。)
  • To free modules from assumptions about how other systems do what they do and instead rely on contracts.(将模块从关于其他系统如何做它们所做的事情的假设中解放出来,而是依赖于契约。)
  • To prevent side effects when replacing a module.(防止更换模块时产生副作用。)
  • Inversion of control is sometimes facetiously referred to as the “Hollywood Principle: Don’t call us, we’ll call you”.”(控制反转有时被戏称为“好莱坞原则:不要打电话给我们,我们会打电话给你”。)

通用职责

  • 依赖处理
    • 依赖查找
    • 依赖注入

生命周期管理

  • 容器
  • 托管的资源( Java Beans 或其他资源)

配置

  • 容器
  • 外部化配置
  • 托管的资源( Java Beans 或其他资源)

17.IoC 容器的实现

主要实现

  • Java SE
  • Java Beans
  • Java ServiceLoader SPI
  • JNDI( Java Naming and Directory Interface)

Java EE

  • EJB( Enterprise Java Beans)
  • Servlet

开源

  • Apache Avalon( http://avalon.apache.org/closed.html)
  • PicoContainer( http://picocontainer.com/)
  • Google Guice( https://github.com/google/guice)
  • Spring Framework( https://spring.io/projects/spring-framework)

这儿我们可以看下Java Beans的代码。具体的如下:

package org.geekbang.ioc.java.beans;

/**
 * 描述人的POJO
 * <p>
 * setter / getter方法
 * 可写方法(writable) / 可读方法(readable)
 */
public class Person {

    String name;

    Integer age;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

package org.geekbang.ioc.java.beans;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyEditorSupport;
import java.util.stream.Stream;

/**
 * {@link BeanInfo} 实例
 */
public class BeanInfoDemo {

    public static void main(String[] args) throws IntrospectionException {
        BeanInfo beanInfo = Introspector.getBeanInfo(Person.class, Object.class);

        Stream.of(beanInfo.getPropertyDescriptors())
                .forEach(propertyDescriptor -> {
                    // PropertyDescriptor 允许添加属性编辑器 PropertyEditor
                    // GUI -> text(String) -> PropertyType
                    // name -> String
                    // age -> Integer
                    Class<?> propertyType = propertyDescriptor.getPropertyType();
                    String propertyName = propertyDescriptor.getName();
                    if ("age".equals(propertyName)) { // 为"age" 属性/字段 添加PropertyEditor
                        // String -> Integer
                        //Integer.valueOf()
                        propertyDescriptor.setPropertyEditorClass(StringToIntegerPropertyEditor.class);
                       //propertyDescriptor.createPropertyEditor()
                    }
                });
    }

    static class StringToIntegerPropertyEditor extends PropertyEditorSupport {
        public void setAsText(String text) throws java.lang.IllegalArgumentException {
            Integer value = Integer.valueOf(text);
            setValue(value);
        }
    }
}

上面简单的介绍了JavaBean的一些简单的API。Spring中也有简单的应用。

18.传统 IoC 容器的实现

Java Beans 作为 IoC 容器

特性

  • 依赖查找
  • 生命周期管理
  • 配置元信息
  • 事件
  • 自定义
  • 资源管理
  • 持久化

规范

  • JavaBeans: https://www.oracle.com/technetwork/java/javase/tech/index-jsp-138795.html
  • BeanContext: https://docs.oracle.com/javase/8/docs/technotes/guides/beans/spec/beancontext.html

19.轻量级 IoC 容器

《Expert One-on-One™ J2EE™ Development without EJB™》 认为轻量级容器的特征:

  • A container that can manage application code.(可以管理应用程序代码的容器。)
  • A container that is quick to start up.(一个可以快速启动的容器。)
  • A container that doesn’t require any special deployment steps to deploy objects within it.(不需要任何特殊部署步骤就可以在其中部署对象的容器。)
  • A container that has such a light footprint and minimal API dependencies that it can be run in a variety of environments.(一个容器占用空间小,依赖性最小,可以在各种环境中运行。)
  • A container that sets the bar for adding a managed object so low in terms of deployment effort and performance overhead that it’s possible to deploy and manage fine-grained objects, as well as coarse-grained components.(在部署工作和性能开销方面,该容器将添加管理对象的门槛设置得非常低,以至于可以部署和管理细粒度对象以及粗粒度组件。)

Expert One-on-One™ J2EE™ Development without EJB™》 认为轻量级容器的好处:

  • Escaping the monolithic container (逃离整体容器)
  • Maximizing code reusability (最大化代码可重用性)
  • Greater object orientation (更强的对象定向)
  • Greater productivity (更大的生产力)
  • Better testability (更好的可测试性)

20.依赖查找 VS. 依赖注入

在这里插入图片描述

21.构造器注入 VS. Setter 注入

Spring Framework 对构造器注入与 Setter 的论点:

  • The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.
  • Spring 团队普遍提倡构造函数注入,因为它可以让您将应用程序组件实现为不可变对象,并确保所需的依赖项不为空。 此外,构造函数注入的组件总是以完全初始化的状态返回给客户端(调用)代码。 作为旁注,大量的构造函数参数是一种糟糕的代码味道,暗示该类可能有太多责任,应该重构以更好地解决关注点的适当分离。
  • Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or reinjection later. Management through JMX MBeans is therefore a compelling use case for setter injection.
  • Setter 注入应该主要只用于可选的依赖项,这些依赖项可以在类中分配合理的默认值。 否则,必须在代码使用依赖项的任何地方执行非空检查。 setter 注入的一个好处是 setter 方法使该类的对象可以在以后重新配置或重新注入。 因此,通过 JMX MBeans 进行管理是 setter 注入的一个引人注目的用例。

《Expert One-on-One™ J2EE™ Development without EJB™》 认为 Setter 注入的优点:

Advantages of Setter Injection include:(setter注入的优点包括:)

  • JavaBean properties are well supported in IDEs. (Java bean属性在IDEs中得到了很好的支持。)
  • JavaBean properties are self-documenting. (Java bean属性是自文档化的。)
  • JavaBean properties are inherited by subclasses without the need for any code.(Java bean属性由子类继承,不需要任何代码。)
  • It’s possible to use the standard JavaBeans property-editor machinery for type conversions if necessary.(如果需要,可以使用标准Java bean属性编辑器机制进行类型转换。)
  • Many existing JavaBeans can be used within a JavaBean-oriented IoC container without modification.(许多现有的Java bean无需修改就可以在面向Java bean的IOC容器中使用。)
  • If there is a corresponding getter for each setter (making the property readable, as well as writable), it is possible to ask the component for its current configuration state. This is particularly useful if we want to persist that state: for example, in an XML form or in a database. With Constructor Injection, there’s no way to find the current state.(如果每个setter都有相应的getter(使属性既可读又可写),则可以向组件请求其当前配置状态。如果我们想要持久化该状态:例如,在xml表单或数据库中,这是特别有用的。使用构造函数注入,没有办法找到当前状态。)
  • Setter Injection works well for objects that have default values, meaning that not all properties need to be supplied at runtime.(Setter 注入适用于具有默认值的对象,这意味着并非所有属性都需要在运行时提供。)

《Expert One-on-One™ J2EE™ Development without EJB™》 认为 Setter 注入的缺点:

Disadvantages include:(缺点包括)

  • The order in which setters are called is not expressed in any contract. Thus, we sometimes need to invoke a method after the last setter has been called to initialize the component. Spring provides the org.springframework.beans.factory.InitializingBean interface for this; it also provides the ability to invoke an arbitrary init method. However, this contract must be documented to ensure correct use outside a container.
  • 调用 setter 的顺序没有在任何合约中表达。 因此,有时我们需要在调用最后一个 setter 来初始化组件之后调用一个方法。 Spring为此提供了org.springframework.beans.factory.InitializingBean接口; 它还提供了调用任意 init 方法的能力。 但是,必须记录此合同以确保在容器外正确使用。
  • Not all the necessary setters may have been called before use. The object can thus be left partially configured
  • 并非所有必要的设置器在使用前都已被调用。 因此,对象可以保留部分配置

《Expert One-on-One™ J2EE™ Development without EJB™》 认为构造器注入的优点:

Advantages of Constructor Injection include:(构造函数注入的优点包括)

  • Each managed object is guaranteed to be in a consistent state—fully configured—before it can be invoked in any business methods. This is the primary motivation of Constructor Injection. (However, it is possible to achieve the same result with JavaBeans via dependency checking, as Spring can optionally perform.) There’s no need for initialization methods.
  • 每个托管对象在被任何业务方法调用之前都保证处于一致的状态——完全配置。 这是构造函数注入的主要动机。 (但是,可以通过依赖性检查用 JavaBeans 实现相同的结果,因为 Spring 可以选择性地执行。)不需要初始化方法。
  • There may be slightly less code than results from the use of multiple JavaBean methods, although will be no difference in complexity.
  • 与使用多个 JavaBean 方法的结果相比,代码可能略少,但在复杂性上没有差异。

《Expert One-on-One™ J2EE™ Development without EJB™》 认为构造器注入的缺点:

Disadvantages include:(缺点包括)

  • Although also a Java-language feature, multi-argument constructors are probably less common in existing code than use of JavaBean properties.(虽然也是 Java 语言的特性,但多参数构造函数在现有代码中可能不如使用 JavaBean 属性常见。)
  • Java constructor arguments don’t have names visible by introspection.(Java 构造函数参数没有内省可见的名称)
  • Constructor argument lists are less well supported by IDEs than JavaBean setter methods.(IDE 对构造函数参数列表的支持不如 JavaBean setter 方法。)
  • Long constructor argument lists and large constructor bodies can become unwieldy.(较长的构造函数参数列表和较大的构造函数主体可能变得笨拙。)
  • Concrete inheritance can become problematic.(具体继承可能会成为问题。)
  • Poor support for optional properties, compared to JavaBeans(与 JavaBeans 相比,对可选属性的支持较差)
  • Unit testing can be slightly more difficult(单元测试可能会稍微困难一些)
  • When collaborators are passed in on object construction, it becomes impossible to change the reference held in the object. (当合作者在对象构造中被传入时,就不可能更改对象中持有的引用。)

22.Spring IOC 依赖查找

根据 Bean 名称查找

  • 实时查找
  • 延迟查找

根据 Bean 类型查找

  • 单个 Bean 对象
  • 集合 Bean 对象
  • 根据 Bean 名称 + 类型查找

根据 Java 注解查找

  • 单个 Bean 对象
  • 集合 Bean 对象

具体的几种查找的代码如下:

package org.learn.spring.ioc.overview.domain;

// 用户类
public class User {

    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
package org.learn.spring.ioc.overview.domain;

import org.learn.spring.ioc.overview.annotation.Super;

// 超级用户
@Super
public class SuperUser extends User {

    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "SuperUser{" +
                "address='" + address + '\'' +
                "} " + super.toString();
    }
}
package org.learn.spring.ioc.overview.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Super {
}
<?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="user" class="org.learn.spring.ioc.overview.domain.User">
        <property name="id" value="1"/>
        <property name="name" value="胡桃"/>
    </bean>

    <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
        <property name="targetBeanName" value="user"/>
    </bean>

    <bean id="superUser" class="org.learn.spring.ioc.overview.domain.SuperUser" parent="user" primary="true">
        <property name="address" value="璃月"/>
    </bean>
</beans>
package org.learn.spring.ioc.overview.dependency.lookup;

import org.learn.spring.ioc.overview.annotation.Super;
import org.learn.spring.ioc.overview.domain.SuperUser;
import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Map;

// 依赖查找的示例
public class DependencyLookupDemo {

    public static void main(String[] args) {
        // 配置XML 配置文件
        // 启动spring的应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:META-INF/dependency-lookup-context.xml");
        // 1.按照名称查找
        lookupInRealTime(beanFactory);
        lookupInLazy(beanFactory);

        // 2.按照类型查找
        lookupByType(beanFactory);
        lookupCollectionByType(beanFactory);

        // 3.按照类型和名称查找
        lookupByNameAndType(beanFactory);

        // 4.通过注解查找对象
        lookupCollectionByAnnotation(beanFactory);
    }

    // 实时查找
    private static void lookupInRealTime(BeanFactory beanFactory) {
        User user = (User) beanFactory.getBean("user");
        System.out.println("实时查找:" + user);
    }

    // 延时查找
    private static void lookupInLazy(BeanFactory beanFactory) {
        ObjectFactory<User> objectFactory = (ObjectFactory<User>) beanFactory.getBean("objectFactory");
        User user = objectFactory.getObject();
        System.out.println("延时查找:" + user);
    }

    // 按照类型查找
    private static void lookupByType(BeanFactory beanFactory) {
        User user = beanFactory.getBean(User.class);
        System.out.println("按照类型查找单一对象:" + user);
    }

    // 按照类型查找集合
    private static void lookupCollectionByType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
            System.out.println("按照类型查找到的所有的 User 集合对象:" + users);
        }
    }

    // 按照类型和名称
    private static void lookupByNameAndType(BeanFactory beanFactory) {
        User user = beanFactory.getBean("user", User.class);
        System.out.println("按照类型和名称查找:" + user);
    }


    // 按照注解查找集合对象
    private static void lookupCollectionByAnnotation(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = (Map) listableBeanFactory.getBeansWithAnnotation(Super.class);
            System.out.println("按照注解查找到的所有的标注@SuperUser User 集合对象:" + users);
        }
    }
}

23.Spring IOC 依赖注入

根据 Bean 名称注入

根据 Bean 类型注入

  • 单个 Bean 对象
  • 集合 Bean 对象

注入容器內建 Bean 对象

注入非 Bean 对象

注入类型

  • 实时注入
  • 延迟注入

具体的几种依赖注入的代码如下:

package org.learn.spring.ioc.overview.repository;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.ApplicationContext;

import java.util.Collection;

// 用户的信息仓库
public class UserRepository {

    private Collection<User> users; // 自定义Bean

    private BeanFactory beanFactory; // 内建非Bean对象(依赖)

    private ObjectFactory<User> userObjectFactory;

    private ObjectFactory<ApplicationContext> objectFactory;

    public Collection<User> getUsers() {
        return users;
    }

    public void setUsers(Collection<User> users) {
        this.users = users;
    }

    public BeanFactory getBeanFactory() {
        return beanFactory;
    }

    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public ObjectFactory<User> getUserObjectFactory() {
        return userObjectFactory;
    }

    public void setUserObjectFactory(ObjectFactory<User> userObjectFactory) {
        this.userObjectFactory = userObjectFactory;
    }

    public ObjectFactory<ApplicationContext> getObjectFactory() {
        return objectFactory;
    }

    public void setObjectFactory(ObjectFactory<ApplicationContext> objectFactory) {
        this.objectFactory = objectFactory;
    }
}
<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 通过导入复用dependency-lookup-context.xml -->
    <import resource="dependency-lookup-context.xml"/>

    <!--<bean id="userRepository" class="org.learn.spring.ioc.overview.repository.UserRepository">
        &lt;!&ndash; 手动配置 &ndash;&gt;
        <property name="users">
            <util:list>
                <ref bean="user"/>
                <ref bean="superUser"/>
            </util:list>
        </property>
    </bean>-->

    <!-- Auto-wiring -->
    <bean id="userRepository" class="org.learn.spring.ioc.overview.repository.UserRepository" autowire="byType"/>


</beans>
package org.learn.spring.ioc.overview.dependency.injection;

import org.learn.spring.ioc.overview.domain.User;
import org.learn.spring.ioc.overview.repository.UserRepository;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


// 依赖注入的示例
public class DependencyInjectionDemo {

    public static void main(String[] args) {
        // 配置XML 配置文件
        // 启动spring的应用上下文
        // 根据名称进行注入autowire byName 根据类型进行注入autowire byType 都是在xml的配置文件中配置好了
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:META-INF/dependency-injection-context.xml");

        UserRepository userRepository = (UserRepository) beanFactory.getBean("userRepository");
        System.out.println(userRepository.getUsers());

        // 注入容器内建的依赖, 非 Bean 对象 这儿是依赖注入,
        System.out.println(userRepository.getBeanFactory());
        System.out.println(beanFactory == userRepository.getBeanFactory());

        // 这行代码直接报错,这儿是依赖查找 这两段的代码可以知道依赖查找和依赖注入 的依赖的来源是不一样的
        //System.out.println(beanFactory.getBean(BeanFactory.class));

        // 延时注入 上面的都是实时注入
        ObjectFactory<User> userObjectFactory = userRepository.getUserObjectFactory();
        System.out.println(userObjectFactory.getObject());

        ObjectFactory<ApplicationContext> objectFactory = userRepository.getObjectFactory();
        System.out.println(objectFactory.getObject());
        System.out.println(objectFactory.getObject() == beanFactory);
    }

}

24.Spring IOC 依赖的来源

  • 自定义 Bean
  • 容器內建 Bean 对象
  • 容器內建依赖

我们借用一下前面的代码,具体的代码如下

package org.learn.spring.ioc.overview.dependency.injection;

import org.learn.spring.ioc.overview.domain.User;
import org.learn.spring.ioc.overview.repository.UserRepository;
import org.springframework.context.support.ClassPathXmlApplicationContext;

// 依赖来源的示例
public class DependencyInjectionDemo {

    public static void main(String[] args) {
        // 配置XML 配置文件
        // 启动spring的应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:META-INF/dependency-injection-context.xml");

        UserRepository userRepository = (UserRepository) beanFactory.getBean("userRepository");
      	// 来源一:自定义的Bean
        System.out.println(userRepository.getUsers());
        // 来源二:容器的内建依赖
        System.out.println(userRepository.getBeanFactory());
      
      	// 来源三:容器内建的Bean
        Environment environment = beanFactory.getBean(Environment.class);
        System.out.println(environment);

    }

}

25.Spring IOC 配置元信息

Bean 定义配置

  • 基于 XML 文件
  • 基于 Properties 文件
  • 基于 Java 注解
  • 基于 Java API( 后面讨论)

IoC 容器配置

  • 基于 XML 文件
  • 基于 Java 注解
  • 基于 Java API ( 后面讨论)

外部化属性配置

  • 基于 Java 注解

26.Spring IOC 容器

BeanFactory 和 ApplicationContext 谁才是 Spring IoC 容器?

ApplicationContext 除了 IoC 容器角色, 还有提供:

  • 面向切面( AOP)
  • 配置元信息( Configuration Metadata)
  • 资源管理( Resources)
  • 事件( Events)
  • 国际化( i18n)
  • 注解( Annotations)
  • Environment 抽象( Environment Abstraction)

BeanFactory 是 Spring 底层 IoC 容器 ApplicationContext 是具备应用特性的 BeanFactory 超集

再来看下如下的代码,简单的对比一下BeanFactory 和 ApplicationContext:

package org.learn.spring.ioc.overview.container;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.factory.xml.XmlReaderContext;

import java.util.Map;

// BeanFactory作为IOC容器示例
public class BeanFactoryAsIoCContainerDemo {

    public static void main(String[] args) {
        // 创建BeanFactory容器
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        // XML 配置文件的路径
        String location = "classpath:META-INF/dependency-lookup-context.xml";
        // 加载配置
        int beanDefinitionsCount = reader.loadBeanDefinitions(location);
        System.out.println("Bean 定义加载的数量:"+beanDefinitionsCount);

        // 依赖查找集合对象
        lookupCollectionByType(beanFactory);
    }

    private static void lookupCollectionByType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
            System.out.println("按照类型查找到的所有的 User 集合对象:" + users);
        }
    }
}
package org.learn.spring.ioc.overview.container;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

import java.util.Map;

// AnnotationApplicationContext作为IOC容器示例
public class AnnotationApplicationContextAsIoCContainerDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // 将当前类AnnotationApplicationContextAsIoCContainerDemo 作为配置类(configuration class)
        applicationContext.register(AnnotationApplicationContextAsIoCContainerDemo.class);
        // 启动应用上下文
        applicationContext.refresh();

        // 依赖查找集合对象
        lookupCollectionByType(applicationContext);
    }

    private static void lookupCollectionByType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
            System.out.println("按照类型查找到的所有的 User 集合对象:" + users);
        }
    }

    @Bean
    public User user(){
        User user = new User();
        user.setId(1L);
        user.setName("宵宫");
        return user;
    }
}

27.Spring IoC 容器生命周期(简单的了解下)

  • 启动
    • 准备此上下文以进行刷新
    • 告诉子类刷新内部 bean 工厂
    • 准备用于此上下文的 bean 工厂
    • 允许在上下文子类中对 bean 工厂进行后处理
    • 调用在上下文中注册为 beans 的工厂处理器
    • 注册拦截 bean 创建的 bean 处理器
    • 为此上下文初始化消息源
    • 为此上下文初始化事件多播器
    • 在特定上下文子类中初始化其他特殊 bean
    • 检查侦听器 bean 并注册它们
    • 实例化所有剩余的(非惰性初始化)单例
    • 最后一步:发布对应事件
  • 运行
  • 停止
    • 发布关机事件
    • 销毁上下文的 BeanFactory 中所有缓存的单例
    • 关闭此上下文本身的状态
    • 让子类做一些最后的清理…

28.定义 Spring Bean

什么是 BeanDefinition?

BeanDefinition 是 Spring Framework 中定义 Bean 的配置元信息接口, 包含:

  • Bean 的类名
  • Bean 行为配置元素, 如作用域、 自动绑定的模式, 生命周期回调等
  • 其他 Bean 引用, 又可称作合作者( collaborators) 或者依赖( dependencies)
  • 配置设置, 比如 Bean 属性( Properties)

29.BeanDefinition 元信息

在这里插入图片描述

BeanDefinition 构建

  • 通过 BeanDefinitionBuilder
  • 通过 AbstractBeanDefinition 以及派生类

具体的代码如下:

package org.learn.spring.bean.definition;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.GenericBeanDefinition;

// BeanDefinition构建的实例
public class BeanDefinitionCreationDemo {

    public static void main(String[] args) {
        // 1.通过 BeanDefinitionBuilder 进行构建
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        // 通过属性设置
        beanDefinitionBuilder.addPropertyValue("id", 1);
        beanDefinitionBuilder.addPropertyValue("name", "神里绫华");
        // 获取BeanDefinition实例
        BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        // BeanDefinition并非Bean的终态 可以自定义修改

        // 2.通过AbstractBeanDefinition 以及派生类
        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        // 设置Bean的类型
        genericBeanDefinition.setBeanClass(User.class);
        MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
        // 通过 MutablePropertyValues 批量操作添加
        // mutablePropertyValues.addPropertyValue("id",1);
        // mutablePropertyValues.addPropertyValue("name","神里绫人");
        // 链式的调用
        mutablePropertyValues.add("id", 2).add("name", "神里绫人");
        genericBeanDefinition.setPropertyValues(mutablePropertyValues);
    }
}

30.命名 Spring Bean

Bean 的名称

  • 每个 Bean 拥有一个或多个标识符( identifiers) , 这些标识符在 Bean 所在的容器必须是唯一的。 通常, 一个 Bean 仅有一个标识符, 如果需要额外的, 可考虑使用别名( Alias) 来扩充。
  • 在基于 XML 的配置元信息中, 开发人员可用 id 或者 name 属性来规定 Bean 的 标识符。 通常 Bean 的 标识符由字母组成, 允许出现特殊字符。 如果要想引入 Bean 的别名的话, 可在name 属性使用半角逗号( “ ,” ) 或分号( “ ;” ) 来间隔。
  • Bean 的 id 或 name 属性并非必须制定, 如果留空的话, 容器会为 Bean 自动生成一个唯一的名称。 Bean 的命名尽管没有限制, 不过官方建议采用驼峰的方式, 更符合 Java 的命名约定。

Bean 名称生成器( BeanNameGenerator)

  • 由 Spring Framework 2.0.3 引入, 框架內建两种实现:
    • DefaultBeanNameGenerator: 默认通用 BeanNameGenerator 实现
    • AnnotationBeanNameGenerator: 基于注解扫描的 BeanNameGenerator 实现, 起始于 SpringFramework 2.5, 关联的官方文档:With component scanning in the classpath, Spring generates bean names for unnamed components,following the rules described earlier: essentially, taking the simple class name and turning its initial character to lower-case. However, in the (unusual) special case when there is more than one character and both the first and second characters are upper case, the original casing gets preserved. These are the same rules as defined by java.beans.Introspector.decapitalize (which Spring uses here).(通过类路径中的组件扫描,Spring 为未命名的组件生成 bean 名称,遵循前面描述的规则:本质上,采用简单的类名并将其初始字符转换为小写。 但是,在(不寻常的)特殊情况下,当有多个字符并且第一个和第二个字符都是大写时,原始外壳将被保留。 这些规则与 java.beans.Introspector.decapitalize(Spring 在此处使用)定义的规则相同。)

具体的代码:

public static String generateBeanName(
			BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
			throws BeanDefinitionStoreException {

		String generatedBeanName = definition.getBeanClassName();
		if (generatedBeanName == null) {
			if (definition.getParentName() != null) {
				generatedBeanName = definition.getParentName() + "$child";
			}
			else if (definition.getFactoryBeanName() != null) {
				generatedBeanName = definition.getFactoryBeanName() + "$created";
			}
		}
		if (!StringUtils.hasText(generatedBeanName)) {
			throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
					"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
		}

		String id = generatedBeanName;
		if (isInnerBean) {
			// Inner bean: generate identity hashcode suffix.
			id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
		}
		else {
			// Top-level bean: use plain class name with unique suffix if necessary.
			return uniqueBeanName(generatedBeanName, registry);
		}
		return id;
	}
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		if (definition instanceof AnnotatedBeanDefinition) {
			String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
			if (StringUtils.hasText(beanName)) {
				// Explicit bean name found.
				return beanName;
			}
		}
		// Fallback: generate a unique default bean name.
		return buildDefaultBeanName(definition, registry);
	}

31.Spring Bean 的别名

Bean 别名( Alias) 的价值

  • 复用现有的 BeanDefinition

  • 更具有场景化的命名方法, 比如:

    <alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
    <alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
    

具体的代码如下:

package org.learn.spring.bean.definition;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

// bean别名示例
public class BeanAliasDemo {

    public static void main(String[] args) {
        // 配置XML 配置文件
        // 启动spring的应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:META-INF/bean-definitions-context.xml");

        // 通过别名 hutao-user 获取曾用名 user 的 bean
        User user =  beanFactory.getBean("user", User.class);
        User hutaoUser =  beanFactory.getBean("hutao-user", User.class);
        // true
        System.out.println("user 和 hutaoUser 是否相等:"+(user == hutaoUser));
    }
}

32.注册 Spring Bean

BeanDefinition 注册

  • XML 配置元信息
    • <bean name=” …” … />
  • Java 注解配置元信息
    • @Bean
    • @Component
    • @Import
  • Java API 配置元信息
    • 命名方式: BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
    • 非命名方式:
      • BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition,BeanDefinitionRegistry)
    • 配置类方式: AnnotatedBeanDefinitionReader#register(Class…)
  • 外部单例对象注册
    • Java API 配置元信息
      • SingletonBeanRegistry#registerSingleton

具体的代码如下:

package org.learn.spring.bean.definition;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Map;

// 注解的BeanDefinition 示例
@Import(AnnotationBeanDefinitionDemo.Config.class)// 3.通过@Import的方式来导入
public class AnnotationBeanDefinitionDemo {
    public static void main(String[] args) {
        // 创建BeanFactory的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册Configuration Class 配置类
        applicationContext.register(AnnotationBeanDefinitionDemo.class);
        // 1.通过@Bean的方式定义
        // 2.通过@Component的方式定义
        // 3.通过@Import的方式来导入

        // 通过 BeanDefinition API来实现
        // 1.通过命名 Bean 方式来注册
        registerUserBeanDefinition(applicationContext, "ganyu-user");
        // 2.通过非命名 Bean 方式来注册
        registerUserBeanDefinition(applicationContext);
        // 启动应用上下文
        applicationContext.refresh();

        // 按照类型依赖查找
        Map<String, Config> configBeans = applicationContext.getBeansOfType(Config.class);
        Map<String, User> userBeans = applicationContext.getBeansOfType(User.class);

        System.out.println("Config 类型的所有beans:" + configBeans);
        System.out.println("User 类型的所有beans:" + userBeans);

        // 显示的关闭spring应用上下文
        applicationContext.close();
    }

    public static void registerUserBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        beanDefinitionBuilder.addPropertyValue("id", 1).addPropertyValue("name", "甘雨");

        // 判断如果 beanName 参数存在时
        if (StringUtils.hasText(beanName)) {
            // 注册BeanDefinition
            // 命名 Bean 的注册方式
            registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
        } else {
            // 非命名 Bean 的注册方式
            BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(), registry);
        }


    }

    public static void registerUserBeanDefinition(BeanDefinitionRegistry registry) {
        registerUserBeanDefinition(registry, null);
    }

    @Component // 定义当前类作为Spring Bean(组件)2.通过@Component的方式定义
    public static class Config {
        // 1.通过@Bean的方式定义
        @Bean(name = {"user", "naxida-user"})
        public User user() {
            User user = new User();
            user.setId(1L);
            user.setName("纳西妲");
            return user;
        }
    }

}

33.实例化 Spring Bean

Bean 实例化( Instantiation)

  • 常规方式
    • 通过构造器( 配置元信息: XML、 Java 注解和 Java API )
    • 通过静态工厂方法( 配置元信息: XML 和 Java API )
    • 通过 Bean 工厂方法( 配置元信息: XML和 Java API )
    • 通过 FactoryBean( 配置元信息: XML、 Java 注解和 Java API )
  • 特殊方式
    • 通过 ServiceLoaderFactoryBean( 配置元信息: XML、 Java 注解和 Java API )
    • 通过 AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
    • 通过 BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)

具体的代码如下:

<?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  -->
    <bean id="user-by-static-method" class="org.learn.spring.ioc.overview.domain.User" factory-method="createUser"/>

    <bean id="userFactory" class="org.learn.spring.bean.factory.DefaultUserFactory"/>

    <!-- 实例(Bean)方法实例化 Bean  -->
    <bean id="user-by-instance-method" factory-bean="userFactory" factory-method="createUser"/>

    <!-- FactoryBean实例化 Bean  -->
    <bean id="user-by-factory-bean" class="org.learn.spring.bean.factory.UserFactoryBean"/>
</beans>
package org.learn.spring.ioc.overview.domain;

// 用户类
public class User {

    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public static User createUser(){
        User user = new User();
        user.setId(1L);
        user.setName("温蒂");
        return user;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

package org.learn.spring.bean.factory;

import org.learn.spring.ioc.overview.domain.User;

// User工厂类
public interface UserFactory {

    default User createUser(){
        return User.createUser();
    }
}

package org.learn.spring.bean.factory;

// 默认的UserFactory的实现
public class DefaultUserFactory implements UserFactory{
}

package org.learn.spring.bean.factory;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.FactoryBean;

// User Bean 的 FactoryBean的实现
public class UserFactoryBean implements FactoryBean<User> {

    @Override
    public User getObject() throws Exception {
        return User.createUser();
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}

package org.learn.spring.bean.definition;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

//Bean 实例化 示例
public class BeanInstantiationDemo {

    public static void main(String[] args) {
        // 配置XML 配置文件
        // 启动spring的应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:META-INF/bean-instantiation-context.xml");
        User userByStaticMethod = beanFactory.getBean("user-by-static-method", User.class);
        User userByInstanceMethod = beanFactory.getBean("user-by-instance-method", User.class);
        User userByFactoryBean = beanFactory.getBean("user-by-factory-bean", User.class);
        System.out.println(userByStaticMethod);
        System.out.println(userByInstanceMethod);
        System.out.println(userByInstanceMethod == userByStaticMethod);
        System.out.println(userByFactoryBean);
    }
}

下面可以通过我们的ServiceLoader的方式来实例化,先在resource文件下创建一个文件夹META-INF/services,然后在这个文件夹下创建一个文件,文件名为org.learn.spring.bean.factory.UserFactory该文件名是全类名,然后内容是org.learn.spring.bean.factory.DefaultUserFactory就是对应的实现。

<?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="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
        <property name="serviceType" value="org.learn.spring.bean.factory.UserFactory"/>
    </bean>
</beans>
package org.learn.spring.bean.definition;

import org.learn.spring.bean.factory.DefaultUserFactory;
import org.learn.spring.bean.factory.UserFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Iterator;
import java.util.ServiceLoader;

//特殊的Bean 实例化 示例
public class SpecialBeanInstantiationDemo {

    public static void main(String[] args) {
        // 配置XML 配置文件
        // 启动spring的应用上下文
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:META-INF/special-bean-instantiation-context.xml");
        // 通过ApplicationContext 获取 AutowireCapableBeanFactory
        AutowireCapableBeanFactory beanFactory =  applicationContext.getAutowireCapableBeanFactory();

        ServiceLoader<UserFactory> serviceLoader = beanFactory.getBean("userFactoryServiceLoader", ServiceLoader.class);
        displayServiceLoader(serviceLoader);

        // 创建DefaultUserFactory对象 通过AutowireCapableBeanFactory
        UserFactory userFactory = beanFactory.createBean(DefaultUserFactory.class);
        System.out.println(userFactory.createUser());
    }

    public static void demoServiceLoader() {
        ServiceLoader<UserFactory> serviceLoader = ServiceLoader.load(UserFactory.class, Thread.currentThread().getContextClassLoader());
        displayServiceLoader(serviceLoader);
    }

    public static void displayServiceLoader(ServiceLoader<UserFactory> serviceLoader){
        Iterator<UserFactory> iterator = serviceLoader.iterator();
        while (iterator.hasNext()){
            UserFactory userFactory = iterator.next();
            System.out.println(userFactory.createUser());
        }
    }
}

34.初始化 Spring Bean

Bean 初始化( Initialization)

  • @PostConstruct 标注方法
  • 实现 InitializingBean 接口的 afterPropertiesSet() 方法
  • 自定义初始化方法
    • XML 配置: <bean init-method=” init” … />
    • Java 注解: @Bean(initMethod=” init” )
    • Java API: AbstractBeanDefinition#setInitMethodName(String)

具体的代码如下:

package org.learn.spring.bean.factory;

import org.springframework.beans.factory.InitializingBean;

import javax.annotation.PostConstruct;

// 默认的UserFactory的实现
public class DefaultUserFactory implements UserFactory, InitializingBean {

    // 1. 通过 @PostConstruct 实现
    @PostConstruct
    public void init(){
        System.out.println("@PostConstruct: UserFactory 初始化中....");
    }

    public void initUserFactory(){
        System.out.println("自定义初始化方法initUserFactory(): UserFactory 初始化中....");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean#afterPropertiesSet(): UserFactory 初始化中....");
    }
}
package org.learn.spring.bean.definition;

import org.learn.spring.bean.factory.DefaultUserFactory;
import org.learn.spring.bean.factory.UserFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// Bean 初始化Demo
@Configuration //Configuration class
public class BeanInitializationDemo {
    public static void main(String[] args) {
        // 创建BeanFactory的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册Configuration Class 配置类
        applicationContext.register(BeanInitializationDemo.class);

        // 启动spring的应用上下文
        applicationContext.refresh();

        // 依赖查找 userFactory
        UserFactory userFactory = applicationContext.getBean(UserFactory.class);

        // 关闭spring的应用上下文
        applicationContext.close();
    }

    @Bean(initMethod = "initUserFactory")
    public UserFactory userFactory(){
        return new DefaultUserFactory();
    }
}

思考: 假设以上三种方式均在同一 Bean 中定义, 那么这些方法的执行顺序是怎样?运行的结果如下:

在这里插入图片描述

可以看出先是@PostConstruct,再是InitializingBean的实现的方法,最后才是我们的自定义的方法。

34.延迟初始化 Spring Bean

Bean 延迟初始化( Lazy Initialization)

  • XML 配置: <bean lazy-init=” true” … />
  • Java 注解: @Lazy(true)

思考: 当某个 Bean 定义为延迟初始化, 那么, Spring 容器返回的对象与非延迟的对象存在怎样的差异?

package org.learn.spring.bean.definition;

import org.learn.spring.bean.factory.DefaultUserFactory;
import org.learn.spring.bean.factory.UserFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

// Bean 初始化Demo
@Configuration //Configuration class
public class BeanInitializationDemo {
    public static void main(String[] args) {
        // 创建BeanFactory的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册Configuration Class 配置类
        applicationContext.register(BeanInitializationDemo.class);

        // 启动spring的应用上下文
        applicationContext.refresh();

        // 非延迟初始化在spring应用上下文启动完成后,被初始化
        System.out.println("spring应用上下文已启动....");
        // 依赖查找 userFactory
        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        System.out.println(userFactory);

        // 关闭spring的应用上下文
        applicationContext.close();
    }

    @Bean(initMethod = "initUserFactory")
    @Lazy
    public UserFactory userFactory(){
        return new DefaultUserFactory();
    }
}

运行的结果如下:

在这里插入图片描述

可以看出非延迟化的Bean,是在spring应用上下文启动后,需要使用的时候才会创建。

我们来修改一下代码,将UserFactory改成非lazy的。

package org.learn.spring.bean.definition;

import org.learn.spring.bean.factory.DefaultUserFactory;
import org.learn.spring.bean.factory.UserFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

// Bean 初始化Demo
@Configuration //Configuration class
public class BeanInitializationDemo {
    public static void main(String[] args) {
        // 创建BeanFactory的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册Configuration Class 配置类
        applicationContext.register(BeanInitializationDemo.class);

        // 启动spring的应用上下文
        applicationContext.refresh();

        // 非延迟初始化在spring应用上下文启动完成后,被初始化
        System.out.println("spring应用上下文已启动....");
        // 依赖查找 userFactory
        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        System.out.println(userFactory);

        // 关闭spring的应用上下文
        applicationContext.close();
    }

    @Bean(initMethod = "initUserFactory")
    //@Lazy
    public UserFactory userFactory(){
        return new DefaultUserFactory();
    }
}

运行结果如下:

在这里插入图片描述

可以看到非lazy的Bean在spring应用上下文启动完成前,这个Bean已经实例化完成了。

34.销毁 Spring Bean

Bean 销毁( Destroy)

  • @PreDestroy 标注方法
  • 实现 DisposableBean 接口的 destroy() 方法
  • 自定义销毁方法
    • XML 配置: <bean destroy=” destroy” … />
    • Java 注解: @Bean(destroy=” destroy” )
    • Java API: AbstractBeanDefinition#setDestroyMethodName(String)

现在看如下的代码:

package org.learn.spring.bean.factory;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

// 默认的UserFactory的实现
public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {

    // 1. 通过 @PostConstruct 实现
    @PostConstruct
    public void init(){
        System.out.println("@PostConstruct: UserFactory 初始化中....");
    }

    public void initUserFactory(){
        System.out.println("自定义初始化方法initUserFactory(): UserFactory 初始化中....");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean#afterPropertiesSet(): UserFactory 初始化中....");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("@PreDestroy: UserFactory 销毁中....");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean#destroy(): UserFactory 销毁中....");
    }

    public void doDestroy() throws Exception {
        System.out.println("自定义销毁方法doDestroy(): UserFactory 销毁中....");
    }
}
package org.learn.spring.bean.definition;

import org.learn.spring.bean.factory.DefaultUserFactory;
import org.learn.spring.bean.factory.UserFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

// Bean 初始化Demo
@Configuration //Configuration class
public class BeanInitializationDemo {
    public static void main(String[] args) {
        // 创建BeanFactory的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册Configuration Class 配置类
        applicationContext.register(BeanInitializationDemo.class);

        // 启动spring的应用上下文
        applicationContext.refresh();

        // 非延迟初始化在spring应用上下文启动完成后,被初始化
        System.out.println("spring应用上下文已启动....");
        // 依赖查找 userFactory
        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        System.out.println(userFactory);

        System.out.println("spring应用上下文准备关闭....");
        // 关闭spring的应用上下文
        applicationContext.close();
        System.out.println("spring应用上下文已关闭....");
    }

    @Bean(initMethod = "initUserFactory",destroyMethod = "doDestroy")
    //@Lazy
    public UserFactory userFactory(){
        return new DefaultUserFactory();
    }
}

思考: 假设以上三种方式均在同一 Bean 中定义, 那么这些方法的执行顺序是怎样?运行的结果如下:

在这里插入图片描述

可以看出先是@PreDestroy,再是DisposableBean的实现的方法,最后才是我们的自定义的方法。从上面的运行的结果我们可以发现是close()触发了这些方法。

35.垃圾回收 Spring Bean

Bean 垃圾回收( GC)

  1. 关闭 Spring 容器( 应用上下文)
  2. 执行 GC
  3. Spring Bean 覆盖的 finalize() 方法被回调

具体的代码如下:

package org.learn.spring.bean.factory;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

// 默认的UserFactory的实现
public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {

    // 1. 通过 @PostConstruct 实现
    @PostConstruct
    public void init(){
        System.out.println("@PostConstruct: UserFactory 初始化中....");
    }

    public void initUserFactory(){
        System.out.println("自定义初始化方法initUserFactory(): UserFactory 初始化中....");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean#afterPropertiesSet(): UserFactory 初始化中....");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("@PreDestroy: UserFactory 销毁中....");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean#destroy(): UserFactory 销毁中....");
    }

    public void doDestroy() throws Exception {
        System.out.println("自定义销毁方法doDestroy(): UserFactory 销毁中....");
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("当前 DefaultFactory 对象正在被垃圾回收...");
    }
}

package org.learn.spring.bean.definition;

import org.learn.spring.bean.factory.UserFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class BeanGarbageCollectionDemo {

    public static void main(String[] args) {
        // 创建BeanFactory的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册Configuration Class 配置类
        applicationContext.register(BeanInitializationDemo.class);

        // 启动spring的应用上下文
        applicationContext.refresh();

        // 关闭spring的应用上下文
        applicationContext.close();

        // 强制触发 GC
        System.gc();
    }
}

运行的结果如下:

在这里插入图片描述

这儿需要注意的是,虽然我们调用了 System.gc();方法,但是也不一定会强制触发GC的,我这儿多调用了几次,才触发了GC。

36.面试题精选

36.1 什么是 Spring Framework?

Spring makes it easy to create Java enterprise applications. It provides everything you need to embrace the Java language in an enterprise environment, with support for Groovy and Kotlin as alternative languages on the JVM, and with the flexibility to create many kinds of architectures depending on an application’ s needs.

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

36.2 Spring Framework 有哪些核心模块?

  • spring-core: Spring 基础 API 模块, 如资源管理, 泛型处理
  • spring-beans: Spring Bean 相关, 如依赖查找, 依赖注入
  • spring-aop : Spring AOP 处理, 如动态代理, AOP 字节码提升
  • spring-context : 事件驱动、 注解驱动, 模块驱动等
  • spring-expression: Spring 表达式语言模块

36.3 什么是 IoC ?

简单地说, IoC 是反转控制, 类似于好莱坞原则, 主要有依赖查找和依赖注入实现

36.4 依赖查找和依赖注入的区别?

依赖查找是主动或手动的依赖查找方式, 通常需要依赖容器或标准 API实现。 而依赖注入则是手动或自动依赖绑定的方式, 无需依赖特定的容器和API

36.5 Spring 作为 IoC 容器有什么优势?

  • 典型的 IoC 管理, 依赖查找和依赖注入
  • AOP 抽象
  • 事务抽象
  • 事件机制
  • SPI 扩展
  • 强大的第三方整合
  • 易测试性
  • 更好的面向对象

36.6 什么是 Spring IoC 容器?

Spring Framework implementation of the Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when itcreates the bean.

Spring Framework 实现了控制反转(IoC)的原理。 IoC 也称为依赖注入 (DI)。 在这个过程中,对象仅通过构造函数参数、工厂方法的参数或在对象实例被构造或从工厂方法返回后设置的属性来定义它们的依赖关系(即它们使用的其他对象) . 然后容器在创建 bean 时注入这些依赖项。

36.7 BeanFactory 与 FactoryBean?

  • BeanFactory 是 IoC 底层容器
  • FactoryBean 是 创建 Bean 的一种方式, 帮助实现复杂的初始化逻辑

36.8 Spring IoC 容器启动时做了哪些准备?

IoC 配置元信息读取和解析、 IoC 容器生命周期、 Spring 事件发布、国际化等。后续的章节会详细的讨论。

36.9 如何注册一个 Spring Bean?

通过 BeanDefinition 和外部单体对象来注册

36.10 什么是 Spring BeanDefinition?

定义 Spring Bean 和 BeanDefinition 元信息

36.11 Spring 容器是怎样管理注册 Bean

IoC 配置元信息读取和解析、 依赖查找和注入以及 Bean 生命周期等。后面的章节会继续讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/15053.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IC设计职位介绍|如何成为一名合格的数字前端设计工程师?

近年来IC行业火热&#xff0c;但因为一些原因&#xff0c;今年以来行业唱衰的人越来越多。尽管全球芯片市场过剩&#xff0c;但我国的半导体行业发展很可观&#xff0c;目前政策倾向国产芯片的发展&#xff0c;所以半导体人才非常稀缺。我国半导体产业终究要崛起&#xff0c;因…

js逆向基础篇-某电商网站-xx街

提示!本文章仅供学习交流,严禁用于任何商业和非法用途,如有侵权,可联系本文作者删除! 网站链接:aHR0cHM6Ly9tLm1vZ3UuY29tLw== 案例分析: 本文分析的参数为下图中圈出来的: 直接复制参数名mw-sign,然后Ctrl+Shift+F打开全局搜索,之后将参数名复制到搜索栏,然后回车…

深入讲解Linux上TCP的几个内核参数调优

生产内核&#xff08;production kernel&#xff09;&#xff1a;产品或者线上服务器当前运行的内核。 捕获内核&#xff08;capture kernel&#xff09;&#xff1a;系统崩溃时&#xff0c;使用kexec启动的内核&#xff0c;该内核用于捕获生产内核当前内存中的运行状态和数据…

收藏10000+,网络安全行业应该考哪些证?

证书是网安从业者知识水平能力的一个体现&#xff0c;考证同时也是拓展自身知识的一个方法。事实证明&#xff0c;有相关证书会让你的职业生涯锦上添花。 市面上的安全证书是非常多的&#xff0c;从大的角度来讲可以分为国际证书和国内证书两大类&#xff0c;国际证书简单来…

抖店订单发货回传的实际开发笔记

目录 前言 一、订单发货接口 二、商家对接接口步骤 1.业务逻辑分析 2.业务逻辑代码 总结 前言 主要是以前有对接过抖店开放平台&#xff0c;所以现在想要记录一下&#xff0c;做一个笔记&#xff0c;好好归纳一下&#xff0c;当时对接订单发货接口&#xff0c;是怎么实现的…

使用pip安装模块时,提示MemoryError

在使用pip安装依赖模块时&#xff0c;报错如下&#xff1a; 解决方法&#xff1a; 添加 --no-cache-dir参数 pip3 --no-cache-dir install -r *** 安装时遇到的其他问题&#xff1a; 设置python源命令&#xff1a; pip3 config set global.index-url https://pypi.tuna.tsing…

老K,硬核“锅”气

把一个有社会价值的事情&#xff0c;经过持续地思考和行动做成第一&#xff0c;并产生商业价值。之前&#xff0c;老K是这么做的&#xff1b; 现在“硬科技 &#xff0b; Food”就是老K新的选择与坚守。 作者|皮爷 出品|产业家 最近一年&#xff0c;一级市场交投清淡&…

SpringMVC学习篇(十一)

SpringMVC文件上传(普通版) 1 准备工作 1.1 导入文件上传需要的依赖 <dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.4</version> </dependency>1.2 在springM…

5G无线技术基础自学系列 | 物理下行共享信道

素材来源&#xff1a;《5G无线网络优化实践》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 NR PDSCH采用OFDM符号调制方式&#xff0…

构建有效和安全的远程工作模式

IDSA的一项研究发现&#xff0c;79%的企业遭受了与身份相关的违规行为。在 ManageEngine&#xff0c;我们了解身份对企业组织的整体安全状况的重要性。在这个身份安全周&#xff0c;我们准备了一系列文章来传播身份安全重要性以及哪些简单措施可以帮助提高企业组织对身份安全的…

【node进阶】深入浅出websocket即时通讯(一)

✅ 作者简介&#xff1a;一名普通本科大三的学生&#xff0c;致力于提高前端开发能力 ✨ 个人主页&#xff1a;前端小白在前进的主页 &#x1f525; 系列专栏 &#xff1a; node.js学习专栏 ⭐️ 个人社区 : 个人交流社区 &#x1f340; 学习格言: ☀️ 打不倒你的会使你更强&a…

游戏黑卡代充36技术及库存系统案例分析

黑卡充值常隐匿于「代充」服务中&#xff0c;且形式多变&#xff0c;从外币汇率差、退款到36漏洞、黑卡/盗刷信用卡充值&#xff0c;甚至还出现了专门的库存系统。 「36漏洞」是利用iOS小额支付漏洞实现的刷单套利业务。苹果为提高用户体验&#xff0c;在 APP Store 购买商品时…

【Hack The Box】linux练习-- Sunday

HTB 学习笔记 【Hack The Box】linux练习-- Sunday &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月17日&#x1f334; &#x1f3…

电容笔用什么品牌?电容笔10大品牌排行榜

尽管苹果公司的原装电容笔使用起来非常的流畅&#xff0c;非常的方便&#xff0c;但是它的价格却非常昂贵&#xff0c;这让很多学生党望而却步&#xff0c;还有这款电容笔的重量也不轻&#xff0c;长时间使用手会感觉疲劳。如果是来学习的&#xff0c;那就未必买如此贵的电容笔…

Java抽象类(abstract)

抽象类 当父类的某些方法&#xff0c;需要声明&#xff0c;但是又不确定如何实现时&#xff0c;可以将其声明为抽象方法&#xff0c;那么这个类就是抽象类 1)用abstract关键字来修饰一个类时,这个类就叫抽象类 访问修饰符 abstract 类名 {}2)用abstract关键宇来修饰一个方法时…

C. Division(分解质因数)

Problem - 1445C - Codeforces 奥列格最喜欢的科目是历史和数学&#xff0c;而他最喜欢的数学分支是除法。 为了提高他的除法技巧&#xff0c;奥列格想出了t对整数pi和qi&#xff0c;并决定为每对整数找到最大的整数xi&#xff0c;这样。 pi能被xi整除。 xi不能被qi整除。 奥…

基于NodeJs+Express+MySQL 实现的个人博客完整项目

目录 一、创建项目并初始化 项目结构 二、安装项目所需要的包 三、创建所需要的数据库表 表 user 用于存放账户密码 表 notepad 用于存放文章数据 表 leaving 用于存放留言板的数据 三、编写app.js文件 1、导入所有需要的包 2、创建web服务器 3、创建db文件夹&#…

智能制造APS,赋能「钣金行业」提升数字化战斗力!

导语&#xff1a; 钣金加工是典型的离散制造行业&#xff0c;具有品种多、批量小、交期短、质量要求高的发展趋势&#xff0c;这对钣金车间的生产管理提出了新的挑战。推进行业数字化、智能化改造&#xff0c;实现质效双提升&#xff0c;打破增长瓶颈&#xff0c;成为钣金行业…

Git常用命令与分支管理

Git常用的命令有以下6个&#xff1a;fetch/clone命令&#xff0c;add命令&#xff0c;commit命令&#xff0c;checkout命令&#xff0c;push命令&#xff0c;pull命令。 workspace&#xff1a;工作区staging area&#xff1a;暂存区loacl repository&#xff1a;本地仓库remot…

计算机毕业设计springboot+vue+elementUI会员制医疗预约服务管理信息系统

项目介绍 会员制医疗预约服务管理信息系统是针对会员制医疗预约服务管理方面必不可少的一个部分。在会员制医疗预约服务管理的整个过程中&#xff0c;会员制医疗预约服务管理系统担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类的管理系统也在不断改进 本系…