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

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 生命周期等。后面的章节会继续讨论。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值