Spring核心编程

1. Spring Framework总览

1.1 特性总览

1.1.1 核心特性

  • IoC 容器

  • Spring 事件

  • 资源管理

  • 国际化

  • 校验

  • 数据绑定

  • 类型转换

  • Spring 表达式

  • 面向切面编程

1.1.2 数据存储

  • JDBC

  • 事务抽象

  • DAO 支持

  • O/R 映射

  • XML 编列

1.1.3 Web 技术

  • Web Servlet 技术

    • Spring MVC

    • WebSocket

    • SockJS

  • Web Reactive 技术

    • Spring WebFlux

    • WebClient

    • WebSocket

1.1.4 技术整合

  • 远程调用

  • Java 消息服务(ActiveMQ)

  • Java 连接架构

  • Java 管理扩展

  • Java 邮件客户端

  • 本地任务

  • 本地调度

  • 缓存抽象

  • Spring 测试

1.1.5 测试

  • 模拟对象

  • TestContext 框架

  • Spring MVC 测试

  • Web 测试客户端

1.2 Spring 版本特性

Spring Framework 版本Java 标准版Java 企业版特性
1.x1.3+J2EE 1.3+1.3 引入接口动态代理
2.x1.4.2+J2EE 1.3+
3.x5+J2EE 1.4 和 Java EE 5Java 5 引入注解
4.x6+Java EE 6 和 7
5.x8+Java EE 7

1.3 Spring 模块化设计

  • Spring-core

  • Spring-aop

  • Spring-aspects

  • Spring-context

  • Spring-context-indexer

  • Spring-context-support

  • Spring-expression

  • Spring-instrument(Java agent支持)

  • Spring-jcl(日志框架)

  • Spring-jdbc

  • Spring-jms

  • Spring-messaging

  • Spring-orm

  • Spring-oxm(xml编列)

  • Spring-test

  • Spring-tx(事务)

  • Spring-web

  • Spring-webflux

  • Spring-webmvc

  • Spring-websocket

1.4 Java 语言特性

Java 5 语法特性 

语法特性Spring 支持版本代表实现
注解1.2+@Transaction
枚举1.2+Propagation(事件传播)
for-each 语法3.0+(3.0 开始强制要求 Java 5)AbstractApplicationContext
自动装箱3.0+
泛型3.0+ApplicationListener

Java 6 语法支持

语法特性Spring 支持版本代表实现
接口 @Override4.0+

Java 7 语法特性

语法特性Spring 支持版本代表实现
Diamond 语法5.0+DefaultListableBeanFactory
try-with-resource 语法5.0+ResourceBundleMessageSource

Java 8 语法特性

语法特性Spring 支持版本代表实现
Lambda 语法5.0+PropertyEditorRegistrySupport

1.5 面试题

1.5.1 什么是 Spring Framework

Spring 是使得创建企业级 Java 应用变得简单的框架,框架核心是 Ioc 和 AOP,当然还支持包括事件、国际化、资源管理、校验、数据绑定以及类型转换等功能,提供你开发企业级应用所需的一切功能

1.5.2 Spring Framework 有哪些核心模块

  • spring-core:Spring 基础 API 模块,如资源管理,泛型处理

  • spring-beans:Spring Bean 相关,如依赖查找,依赖注入

  • spring-aop:Apring AOP 处理,如动态代理,AOP字节码提升

  • spring-context:事件驱动、注解驱动、模块驱动

  • spring-expression:Spring 表达式语言模块

1.5.3 Spring Framework 的优势和不足是什么

2. 重新认识 IOC

2.1 IoC 发展简介

2.2 IoC 主要实现策略

  • 依赖查找

  • 依赖注入

2.3 IoC 容器的职责

  • 依赖处理

    • 依赖查找

    • 依赖注入

  • 生命周期管理

    • 容器

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

  • 配置

    • 容器

    • 外部化配置

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

2.4 IoC 容器的实现

  • Java SE

    • Java Beans

    • Java ServiceLoader SPI

    • JNDI(Java Naming and Directory Interface)

  • Java EE

    • EJB(Enterprise Java Beans)

    • Servlet

  • 开源

    • Apache Avalon

    • PicoContainer

    • Google Guice

    • Spring Framework

2.5 传统 IoC 容器实现

Java Beans 作为 IoC 容器,特性如下:

  • 依赖查找

  • 生命周期管理

  • 配置元信息

  • 事件

  • 自定义

  • 资源管理

  • 持久化

规范:

2.6 轻量级 IoC 容器

2.7 依赖查找 VS. 依赖注入

类型依赖处理实现便利性代码侵入性API 依赖性可读性
依赖查找主动获取相对繁琐侵入业务代码依赖容器 API良好
依赖注入被动提取相对便利低侵入性不依赖容器 API一般

2.8 构造器注入 VS. Setter 注入

Spring 团队推荐使用构造器注入,对象是不可变的确保依赖对象不是空的,缺点是参数过多,代码看起来不是很良好,需要适当进行重构

Setter 注入应该仅用于可选对象的注入

2.9 面试题精选

2.9.1 什么是 IoC

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

2.9.2 依赖查找和依赖注入的区别

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

2.9.3 Spring 作为 IoC 容器有什么优势

  • 典型的 IoC 管理,依赖查找和依赖注入

  • AOP 抽象

  • 事务抽象

  • 事件机制

  • SPI 扩展

  • 强大的第三方整合

  • 易测试性

  • 更好的面向对象

3. Spring IOC容器概述

3.1 依赖查找

  • 根据 Bean 名称查找

    • 实时查找(getBean("xxx"))

    • 延迟查找(ObjectFactory)

  • 根据 Bean 类型查找

    • 单个 Bean 对象(getBean(xxx.class))

    • 集合 Bean 对象(getBeansByType(xxx.class))

  • 根据 Bean 名称 + 类型查找(getBean("xxx", xxx.class))

  • 根据 Java 注解查找(getBeansWithAnnotation(xxx.class))

    • 单个 Bean 对象

    • 集合 Bean对象

3.2 依赖注入

  • 根据 Bean 名称注入(ref="xxx"或者autowire="byName")

  • 根据 Bean 类型注入

    • 单个 Bean 对象

    • 集合 Bean 对象(util:list、util:set等,autowire="byType")

  • 注入容器内建 Bean 对象(Environment等)

  • 注入非 Bean 对象(BeanFactory、ApplicationContext等)

  • 注入类型

    • 实时注入

    • 延迟注入(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">
​
        <!-- 集合注入,通过类型注入 -->
    <bean id="userRepository" class="xxx.xxx.xxx.UserRepository" autowire="byType"/>
    <!-- 
<property name="users">
<util:list>
<ref bean="superUser"/>
<ref bean="user"/>
</util:list>
</property>
-->
​
</beans>

3.3 依赖来源

  • 自定义 Bean(XML、注解等)

  • 容器内建 Bean 对象(environment对象等,通过手动注册BeanDefinition)

  • 容器内建依赖(BeanFactory等,直接添加到BeanFactory中)

3.4 配置元信息

  • Bean 定义配置(BeanDefinition)

    • 基于 XML 文件

    • 基于 Properties 文件

    • 基于 Java 注解

    • 基于 Java API

  • IoC 容器配置

    • 基于 XML 文件

    • 基于 Java 注解

    • 基于 Java API

  • 外部化配置(@Value)

    • 基于 Java 注解

3.5 BeanFactory和ApplicationContext

ApplicationContext 是 BeanFactory 子接口,功能上是 BeanFactory 的超集,并且在AbstractRefreshableApplicationContext 中通过组合的方式将 BeanFactory 组合进来,通过代理的方式将 BeanFactory 重写的方法指定给内部组合的 BeanFactory 来执行,比如在 AbstractApplicationContext 中 getBean() 方法都是这样实现的:

//---------------------------------------------------------------------
// Implementation of BeanFactory interface
//---------------------------------------------------------------------
​
@Override
public Object getBean(String name) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name);
}
​
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name, requiredType);
}
​
@Override
public Object getBean(String name, Object... args) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name, args);
}
​
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(requiredType);
}
​
@Override
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(requiredType, args);
}
​
@Override
public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) {
    assertBeanFactoryActive();
    return getBeanFactory().getBeanProvider(requiredType);
}
​
@Override
public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {
    assertBeanFactoryActive();
    return getBeanFactory().getBeanProvider(requiredType);
}
​
@Override
public boolean containsBean(String name) {
    return getBeanFactory().containsBean(name);
}
​
@Override
public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
    assertBeanFactoryActive();
    return getBeanFactory().isSingleton(name);
}
​
@Override
public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
    assertBeanFactoryActive();
    return getBeanFactory().isPrototype(name);
}
​
@Override
public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
    assertBeanFactoryActive();
    return getBeanFactory().isTypeMatch(name, typeToMatch);
}
​
@Override
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
    assertBeanFactoryActive();
    return getBeanFactory().isTypeMatch(name, typeToMatch);
}
​
@Override
@Nullable
public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
    assertBeanFactoryActive();
    return getBeanFactory().getType(name);
}
​
@Override
@Nullable
public Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException {
    assertBeanFactoryActive();
    return getBeanFactory().getType(name, allowFactoryBeanInit);
}
​
@Override
public String[] getAliases(String name) {
    return getBeanFactory().getAliases(name);
}

即 BeanFactory 是 Spring 最基本的容器,ApplicationContext 拥有 BeanFactory 所有功能,是 BeanFactory 的超集,同时还有许多自身特有的功能,功能在下一小节叙述。

3.6 ApplicationContext作用

ApplicationContext 除了提供 IoC 容器角色,还提供以下功能:

  • 面向切面(AOP)

  • 配置元信息(Configuration Metadata)

  • 资源管理(Resources)

  • 事件(Event)

  • 国际化(i18n)

  • 注解(Annotation)

  • Environment 抽象(Environment Abstraction)

3.7 BeanFactory 和 ApplicationContext选择

  • BeanFactory 是 Spring 的底层 IoC 容器

  • ApplicationContext 是具备应用特性的 BeanFactory 超集

3.7.1 BeanFactory使用

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
int beanDefinitionCount = reader.loadBeanDefinitions("classpath:spring.xml");
// 接下来就能使用factory了

3.7.2 ApplicationContext使用

@Configuration
public class AnnotationContextAsIoCContainerDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册配置类
        context.register(AnnotationContextAsIoCContainerDemo.class);
        context.refresh();
        // 接下来就能使用context了
    }
    
    @Bean
    public User user() {
        return new User("foo", 18);
    }
}

3.8 IOC 容器启停过程

3.8.1 启动

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
​
        // Prepare this context for refreshing.
        // 记录启动时间,初始化PropertiySources,校验Environment对象等
        prepareRefresh();
​
        // Tell the subclass to refresh the internal bean factory.
        // 创建BeanFactory -> DefaultListableBenaFactory,在AbstractRefreshableApplicationContext中创建,此处只完成实例化,还未完成初始化
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
​
        // Prepare the bean factory for use in this context.
        // 添加一些内建的bean(environment、systemProperties等)以及依赖(BeanFactory、ResourceLoader等)
        prepareBeanFactory(beanFactory);
​
        try {
            // Allows post-processing of the bean factory in context subclasses.
            // 注册针对容器的扩展点(对容器进行调整)
            postProcessBeanFactory(beanFactory);
​
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // Invoke factory processors registered as beans in the context.
            // 调用针对容器的扩展点
            invokeBeanFactoryPostProcessors(beanFactory);
​
            // Register bean processors that intercept bean creation.
            // 注册针对bean的扩展点(对bean进行调整)
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();
​
            // Initialize message source for this context.
            // 初始化国际化信息
            initMessageSource();
​
            // Initialize event multicaster for this context.
            // 初始化事件发布
            initApplicationEventMulticaster();
​
            // Initialize other special beans in specific context subclasses.
            onRefresh();
​
            // Check for listener beans and register them.
            // 注册监听
            registerListeners();
​
            // Instantiate all remaining (non-lazy-init) singletons.
            // 实例化所有单例对象
            finishBeanFactoryInitialization(beanFactory);
​
            // Last step: publish corresponding event.
            // 发布事件
            finishRefresh();
        }
​
        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }
​
            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();
​
            // Reset 'active' flag.
            cancelRefresh(ex);
​
            // Propagate exception to caller.
            throw ex;
        }
​
        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}

3.8.3 停止

public void close() {
    synchronized (this.startupShutdownMonitor) {
        // 关闭
        doClose();
        // If we registered a JVM shutdown hook, we don't need it anymore now:
        // We've already explicitly closed the context.
        if (this.shutdownHook != null) {
            try {
                // 移除shutdownhook
                Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
            }
            catch (IllegalStateException ex) {
                // ignore - VM is already shutting down
            }
        }
    }
}
​
protected void doClose() {
    // Check whether an actual close attempt is necessary...
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
        if (logger.isDebugEnabled()) {
            logger.debug("Closing " + this);
        }
​
        if (!NativeDetector.inNativeImage()) {
            LiveBeansView.unregisterApplicationContext(this);
        }
​
        try {
            // Publish shutdown event.
            publishEvent(new ContextClosedEvent(this));
        }
        catch (Throwable ex) {
            logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
        }
​
        // Stop all Lifecycle beans, to avoid delays during individual destruction.
        if (this.lifecycleProcessor != null) {
            try {
                this.lifecycleProcessor.onClose();
            }
            catch (Throwable ex) {
                logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
            }
        }
​
        // Destroy all cached singletons in the context's BeanFactory.
        // 销毁所有Bean
        destroyBeans();
​
        // Close the state of this context itself.
        // 关闭所有BeanFactory
        closeBeanFactory();
​
        // Let subclasses do some final clean-up if they wish...
        onClose();
​
        // Reset local application listeners to pre-refresh state.
        if (this.earlyApplicationListeners != null) {
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }
​
        // Switch to inactive.
        this.active.set(false);
    }
}

3.9 面试题

3.9.1 什么是 Spring IOC 容器

依赖注入依赖查找以及一些其他特性共同构成了 Spring IOC 容器,官方答案如下:

Spring 框架实现 IoC 原则,IoC 也称之为 DI(依赖注入,此处官方文档有点不太严谨,因为 IoC 不只是依赖注入,还有依赖查找),这个过程会伴随状态的依赖,此处主要指通过构造器参数、工厂方法或者是属性的setter方法去注入一些其他的对象,从而实现依赖注入,容器会将依赖注入的信息放到创建的 Bean 中来。Spring 框架是一个 IoC 容器的实现,DI 是它的实现原则

官方文档中 IoC 不提以来查找的原因是由于依赖查找在 Java EE时 代已经实现了,DI 部分实现,只不过要和 Java EE 传统容器加以区别

3.9.2 BeanFactory 和 FactoryBean 区别

BeanFactory 是 IoC 的底层容器,ApplicationContext 中通过继承 BeanFactory 接口并且在实现类中通过组合 BeanFactory 对象方式提供 BeanFactory 所有功能以及新增自己的一些功能(代理)

FactoryBean 则是创建 Bean 的一种方式,帮助实现复杂的初始化逻辑,通过 FactoryBean 创建的 Bean 是否有会包含 Bean 生命周期?后续进行讲解。

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

IoC 配置元信息读取和解析(xml、注解等读取和解析)、IoC 容器生命周期、Spring 事件发布、国际化等

4. Spring Bean 基础

4.1 什么是 BeanDefinition

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

  • Bean 类名(全限定名)

  • Bean 行为配置元素(作用域、自动绑定模式、生命周期)

  • 其他 Bean 引用,依赖

  • 配置设置,属性

4.2 BeanDefinition 元信息

4.2.1 元信息说明

属性(Property)说明
ClassBean 全类名,必须是具体类,不能用抽象类或接口
NameBean 的名称或者 ID
ScopeBean 作用域(Singleton、prototype)
Constructor argumentsBean 构造器参数(依赖注入)
PropertiesBean 属性设置(依赖注入)
AutoWiring modeBean 自动绑定模式(byType、byName等)
Lazy initialization modeBean延迟初始化模式(延迟或者非延迟)
Initialization methodBean 初始化回调方法
Destruction methodBean 销毁回调方法名称

4.2.2 BeanDefinition构建

  • 通过 BeanDefinitionBuilder

  • 通过AbstractBeanDefinition 以及派生类

// 通过 BeanDefinitionBuilder 创建 BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
AbstractBeanDefinition beanDefinition = builder.addPropertyValue("name", "foo")
    .addPropertyValue("age", 28).getBeanDefinition();
beanDefinition.addQualifier(new AutowireCandidateQualifier("user"));
beanDefinition.setScope("singleton");
​
// 通过 AbstractBeanDefinition 以及派生类创建 BeanDefinition
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(User.class);
MutablePropertyValues values = new MutablePropertyValues();
values.add("name", "foo")
    .add("age", 29);
genericBeanDefinition.setPropertyValues(values);

4.3 id 和 name

4.3.1 Bean 标识符

每个 Bean 拥有一个或者多个标识符(identifier),这些标识符在 Bean 所在的容器必须是唯一的。通常发,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名扩充,依赖注入以及依赖查找时可以使用

在基于 XML 的配置元信息中,开发人员可用 id 或者 name 属性来规定 Bean 的标识符。通畅 Bean 的标识符由字母组成,允许出现特殊字符,如果想要引入 Bean 的别名,可在 name 属性使用半角逗号或分号来间隔

Bean 的 id 或 name 属性并非必须指定,如果留空的话,容器会为 Bean 自动生成一个唯一的名称。Bean 的命名景观没有限制,不过官方建议采用驼峰的方式,更符合 Java 的命名约定。

4.3.2 Bean 名称生成器(BeanNameGenerator)

由 Spring Framework 2.0.3 引入,框架内建两种实现:

  • DefaultBeanNameGenerator:默认通用 BeanNameGenerator 实现

  • AnnotationBeanNameGenerator:基于注解扫描的 BeanNameGenerator 实现,起始于 Spring Framework 2.5

4.3.3 id 和 name

id 和 name 都是 bean 的标识符,id 最多一个,name 可有多个

4.4 别名

别名的价值:

  • 复用现有的 BeanDefinition

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

    • <alias name="myApp-dataSource" alias="subsystemA-dataSource">

    • <alias name="myApp-dataSource" alias="subsystemB-dataSource">

4.5 注册 Bean

4.5.1 注册方式

  • XML 配置元信息

    • <bean id="", class="">

  • Java 注解配置元信息

    • @Bean

    • @Component

    • @Import

  • Java API 配置元信息

    • 命名方式:BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)

    • 非命名方式:BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition, BeanDefinitionRegistry)

    • 配置类方式:AnnotatedBeanDefinitionReader#register(Class...)(就是注解注册中通过context.register("config", Config.class)方法进行注册)

4.5.2 注解注册

@Import(AnnotationBeanDefinitionDemo.Config.class)
public class AnnotationBeanDefinitionDemo {
    public static void main(String[] args) {
        // 创建context
        try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
            // 注册配置类
            context.register(AnnotationBeanDefinitionDemo.class);
            context.registerBean("config", Config.class);
            // 刷新容器(启动容器)
            context.refresh();
            Config config = context.getBean(Config.class);
            User user = context.getBean(User.class);
            System.out.println(user);
            System.out.println(config);
        }
    }
​
    static class Config {
        @Bean
        public User user() {
            User user = new User();
            user.setAge(28);
            user.setName("foo");
            return user;
        }
    }
}

由上面可以得出一个结论,Spring 不会进行重复注册,手动 registerBean 以及使用 @Import 注解同时注入了 Config 对象,但是不会报错,并且成功注入

4.5.3 Java API 方式注入

@Import(AnnotationBeanDefinitionDemo.Config.class)
public class AnnotationBeanDefinitionDemo {
    public static void main(String[] args) {
        try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
            context.register(AnnotationBeanDefinitionDemo.class);
            context.registerBean("config", Config.class);
            // 如果此处名字和下面@Bean中名字相同,会覆盖
            registerUserBeanDefinition(context, "newUser");
            registerUserBeanDefinition(context);
            context.refresh();
​
            System.out.println("Config 类型所有 Beans:" + context.getBeansOfType(Config.class));
            System.out.println("User 类型所有 Beans:" + context.getBeansOfType(User.class));
        }
    }
​
    public static void registerUserBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
        AbstractBeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(User.class)
                .addPropertyValue("age", 18)
                .addPropertyValue("name", "fooNew").getBeanDefinition();
        if (StringUtils.hasText(beanName)) {
            // 命名 Bean 注册方法
            registry.registerBeanDefinition(beanName, definition);
        } else {
            // 非命名 Bean 注册方法
            BeanDefinitionReaderUtils.registerWithGeneratedName(definition, registry);
        }
    }
​
    public static void registerUserBeanDefinition(BeanDefinitionRegistry registry) {
        registerUserBeanDefinition(registry, null);
    }
​
    @Component
    static class Config {
        @Bean
        public User user() {
            User user = new User();
            user.setAge(28);
            user.setName("foo");
            return user;
        }
    }
}
​
// 运行结果:
Config 类型所有 Beans:{config=com.gcl.bean.AnnotationBeanDefinitionDemo$Config@327b636c}
User 类型所有 Beans:{newUser=User{name='fooNew', age=18}, com.gcl.bean.User#0=User{name='fooNew', age=18}}

4.6 实例化 Bean

4.6.1 方法

  • 常规方式

    • 通过构造器(配置元信息: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.String, int boolean)

    • 通过 BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)

4.6.2 常规方式

public class BeanInstantiationDemo {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean-create.xml");
        System.out.println(context.getBean("user", User.class));
        System.out.println(context.getBean("factoryUser"));
        System.out.println(context.getBean("userByFactoryBean"));
        System.out.println(context.getBean("user") == context.getBean("factoryUser"));
    }
}
​
public class User {
    private String name;
    private int age;
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
​
    // 静态工厂方法创建bean
    public static User user() {
        User user = new User();
        user.setName("static");
        user.setAge(22);
        return user;
    }
}
​
// 抽象工厂创建bean
public interface UserFactory {
    User createUser();
}
​
public class DefaultUserFactory implements UserFactory {
    @Override
    public User createUser() {
        return User.user();
    }
}
​
//FactoryBean创建bean
public class UserFactoryBean implements FactoryBean<User> {
​
    private static final User USER = User.user();
​
    @Override
    public User getObject() throws Exception {
        return USER;
    }
​
    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}
<?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" class="com.gcl.bean.User" factory-method="user"/>
​
    <!-- 工厂方法实例化 Bean -->
    <bean id="userFactory" class="com.gcl.bean.factory.DefaultUserFactory"/>
    <bean id="factoryUser" factory-bean="userFactory" factory-method="createUser"/>
​
    <!-- FactoryBean 实例化 Bean -->
    <bean id="userByFactoryBean" class="com.gcl.bean.factory.UserFactoryBean"/>
​
</beans>

4.6.3 非常规方式

代码

public class SpecialBeanInstantiationDemo {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:special-bean-create.xml");
​
        // 通过 applicationContext 可以获取到 AutowireCapableBeanFactory 对象
        AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();
​
        // 获取到的是 FactoryBean#getObject 方法返回的对象
        ServiceLoader<UserFactory> loader = context.getBean("userFactoryServiceLoader", ServiceLoader.class);
        displayServiceLoader(loader);
​
        // 通过 AutowireCapableBeanFactory 对象实例化 Bean,通过该方法创建的 Bean 可以享受 Bean生命周期的所有阶段
        UserFactory factory = beanFactory.createBean(DefaultUserFactory.class);
        System.out.println(factory.createUser());
    }
​
    private static void displayServiceLoader(ServiceLoader<UserFactory> loader) {
        Iterator<UserFactory> iterator = loader.iterator();
        while (iterator.hasNext()) {
            UserFactory factory = iterator.next();
            System.out.println(factory.createUser());
        }
    }
}

SPI 文件

com.gcl.bean.factory.DefaultUserFactory

XML 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <!-- ServiceLoaderFactoryBean 对象,获取时,其实真正获取到的是 ServiceLoader 对象,因为 FactoryBean 对应的 Bean 是 getObject 方法返回的对象 -->
    <bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
        <!-- ServiceLoader<UserFactory> 配置 ServiceLoader 对象泛型类型 -->
        <property name="serviceType" value="com.gcl.bean.factory.UserFactory"/>
    </bean>
​
</beans>

4.7 初始化 Bean

  • @PostConstruct 标注方法

  • 实现 InitializingBean 接口的 afterPropertiesSet 方法

  • 自定义初始化方法

    • XML 配置: <bean init-method="init" .../>

    • Java 注解:@Bean(initMethod="init")

    • Java API:AbstractBeanDefinition#setInitMethodName(String)(其实上述两个自定义方法底层就是调用的setInitMethodName 方法)

上述三种方式执行顺序?@PostConstructor > InitializingBean > 自定义初始化方法。

public class DefaultUserFactory implements UserFactory, InitializingBean {
​
    @Override
    public User createUser() {
        return User.user();
    }
​
    @PostConstruct
    public void init() {
        System.out.println("@PostConstructor : UserFactory 初始化中...");
    }
​
    public void initUserFactory() {
        System.out.println("@自定义初始化方法  : UserFactory 初始化中...");
    }
​
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean#afterPropertiesSet  初始化方法  : UserFactory 初始化中...");
    }
}
​
@Configuration
public class BeanInitializationDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
​
        // 注册配置类
        context.register(BeanInitializationDemo.class);
​
        // 启动容器
        context.refresh();
​
        // 依赖查找
        UserFactory factory = context.getBean(UserFactory.class);
        System.out.println(factory.createUser());
​
        // 关闭容器
        context.close();
    }
​
    @Bean(initMethod = "initUserFactory")
    public UserFactory factory() {
        return new DefaultUserFactory();
    }
}
​
// 运行结果为:
@PostConstructor : UserFactory 初始化中...
InitializingBean#afterPropertiesSet  初始化方法  : UserFactory 初始化中...
@自定义初始化方法  : UserFactory 初始化中...
User{name='static', age=22}

由结果可知三种实例化方式的优先级为:

@PostConstructor > InitializingBean > 自定义初始化方法(init-mehtod、initMethod)

4.8 延迟初始化 Bean

  • XML 配置:<bean lazy-init="true" .../>

  • Java 注解:@Lazy(true)

以上一小节例子为例:

public class BeanInitializationDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
​
        // 注册配置类
        context.register(BeanInitializationDemo.class);
​
        // 启动容器
        context.refresh();
        System.out.println("Spring 应用上下文以启动...");
​
        // 依赖查找
        UserFactory factory = context.getBean(UserFactory.class);
        System.out.println(factory.createUser());
​
        // 关闭容器
        context.close();
    }
​
    @Bean(initMethod = "initUserFactory")
    @Lazy
    public UserFactory factory() {
        return new DefaultUserFactory();
    }
}
​
// 如果不加@Lazy注解,“Spring 应用上下文以启动...”这句话后于初始化方法输出,如下:
@PostConstructor : UserFactory 初始化中...
InitializingBean#afterPropertiesSet  初始化方法  : UserFactory 初始化中...
@自定义初始化方法  : UserFactory 初始化中...
Spring 应用上下文以启动...
User{name='static', age=22}
// 如果加上 @Lazy 注解,输出结果如下:
Spring 应用上下文以启动...
@PostConstructor : UserFactory 初始化中...
InitializingBean#afterPropertiesSet  初始化方法  : UserFactory 初始化中...
@自定义初始化方法  : UserFactory 初始化中...
User{name='static', age=22}

可以看到延迟初始化 Bean 在容器启动时并未进行初始化,只进行了实例化,只有在真正使用时,才会触发初始化逻辑,非延迟初始化则是在容器启动时就进行初始化操作。

4.9 销毁 Bean

  • @PreDestroy 标注方法

  • 实现 DisposableBean 接口的 destroy 方法

  • 自定义销毁方法

    • XML 配置:<bean destroy="destroy" .../>

    • Java 注解:@Bean(destroy="destroy")

    • Java API:AbstractBeanDefinition#setDestroyMethodName(String)(上述两种方法底层就是调用的 setDestroyMethodName 方法)

public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {
​
    @Override
    public User createUser() {
        return User.user();
    }
​
    @PostConstruct
    public void init() {
        System.out.println("@PostConstructor : UserFactory 初始化中...");
    }
​
    @PreDestroy
    public void preDestroy() {
        System.out.println("@PreDestroy : UserFactory 销毁后...");
    }
​
    public void initUserFactory() {
        System.out.println("自定义初始化方法  : UserFactory 初始化中...");
    }
​
    public void destroyUserFactory() {
        System.out.println("自定义销毁方法 : UserFactory 销毁后...");
    }
​
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean#afterPropertiesSet 初始化方法  : UserFactory 初始化中...");
    }
​
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean#destroy 销毁方法 : UserFactory 销毁后...");
    }
}
​
@Configuration
public class BeanInitializationDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
​
        // 注册配置类
        context.register(BeanInitializationDemo.class);
​
        // 启动容器
        context.refresh();
        System.out.println("Spring 应用上下文以启动...");
​
        // 依赖查找
        UserFactory factory = context.getBean(UserFactory.class);
        System.out.println(factory.createUser());
​
        // 关闭容器
        context.close();
    }
​
    @Bean(initMethod = "initUserFactory", destroyMethod = "destroyUserFactory")
    @Lazy
    public UserFactory factory() {
        return new DefaultUserFactory();
    }
}
​
// 输出结果为:
Spring 应用上下文以启动...
@PostConstructor : UserFactory 初始化中...
InitializingBean#afterPropertiesSet 初始化方法  : UserFactory 初始化中...
自定义初始化方法  : UserFactory 初始化中...
User{name='static', age=22}
@PreDestroy : UserFactory 销毁后...
DisposableBean#destroy 销毁方法 : UserFactory 销毁后...
自定义销毁方法 : UserFactory 销毁后...

销毁 Bean 优先级:@PreDestroy > DisposableBean > 自定义销毁方法(destroy-method、destroyMethod)

4.10 回收 Bean

  1. 关闭 Spring 容器(应用上下文)

  2. 执行 GC

  3. Speing Bean 覆盖的 finalize() 方法

也就是说在 Spring 关闭以后,当发生 GC 时会回收容器中的 Bean,回收方法按照 GC 方法来进行,可达性分析以及 finalize 自救

4.11 面试题

4.11.1 如何注册 Bean

通过 BeanDefinition (XML、注解@Bean,@Component等、Java API方式(BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)等))和外部单体对象来注册(外部单体对象生命周期并不由 Spring 来管理,但是也可以被容器托管),如下:

public class SingletonBeanRegistrationDemo {
    public static void main(String[] args) {
        // 创建 Spring 容器(上下文)
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 创建外部单体对象
        DefaultUserFactory factory = new DefaultUserFactory();
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 注册外部单体对象
        beanFactory.registerSingleton("userFactory", factory);
        context.refresh();
        // 依赖查找
        UserFactory factory1 = context.getBean(UserFactory.class);
        System.out.println(factory == factory1);
    }
}
​
// 打印结果:true

4.11.2 什么是 Spring BeanDifinition

BeanDefinition 实际上是关于 Bean 定义的元数据信息,包括scope、lazy、init-method、destroy-method、beanName、primary、role、autowire等

4.11.3 Spring 容器是如何注册 Bean 的

IOC 配置元信息读取和解析、依赖查找和注入以及 Bean 生命周期等(后续详解)

5. 依赖查找

5.1 依赖查找前世今生

  • 单一类型依赖查找

    • JNDI ——javax.naming.Context#lookup(javax.naming.Name)

    • JavaBeans—— java.beans.beancontext.BeanContext

  • 集合类型依赖查找

    • java.beans.beancontext.BeanContext

  • 层次依赖查找

    • java.beans.beancontext.BeanContext

public interface BeanContext extends BeanContextChild, Collection, DesignMode, Visibility {
    // ...
}

java.beans.beancontext.BeanContext 针对 GUI 程序以及普通计算程序,Spring 的实现很大程度上参考了其实现,其实现了 Collection 接口,因此其内部所有成员都是 Bean,BeanContext 负责对其进行 crud 操作

5.2 单一类型依赖查找

Spring 中单一类型依赖查找依赖 BeanFactory 接口,可以根据名称、类型(实时和延迟查找)、名称+类型进行查找。

延迟查找举例如下:

public class ObjectProviderDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ObjectProviderDemo.class);
​
        lookupByObjectProvider(context);
​
        context.close();
    }
​
    @Bean
    public String helloWorld() {
        return "Hello World";
    }
​
    private static void lookupByObjectProvider(AnnotationConfigApplicationContext context) {
        ObjectProvider<String> beanProvider = context.getBeanProvider(String.class);
        System.out.println(beanProvider.getObject());
    }
}

5.3 集合类型依赖查找(ListableBeanFactory)

Spring 中集合类型依赖查找依赖 ListableBeanFactory 接口,查找方式如下:

  • 根据 Bean 类型查找

    • 获取同类型 Bean 名称列表(BeanDefinition 中进行查找,Bean 还未实例化

      • getBeanNamesForType(Class)

      • Spring 4.2 getBeanNamesForType(ResolvableType)

    • 获取同类型 Bean 实例列表(Bean 已经实例化或者会触发 Bean 的实例化)

      • getBeanOfType(Class)以及重载方法

  • 根据注解查找

    • Spring 3.0 获取标注类型 Bean 名称列表(BeanDefinition 中进行查找,Bean 还未实例化

      • getBeanNamesForAnnotation(Class<? extends Annotation>)

    • Spring 3.0 获取标注类型 Bean 实例列表(Bean 已经实例化或者会触发 Bean 的实例化)

      • getBeansWithAnnotation(Class<? extends Annotation>)

    • Spring 3.0 获取指定名称+标注类型 Bean 实例(Bean 已经实例化或者会触发 Bean 的实例化)

      • findAnnotationOnBean(String, Class<? extends Annotation>)

5.4 层次性依赖查找

层次型依赖查找依赖 HierarchicalBeanFactory 接口

  • 父 BeanFactory:getParentBenaFactory

  • 层次性查找

    • 根据 Bean 名称查找

      • 基于 containsLocalBean 方法实现(当前 BeanFactory 查找,不包含父类 BeanFactory)

    • 根据 Bean 类型查找实例列表

      • 单一类型:BeanFactoryUtils#beanOfType

      • 集合类型:BeanFactoryUtils#beansOfTypeIncludingAncestors

    • 根据 Java 注解查找名称列表

      • BeanFactoryUtils#beanNamesForTypeIncludingAncestors

真正使用时通过使用 ConfigurableListableBeanFactory 来使用,接口间继承关系如下:

// 父子级联容器接口(类似双亲委派,优先在父容器中查找,找不到才在子容器中查找)
public interface HierarchicalBeanFactory extends BeanFactory {
​
    /**
     * Return the parent bean factory, or {@code null} if there is none.
     */
    @Nullable
    BeanFactory getParentBeanFactory();
​
    /**
     * Return whether the local bean factory contains a bean of the given name,
     * ignoring beans defined in ancestor contexts.
     * <p>This is an alternative to {@code containsBean}, ignoring a bean
     * of the given name from an ancestor bean factory.
     * @param name the name of the bean to query
     * @return whether a bean with the given name is defined in the local factory
     * @see BeanFactory#containsBean
     */
    boolean containsLocalBean(String name);
​
}
​
// 可配置容器接口,包含父子级联功能以及单例注册功能,自己新增的功能有配置BeanFacory的属性,比如BeanPostProcessor、PropertyEditor等
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
    // ...
}
​
// 可配置容器接口,包含集合容器功能、自动注入(非Spring管理的Bean实现依赖注入,被注入的对象必须是Spring容器内的对象)、可配置容器接口(父子级联、单例注册)
public interface ConfigurableListableBeanFactory
        extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {
    // ...
}

根据名称查找举例:

public class HierarchicalDependencyLookupDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(HierarchicalDependencyLookupDemo.class);
​
        // 获取 HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListableBeanFactory
        ConfigurableListableBeanFactory factory = context.getBeanFactory();
        System.out.println("当前 BeanFactory 的 parent :" + factory.getParentBeanFactory());
​
        // 添加 parentBeanFactory
        HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory();
        factory.setParentBeanFactory(parentBeanFactory);
        System.out.println("当前 BeanFactory 的 parent :" + factory.getParentBeanFactory());
​
        displayContainsBean(factory, "user");
        displayLocalBean(factory, "user");
        displayLocalBean(parentBeanFactory, "user");
​
        // 启动上下文
        context.refresh();
​
        // 关闭上下文
        context.close();
    }
​
    // 创建父容器
    private static HierarchicalBeanFactory createParentBeanFactory() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
        return factory;
    }
​
    private static void displayLocalBean(HierarchicalBeanFactory factory, String beanName) {
        System.out.printf("当前 BeanFactory[%s] 是否包含 Local Bean[name: %s] : %s\n", factory, beanName, factory.containsLocalBean(beanName));
    }
​
    private static void displayContainsBean(HierarchicalBeanFactory factory, String beanName) {
        System.out.printf("当前 BeanFactory[%s] 是否包含 Bean[name: %s] : %s\n", factory, beanName, containsBean(factory, beanName));
    }
​
    // 类似双亲委派,优先从父容器中查找,递归查找
    private static boolean containsBean(HierarchicalBeanFactory factory, String beanName) {
        BeanFactory parentBeanFactory = factory.getParentBeanFactory();
        if (parentBeanFactory instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory hierarchicalBeanFactory = (HierarchicalBeanFactory) parentBeanFactory;
            if (containsBean(hierarchicalBeanFactory, beanName)) {
                return true;
            }
        }
        return factory.containsLocalBean(beanName);
    }
}
​
// 输出结果:
当前 BeanFactory 的 parent :null
    
当前 BeanFactory 的 parent :org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1: defining beans [user,superUser,userRepository,objectFactory]; root of factory hierarchy
    
当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@25359ed8: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,hierarchicalDependencyLookupDemo]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1] 是否包含 Bean[name: user] : true
    
当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@25359ed8: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,hierarchicalDependencyLookupDemo]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1] 是否包含 Local Bean[name: user] : false
    
当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@282003e1: defining beans [user,superUser,userRepository,objectFactory]; root of factory hierarchy] 是否包含 Local Bean[name: user] : true

5.5 延迟依赖查找

延迟查找依赖的接口:

  • org.springframework.beans.factory.ObjectFactory

  • org.springframework.beans.factory.ObjectProvider

    • Spring 5 对 Java 8 特性扩展

      • 函数式接口

        • getIfAvailable(Supplier)

        • ifAvailable(Consumer)

      • Stream 扩展——stream()

思考:非延迟初始化的 bean 是否能够实现延迟查找?

public class ObjectProviderDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(ObjectProviderDemo.class);
        context.refresh();
​
        lookupByObjectProvider(context);
        lookupIfAvailable(context);
        lookupByStreamOps(context);
​
        context.close();
    }
​
    private static void lookupByStreamOps(AnnotationConfigApplicationContext context) {
        ObjectProvider<String> provider = context.getBeanProvider(String.class);
        provider.stream().forEach(System.out::println);
    }
​
    private static void lookupIfAvailable(AnnotationConfigApplicationContext context) {
        ObjectProvider<User> beanProvider = context.getBeanProvider(User.class);
        User user = beanProvider.getIfAvailable(User::user);
        System.out.println("当前 User 对象:" + user);
    }
​
    @Bean
    @Primary
    public String helloWorld() {
        return "Hello World";
    }
​
    @Bean
    public String message() {
        return "Message";
    }
​
    private static void lookupByObjectProvider(AnnotationConfigApplicationContext context) {
        ObjectProvider<String> beanProvider = context.getBeanProvider(String.class);
        System.out.println(beanProvider.getObject());
    }
}

5.6 安全依赖查找

依赖查找安全性对比:

依赖查找类型代表实现是否安全
单一类型查找BeanFactory#getBean
ObjectFactory#getObject
ObjectProvider#getIfAvailable
集合类型查找ListableBeanFactory#getBeansOfType
ObjectProvider#stream

安全与否在于调用方法进行依赖查找时是否会触发异常。

层次类型依赖查找安全性依赖于单一或者集合类型依赖查找安全性

推荐使用 ObjectProvider 进行依赖查找,因为可以实现单一和集合类型查找,并且查找是安全的

5.7 内建可查找依赖

AbstractApplicationContext 内建可查找的依赖

Bean名称Bean 实例使用场景
environmentEnvironment 对象外部化配置以及 Profiles
SystemPropertiesjava.util.Properties 对象Java 系统属性
systemEnvironmentjava.util.Map 对象操作系统环境变量
messageSourceMessageSource 对象国际化文案
lifecycleProcessorLifecycleProcessor 对象Lifecycle Bean 处理器
applicationEventMulticasterApplicationEventMulticaster 对象Spring 事件广播器

注解驱动 Spring 应用上下文内建可查找依赖(部分)(componentScan或者使用ApplicaitonContext激活这些内部 Bean)(名称很长也比较难记,可以在AnnotationConfigUtils类中进行查找)

Bean 名称Bean 实例使用场景
org.springframework.context.annotation.internalConfigurationClassPostProcessor(BeanFactory生命周期)ConfigurationClassPostProcessor对象处理 Spring 配置类
org.springframework.context.annotation.internalAutoWiredAnnotationBeanPostProcessorAutoWiredAnnotationBeanPostProcessor 对象处理 @Autowired 以及 @ Value 注解
org.springframework.context.annotation.internalCommonAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor 对象(条件激活)处理 JSR-250 注解,如 @PostConstructor 等
org.springframework.context.annotation.internalEventListenerMethodProcessorEventListenerMethodProcessor 对象处理标注 @EventListener 的 Spring 事件监听方法
org.springframework.context.annotation.internalDefaultEventListenerFactoryDefaultEventListenerFactory@EventListener 事件监听方法适配为 ApplicationListener
org.springframework.context.annotation.internalPersistenceAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessor(条件激活)处理 JPA 注解场景

5.8 依赖查找经典异常

BeansException 子类型(非 check 异常,Runtime 异常)

异常类型触发条件(举例)场景举例
NoSuchBeanDefinitionException当查找 Bean 不存在于 IoC 容器时BeanFactory#getBean ObjectFactory#getObject
NoUniqueBeanDefinitionException类型依赖查找时,IoC 容器存在多个 Bean 实例BeanFactory#getBean
BeanInstantiationException当 Bean 所对应的类型非具体类型时BeanFactory#getBean
BeanCreationException当 Bean 初始化过程出错Bean 初始化方法执行异常时
BeanDefinitionStoreException当 BeanDefinition 配置元信息非法时XML 配置资源无法打开时

5.9 面试题

5.9.1 ObjectFactory 和 BeanFactory 区别

ObjectFactory 是 Spring 比较早期的接口,ObjectFactory 和 BeanFactory 均提供依赖查找功能,不同点:

  • BeanFactory 仅关注一个或一种类型的 Bean 依赖查找,并且自身不具备依赖查找的能力,能力由 BeanFactory 支撑

  • BeanFactory 提供单一类型、集合类型以及层次性等多种依赖查找功能

从实用也可以看出:

<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
    <property name="targetBeanName" value="user"/>
</bean>
public class ObjectFactoryCreatingFactoryBean extends AbstractFactoryBean<ObjectFactory<Object>> {
​
    @Nullable
    private String targetBeanName;
​
​
    /**
     * Set the name of the target bean.
     * <p>The target does not <i>have</i> to be a non-singleton bean, but realistically
     * always will be (because if the target bean were a singleton, then said singleton
     * bean could simply be injected straight into the dependent object, thus obviating
     * the need for the extra level of indirection afforded by this factory approach).
     */
    public void setTargetBeanName(String targetBeanName) {
        this.targetBeanName = targetBeanName;
    }
​
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.hasText(this.targetBeanName, "Property 'targetBeanName' is required");
        super.afterPropertiesSet();
    }
​
​
    @Override
    public Class<?> getObjectType() {
        return ObjectFactory.class;
    }
​
    @Override
    protected ObjectFactory<Object> createInstance() {
        BeanFactory beanFactory = getBeanFactory();
        Assert.state(beanFactory != null, "No BeanFactory available");
        Assert.state(this.targetBeanName != null, "No target bean name specified");
        return new TargetBeanObjectFactory(beanFactory, this.targetBeanName);
    }
​
​
    /**
     * Independent inner class - for serialization purposes.
     * 此处生成 ObjectFactory 对象,当通过 Spring 容器获取对象时,其实获取的是 ObjectFactory#getObject方法返回的对象
     * 此处是从容器中获取对象的
     */
    @SuppressWarnings("serial")
    private static class TargetBeanObjectFactory implements ObjectFactory<Object>, Serializable {
​
        private final BeanFactory beanFactory;
​
        private final String targetBeanName;
​
        public TargetBeanObjectFactory(BeanFactory beanFactory, String targetBeanName) {
            this.beanFactory = beanFactory;
            this.targetBeanName = targetBeanName;
        }
​
        @Override
        public Object getObject() throws BeansException {
            // 从 Spring 容器中根据 name 获取 bean 返回
            return this.beanFactory.getBean(this.targetBeanName);
        }
    }
​
}

5.9.2 BeanFactory#getBean 方法是否线程安全

是线程安全的,操作过程中会通过 synchronized 加锁,jdk 5 update 6 中新增偏向锁功能,该功能可以大大降低 synchronized 关键字的重量,在只有一个线程访问时,可以将只看为没有锁(只有加锁时会执行一个 CAS 操作将锁偏向当前线程,后续该线程运行到加锁位置只会比较偏向锁线程是否为当前线程,是的话就通过 CAS 替换一次 lock record,后续不再加锁),因此不建议将启动过程放在子线程执行,就在 main 主线程执行性能是最好的

5.9.4 Spring 的依赖查找和依赖注入在来源上有什么区别

依赖查找只能查询容器中有的 Bean(手动注入或者框架内建),但是依赖注入除了容器中有的 Bean 以外,还支持注入非 Bean 对象(内建依赖)

6. 依赖注入

两层意思:

  • 注入 Spring Bean 或者可依赖对象(@Autowired)

  • 注入外部化配置(@Value)

6.1 依赖注入的模式

  • 手动模式

    • XML 资源配置元信息

    • Java 注解配置元信息

    • API 配置元信息(普通用户使用较少,框架开发者使用较多,需掌握)

  • 自动模式(官方不推荐使用自动绑定模式)

    • Autowiring(自动绑定)

依赖注入类型:

依赖注入类型配置元数据举例
Setter 方法<property name = "user" ref="userBean"/>
构造器<constructor-arg name="user" ref="userBean"/>
字段@Autowired User user;
方法@Autowired public void user(User user) {...}
接口回调class MyBean implements BeanFactoryAware {...}

6.2 自动绑定

6.2.1 自动绑定原因

Spring 容器能够根据自动绑定在合作的 Bean(依赖 Bean 和被依赖 Bean) 之间,通过某种策略自动处理这些合作者之间的关系

自动绑定优点:

  • 减少属性或者构造器参数的设置

  • 自动绑定能够更新配置

6.2.2 自动绑定模式

模式说明
no默认值,未激活 Autowiring,需要手动指定依赖注入对象
byName根据被注入属性的名称作为 Bean 名称进行依赖查找,并将对象设置到该属性
byType根据被注入属性的类型作为依赖类型进行查找,并将对象设置到该属性
constructor特殊 byType 类型,用于构造器参数

参考枚举类:org.springframework.beans.factory.annotation.Autowire

public enum Autowire {
​
    /**
     * Constant that indicates no autowiring at all.
     */
    NO(AutowireCapableBeanFactory.AUTOWIRE_NO),
​
    /**
     * Constant that indicates autowiring bean properties by name.
     */
    BY_NAME(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME),
​
    /**
     * Constant that indicates autowiring bean properties by type.
     */
    BY_TYPE(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);
​
​
    private final int value;
​
​
    Autowire(int value) {
        this.value = value;
    }
​
    public int value() {
        return this.value;
    }
​
    /**
     * Return whether this represents an actual autowiring value.
     * @return whether actual autowiring was specified
     * (either BY_NAME or BY_TYPE)
     */
    public boolean isAutowire() {
        return (this == BY_NAME || this == BY_TYPE);
    }
​
}

6.2.3 自动绑定限制以及不足(XML 特有)

  • 自动绑定会被属性或者构造器参数等注入覆盖

  • 无法绑定 String、原生类型、Class 类型以及这些类型的数组类型

  • 缺乏精确性

  • 自动绑定的信息很难在一些工具类中进行呈现,比如自动生成的文档等

  • 当绑定对象不止一个会有歧义,并且会报错

6.3 Setter 方法依赖注入

  • 手动模式

    • XML 资源配置元信息

    • Java 注解配置元信息

    • API 配置元信息

  • 自动模式

    • byName

    • byType

6.3.1 XML 方式

public class XmlDependencySetterInjectionDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions("classpath:dependency-setter-injection.xml");
        System.out.println(factory.getBean(UserHolder.class));
    }
}
<?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">
​
    <import resource="beans-ioc-lookup.xml"/>
​
    <bean class="com.gcl.dependency.injection.UserHolder">
        <property name="user" ref="superUser"/>
    </bean>
​
</beans>

6.3.2 Java 注解

public class AnnotationDependencySetterInjectionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationDependencySetterInjectionDemo.class);
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        System.out.println(context.getBean(UserHolder.class));
​
        context.close();
    }
​
    @Bean
    public UserHolder userHolder(User user) {
        // 构造器方式
        // return new UserHolder(user);
​
        // setter 方式
        UserHolder holder = new UserHolder();
        holder.setUser(user);
        return holder;
    }
}

6.3.3 API 方式

public class ApiDependencySetterInjectionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册 BeanDefinition
        context.registerBeanDefinition("userHolder", createBeanDefinitionUserHolder());
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        System.out.println(context.getBean(UserHolder.class));
​
        context.close();
    }
​
    private static BeanDefinition createBeanDefinitionUserHolder() {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
​
        builder.addPropertyReference("user", "superUser");
​
        return builder.getBeanDefinition();
    }
}

6.3.4 自动注入方式

public class AutowiringByNameDependencySetterInjectionDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory context = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("autowiring-dependency-setter-injection.xml");
        System.out.println(context.getBean(UserHolder.class));
    }
}
<?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">
​
    <import resource="beans-ioc-lookup.xml"/>
​
    <!-- byName -->
    <bean class="com.gcl.dependency.injection.UserHolder" autowire="byName"/>
    
    <!-- byType -->
<!--    <bean class="com.gcl.dependency.injection.UserHolder" autowire="byType"/>-->
​
</beans>

6.4 构造器依赖注入

  • 手动模式

    • XML 资源配置元信息

    • Java 注解配置元信息

    • API 配置元信息

  • 自动模式

    • constructor

6.4.1 XML 方式

public class XmlDependencyConstructorInjectionDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions("classpath:dependency-constructor-injection.xml");
        System.out.println(factory.getBean(UserHolder.class));
    }
}
<?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">
​
    <import resource="beans-ioc-lookup.xml"/>
​
    <bean class="com.gcl.dependency.injection.UserHolder">
        <constructor-arg name="user" ref="user"/>
    </bean>
​
</beans>

6.4.2 Java 注解方式

public class AnnotationDependencyConstructorInjectionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationDependencyConstructorInjectionDemo.class);
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        System.out.println(context.getBean(UserHolder.class));
​
        context.close();
    }
​
    @Bean
    public UserHolder userHolder(User user) {
         // 构造器方式
         return new UserHolder(user);
    }
}

6.4.3 API 方式

public class ApiDependencyConstructorInjectionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
​
        context.registerBeanDefinition("userHolder", createBeanDefinitionUserHolder());
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        System.out.println(context.getBean(UserHolder.class));
​
        context.close();
    }
​
    private static BeanDefinition createBeanDefinitionUserHolder() {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
​
        // 按照构造器参数顺序进行设置
        builder.addConstructorArgReference("superUser");
​
        return builder.getBeanDefinition();
    }
}

6.4.4 自动注入方式

public class AutowiringByConstructorDependencySetterInjectionDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory context = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("autowiring-dependency-constructor-injection.xml");
        System.out.println(context.getBean(UserHolder.class));
    }
}
<?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">
​
    <import resource="beans-ioc-lookup.xml"/>
​
    <bean class="com.gcl.dependency.injection.UserHolder" autowire="constructor"/>
​
</beans>

6.5 字段注入

只有手动注入方式,以注解为主:

  • @Autowired(忽略静态字段)

  • @Resource

  • @Inject

public class AnnotationDependencyFieldInjectionDemo {
​
    // Autowire 会忽略静态字段
    @Autowired
    private UserHolder userHolder;
​
    @Resource
    private UserHolder userHolder2;
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationDependencyFieldInjectionDemo.class);
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        AnnotationDependencyFieldInjectionDemo demo = context.getBean(AnnotationDependencyFieldInjectionDemo.class);
        System.out.println(demo.userHolder);
        System.out.println(demo.userHolder == demo.userHolder2);
​
        context.close();
    }
​
    @Bean
    public UserHolder userHolder(User user) {
         // 构造器方式
         return new UserHolder(user);
    }
}

6.6 方法注入

手动注入:

  • @Autowired

  • @Resource

  • @Inject(可选)、

  • @Bean

public class AnnotationDependencyMethodInjectionDemo {
​
    private UserHolder userHolder;
​
    private UserHolder userHolder2;
​
    @Autowired
    public void initUserHolder(UserHolder userHolder) {
        this.userHolder = userHolder;
    }
​
    @Resource
    public void initUserHolder2(UserHolder userHolder2) {
        this.userHolder2 = userHolder2;
    }
​
    @Bean
    public UserHolder userHolder(User user) {
        // 构造器方式
        return new UserHolder(user);
    }
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationDependencyMethodInjectionDemo.class);
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        AnnotationDependencyMethodInjectionDemo demo = context.getBean(AnnotationDependencyMethodInjectionDemo.class);
        System.out.println(demo.userHolder);
        System.out.println(demo.userHolder == demo.userHolder2);
​
        context.close();
    }
}

6.7 回调注入

Aware 系列接口回调注入(内建依赖)

内建接口说明
BeanFactoryAware获取 IoC 容器——BeanFactory
ApplicationContextAware获取 Spring 应用上下文——ApplicationContext 对象
EnvironmentAware获取 Environment 对象
ResourceLoaderAware获取资源加载器对象——ResourceLoader
BeanClassLoaderAware获取加载当前 Bean Class 的 ClassLoader
BeanNameAware获取当前 Bean的名称
MessageSourceAware获取 MessageSource 对象,用于 Spring 国际化
ApplicationEventPublisherAware获取 ApplicationEventPublisher 对象,用于 Spring 事件
EmbeddedValueResolverAware获取 StringValueResolver 对象,用于占位符处理

6.8 依赖注入类型选择

  • 低依赖:构造器注入(Spring 官方推荐注入方式)

  • 多依赖:Setter 方法注入

  • 便利性:字段注入(Spring 和 Spring 都不推荐使用字段注入)

  • 声明类:方法注入

6.9 基础类型注入

  • 原生类型(Primitive):boolean、byte、char、short、int、long、float、double

  • 标量类型(Scalar):Number、Character、Boolean、Enum、Locale、Charset、Currency、Properties、UUID

  • 常规类型(General):Object、String、TimeZone、Calendar、Optional等

  • Spring 类型:Resource、InputSource、Formatter

<?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 definitions here -->
    <bean id="user" class="com.gcl.bean.User">
        <property name="name" value="foo"/>
        <property name="age" value="18"/>
        <!-- 枚举类型注入 -->
        <property name="city" value="BEIJING"/>
        <!-- Resource 对象注入-->
        <property name="configFileLocation" value="classpath:user-config.properties"/>
    </bean>
    <bean id="superUser" class="com.gcl.bean.SuperUser" parent="user" primary="true">
        <property name="level" value="1"/>
    </bean>
    <bean id="userRepository" class="com.gcl.bean.UserRepository" autowire="byType"/>
    <!--        <property name="users">-->
    <!--            <util:list>-->
    <!--                <ref bean="user"/>-->
    <!--                <ref bean="superUser"/>-->
    <!--            </util:list>-->
    <!--        </property>-->
    <!--    </bean>-->
​
    <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
        <property name="targetBeanName" value="user"/>
    </bean>
​
</beans>

6.10 集合类型注入

  • 数组类型(Array):原生类型、标量类型、常规类型、Spring 类型

  • 集合类型(Collection)

    • Collection:List、Set(SortedSet、NavigableSet、EnumSet)

    • Map:Properties

<?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 definitions here -->
    <bean id="user" class="com.gcl.bean.User">
        <property name="name" value="foo"/>
        <property name="age" value="18"/>
        <property name="city" value="BEIJING"/>
        <property name="cities" value="BEIJING,HANGZHOU"/>
        <property name="cityList">
            <list>
                <value>BEIJING</value>
                <value>HANGZHOU</value>
                <value>SHANGHAI</value>
            </list>
        </property>
        <property name="citySet">
            <set>
                <value>HANGZHOU</value>
                <value>SHANGHAI</value>
            </set>
        </property>
        <property name="cityMap">
            <map>
                <entry key="beijing" value="BEIJING"/>
                <entry key="shanghai" value="SHANGHAI"/>
            </map>
        </property>
        <property name="configFileLocation" value="classpath:user-config.properties"/>
    </bean>
    <bean id="superUser" class="com.gcl.bean.SuperUser" parent="user" primary="true">
        <property name="level" value="1"/>
    </bean>
    <bean id="userRepository" class="com.gcl.bean.UserRepository" autowire="byType"/>
    <!--        <property name="users">-->
    <!--            <util:list>-->
    <!--                <ref bean="user"/>-->
    <!--                <ref bean="superUser"/>-->
    <!--            </util:list>-->
    <!--        </property>-->
    <!--    </bean>-->
​
    <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
        <property name="targetBeanName" value="user"/>
    </bean>
​
</beans>

6.11 限定注入

  • @Qualifier 限定注入

    • 通过 Bean 名称限定

    • 通过分组限定

  • 基于 @Qualifier 扩展限定

    • 自定义注解——如 Spring Cloud @LoadBalance

public class AnnotationQualifierDependencyInjectionDemo {
​
    @Autowired
    @Qualifier("user")
    private User user;
​
    @Autowired
    private Collection<User> allUsers;
​
    @Autowired
    @Qualifier
    private Collection<User> qualifiedUsers;
​
    // 通过使用 @Qualifier 进行逻辑分组
    @Bean
    @Qualifier
    public User user1() {
        User user = new User();
        user.setName("user1");
        return user;
    }
​
    // 通过使用 @Qualifier 进行逻辑分组
    @Bean
    @Qualifier
    public User user2() {
        User user = new User();
        user.setName("user2");
        return user;
    }
​
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationQualifierDependencyInjectionDemo.class);
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
        context.refresh();
​
        AnnotationQualifierDependencyInjectionDemo bean = context.getBean(AnnotationQualifierDependencyInjectionDemo.class);
        System.out.println(bean.user);
        System.out.println(bean.allUsers);
        System.out.println(bean.qualifiedUsers);
​
        context.close();
    }
}

6.12 延迟注入

  • 使用 API ObjectFactory 实现延迟注入

    • 单一类型

    • 集合类型

  • 使用 API ObjectProvider 实现延迟注入(推荐使用,安全)

    • 单一类型

    • 集合类型

6.13 依赖处理过程

  • 入口——DefaultListableBeanFactory#resolveDependency

  • 依赖描述符——DependencyDescriptor

  • 自动绑定候选对象处理器——AutowireCandidateResolver

6.14 @Autowire 注入原理

  • 元信息解析(MergedBeanDefinitionPostProcessor#postProcessorMergedBeanDefinition 方法中进行)

  • 依赖查找(注入过程中包含依赖查找)

  • 依赖注入(字段、方法)(AutowiredAnnotationBeanPostProcessor#postProcessorProperties 方法中进行)

6.15 JSR-330 @Inject 注入原理

public AutowiredAnnotationBeanPostProcessor() {
    this.autowiredAnnotationTypes.add(Autowired.class);
    this.autowiredAnnotationTypes.add(Value.class);
    try {
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
                                          ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

@Inject 注解跟 @Autowired 注解注入流程是相似的,如果引入了 JSR-330 jar 包,就可以使用该注解进行注入

6.16 Java 通用注解注入原理(@Resource等)

CommonAnnotationBeanPostProcessor(比 AutowiredAnnotationBeanPostProcessor 支持更多的注解,除了注入注解,还支持生命周期注解)

  • 注入注解

    • javax.xml.ws.WebServiceRef

    • javax.ejb.EJB

    • javax.annotation.Resource

  • 生命周期注解(构建 LifecycleElement)

    • javax.annotation.PostConstructor

    • javax.annotation.PreDestroy

6.17 自定义依赖注入注解

  • 基于 AutowiredAnnotationBeanPostProcessor 实现

  • 自定义实现

    • 生命周期处理

      • InstantiationAwareBeanPostProcessor

      • MergedBeanDefinitionPostProcessor

    • 元数据

      • InjectElement

      • InjectMetadata

6.18 面试题

6.18.1 有多少种依赖注入方式

  • 构造器注入(少依赖,强制依赖)

  • Setter 注入(多依赖,非强制依赖)

  • 字段注入

  • 方法注入

  • 接口回调注入

6.18.2 你偏好构造器注入还是 Setter 注入?

两种依赖注入均可使用,如果是必须依赖,推荐使用构造器注入,Setter注入可选依赖

依赖注入少用外部注解,包括 Java 注解和 Spring 注解,sh奥用注解就少依赖,使用原始 API、原始构造器参数来进行操作,可以解决依赖问题,同时还能解决线程安全问题(构造器可以确保线程安全问题)

6.18.3 Spring 依赖注入来源有哪些?

依赖查找通过 getBean,依赖注入通过 resolveDependency

  • 自定义 Bean(XML、注解、API)

  • 内建 Bean

  • 内建依赖

7. Spring IoC依赖来源

7.1 依赖查找来源

7.1.1 来源

来源配置元数据
Spring DeanDefinition<bean id = "user" class="com.gcl.bean.User"/>
@Bean public User user {...}
BeanDefinitionBuilder
单例对象API实现

7.1.2 Spring 内建 BeanDefinition 以及内建单例对象

详情见 5.7 小结

如何通过 XML 开启注解功能?首先注解功能是由 AnnotationConfigUtils#registerAnnotationConfigProcessors 方法注册启动的,而该方法在 AnnotationConfigBeanDefinitionParser、ComponentScanBeanDefinitionParser、AnnotationBeanDefinitionReader、ClasspathBeanDefinitionScanner 中被调用,启动注解方式有两种:

<context:annotation-config/>
<!-- 不存在的路径也可以,只要有该元素即可 -->
<context:component-scan base-package="xxx"/>

7.2 依赖注入来源

来源配置元数据
Spring DeanDefinition<bean id = "user" class="com.gcl.bean.User"/>
@Bean public User user {...}
BeanDefinitionBuilder
单例对象API实现
非 Spring 容器管理对象
public class DependencySourceDemo {

    // 注入在 postProcessorProperties 方法执行,早于 Setter 注入,也早于 @PostConstructor
    // 这四个对象在 AbstractApplicationContext 中进行注册,但是无法从容器中获取
    @Resource
    private BeanFactory factory;
    @Resource
    private ResourceLoader loader;
    @Resource
    private ApplicationContext context;
    @Resource
    private ApplicationEventPublisher publisher;

    @PostConstruct
    public void init() {
        System.out.println("beanFactory == applicationContext : " + (factory == context));
        System.out.println("beanFactory == applicationContext.getBeanFactory : " + (factory == context.getAutowireCapableBeanFactory()));
        System.out.println("loader == applicationContext : " + (loader == context));
        System.out.println("publisher == applicationContext : " + (publisher == context));
    }

    @PostConstruct
    public void getBean() {
        getBean(BeanFactory.class);
        getBean(ApplicationEventPublisher.class);
        getBean(ApplicationContext.class);
        getBean(ResourceLoader.class);
    }

    private <T> T getBean(Class<T> type) {
        try {
            return factory.getBean(type);
        } catch (NoSuchBeanDefinitionException e) {
            System.err.println("当前类型" + type.getName() + "无法在 BeanFactory 中查找到!");
        }
        return null;
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(DependencySourceDemo.class);

        context.refresh();

        System.out.println(context.getBean(User.class));

        context.close();
    }

    @Bean
    public User user() {
        User user = new User();
        user.setName("foo");
        user.setAge(18);
        return user;
    }
}

// 运行结果:
beanFactory == applicationContext : false
beanFactory == applicationContext.getBeanFactory : true
loader == applicationContext : true
publisher == applicationContext : true
当前类型org.springframework.beans.factory.BeanFactory无法在 BeanFactory 中查找到!
当前类型org.springframework.context.ApplicationEventPublisher无法在 BeanFactory 中查找到!
当前类型org.springframework.context.ApplicationContext无法在 BeanFactory 中查找到!
当前类型org.springframework.core.io.ResourceLoader无法在 BeanFactory 中查找到!
User{name='foo', age=18}
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
​
    // ...
    
    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
    // ...
}

7.3 Spring 容器管理和游离对象

来源Spring Bean 对象生命周期管理配置元信息使用场景
Spring BeanDefinition依赖查找、依赖注入
单体对象依赖查找、依赖注入
Resolvable Dependency(游离对象)依赖注入

7.4 Spring BeanDefinition 作为依赖来源

  • 元数据:BeanDefinition

  • 注册:BeanDefinitionRegistry#registerBeanDefinition

  • 类型:延迟和非延迟

  • 顺序:Bean 生命周期顺序按照注册顺序

注册时会使用 Map 保存 name 和 BeanDefiniton 的映射,并且还会用一个 ArrayList 保存 name 顺序(Map无序),供后续实例化使用,并且需要注意在 Springboot 2.1 以后不允许注入同名 bean

7.5 单例对象作为依赖来源

  • 要素

    • 来源:外部普通 Java 对象(不一定是 POJO)

    • 注册:SingletonBeanRegistry#registerSingleton

  • 限制

    • 无生命周期管理

    • 无法实现延迟初始化 Bean

public class SingletonBeanRegistrationDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 创建外部单体对象
        DefaultUserFactory factory = new DefaultUserFactory();
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 注册外部单体对象
        beanFactory.registerSingleton("userFactory", factory);
        context.refresh();
        // 依赖查找
        UserFactory factory1 = context.getBean(UserFactory.class);
        System.out.println(factory == factory1);
    }
}

7.6 非 Spring 容器管理对象作为依赖来源

  • 要素

    • 注册:ConfigurableListableBeanFactory#registerResolvableDependency

  • 限制

    • 无生命周期管理

    • 无法实现延迟初始化

    • 无法通过依赖查找

public class ResolvableDependencySourceDemo {
​
    @Resource
    private String value;
​
    @Resource
    private BeanFactory factory;
​
    @PostConstruct
    public void init() {
        System.out.println(value);
        try {
            factory.getBean(String.class);
        } catch (Exception e) {
            System.out.println("err");
        }
    }
​
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
​
        ConfigurableListableBeanFactory factory = context.getBeanFactory();
        factory.registerResolvableDependency(String.class, "Hello world");
​
        context.register(ResolvableDependencySourceDemo.class);
​
        context.refresh();
​
        context.close();
    }
}

7.7 外部化配置作为依赖来源(@Value)

  • 要素

    • 类型:非常规 Spring 对象依赖来源

  • 限制

    • 无生命周期管理

    • 无法实现延迟初始化 Bean

    • 无法通过依赖查找

@Configuration
@PropertySource(value = "default.properties", encoding = "UTF-8")
public class ExternalConfigurationDependencySourceDemo {
​
    @Value("${usr.name:text}")
    private String name;
​
    @Value("${user.age:18}")
    private int age;
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(ExternalConfigurationDependencySourceDemo.class);
        context.refresh();
        ExternalConfigurationDependencySourceDemo demo = context.getBean(ExternalConfigurationDependencySourceDemo.class);
        System.out.println(demo.name);
        System.out.println(demo.age);
        context.close();
    }
}

7.8 面试题

7.8.1 依赖注入和依赖查找来源是否相同

不同,依赖查找来源仅限于 Spring BeanDefinition 以及单例对象,而依赖注入的来源还包括 Resolvable Dependency 以及 @Value 所标注的外部化配置

7.8.2 单例对象能在 IoC 容器启动后注册吗?

可以,单例对象与 BeanDefinition 不同, BeanDefinition 会被 ConfigurableListableBeanFactory#freezeConfiguration 方法影响从而冻结注册,单例对象则没有该限制

7.8.3 Spring 依赖注入有哪些来源

Spring BeanDefinition

单例对象

Resolvable Dependency

@Value 外部化配置

8. Spring Bean 作用域

来源说明
sigleton默认 Spring Bean 作用域,一个 BeanFactory 有且仅有一个实例
prototype原型作用域,每次依赖查找和依赖注入生成新 Bean 对象
request将 Spring Bean 存储在 ServletRequest 上下文中
session将 Spring Bean 存储在 HttpSession 中
application将 Spring Bean 存储在 ServletContext 中

主要是 Singleton 和 Prototype 这两种,其余的是在后续版本中才加入的,并且在 BeanDefinition 中也只有 sigleton 和 prototype 两种作用域的定义

8.1 Singleton

在一定范围内是唯一的,Spring 中就是在一个 BeanFactory 内部是唯一的

8.2 Prototype

@Configuration
@PropertySource("default.properties")
public class BeanScopeDemo {

    @Value("${usr.name:default}")
    private String name;

    @Value("${usr.age:28}")
    private int age;

    @Bean
    public User singletonUser() {
        return new User(this.name, this.age);
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public User prototypeUser() {
        return new User(this.name, this.age);
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(BeanScopeDemo.class);

        context.refresh();

        System.out.println(context.getBean("singletonUser", User.class) == context.getBean("singletonUser", User.class));
        System.out.println(context.getBean("prototypeUser", User.class) == context.getBean("prototypeUser", User.class));

        context.close();
    }
}

public class User implements BeanNameAware {

    private transient String beanName;
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

    @PostConstruct
    public void init() {
        System.out.println("用户 Bean[" + beanName + "]初始化...");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("用户 Bean[" + beanName + "]销毁...");
    }

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }
}

// 运行结果:
用户 Bean[singletonUser]初始化...
true
用户 Bean[prototypeUser]初始化...
用户 Bean[prototypeUser]初始化...
false
用户 Bean[singletonUser]销毁...

Spring 容器没有办法管理 protoType Bean 完整的生命周期,也没有办法记录实例的存在。销毁回调方法将不会执行,可以利用 BeanPostProcessor 进行清扫工作。

结论:

  1. Singleton Bean 无论依赖查找还是依赖注入,均为同一个对象,Prototype Bean 无论依赖查找还是依赖注入,均为新生成对象

  2. 如果依赖注入集合类型对象,Singleton Bean 和 Prototype Bean 均会存在一个且只有一个

  3. 无论是 Singleton 还是 Prototype Bean 均会执行初始化方法回调,不过仅 Singleton Bean 会执行销毁方法回调

8.3 Request

配置

  • XML——<bean class="..." scope="request"/>

  • Java 注解——@RequestScope 或 @Scope(WebApplicationContext.SCOPE_REQUEST)

实现

  • API——RequestScope

对象会被代理,页面渲染时是新对象,通过 Model 传递给 JSP 的对象每次都是新的,但是代理对象是不变的

8.4 Session

配置

  • XML——<bean class="..." scope="session"/>

  • Java 注解——@SessionScope 或 @Scope(WebApplicationContext.SCOPE_SESSION)

实现

  • API——SessionScope

对象会被代理,页面渲染时是新对象,通过 Model 传递给 JSP 的对象在每次会话中都是相同的,不同会话中不同,但是代理对象是不变的

8.5 Application

配置

  • XML——<bean class="..." scope="application"/>

  • Java 注解——@ApplicationScope 或 @Scope(WebApplicationContext.SCOPE_APPLICATION)

实现

  • API——ServletContextScope

JSP EL 变量搜索路径 page -> request -> session -> application(ServletContext)

ApplicationContext 作用域其实就是将 Bean 放到 ServletContext 中进行存储,因此我们不必使用 Model 进行数据的传输,可以直接在 JSP 页面中使用 Application 作用域的bean

8.6 自定义 Bean 作用域

  • 实现 Scope

    • org.springfamework.beans.factory.config.Scope

  • 注册 Scope

    • API——org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope

    • 配置

      <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
          <property name="scopes">
              <map>
                  <entry key="..." value="..."/>
              </map>
          </property>
      </bean>

8.7 面试题

8.7.1 Spring 内建的 Bean 作用域有几种?

六种:sigleton、prototype、request、session、application 以及 websocket

从设计模式:单例和原生(原型)

从web模式:request、session、application、websocket

8.7.2 singleton Bean 是否在一个应用中是唯一的

不是,只是在一个 BeanFactory 中是唯一的,整个应用可能包含多个应用上下文(层次上下文),不同上下文中互不影响

同样的,一个静态字段在 JVM 中是否唯一?不是唯一的,静态字段相对于 ClassLoader 唯一,但是一个 JVM 进程可能包含多个 ClassCloader

8.7.3 application 作用域 Bean 是否能被其他方案替代

是可以的,实际上,application bean 与 singleton bean 没有本质区别,无非是在某处进行了存储,跟 Ioc 容器中的 bean 是同一个

9. Spring Bean 生命周期

9.1 Spring Bean 元信息配置阶段

  • 面向资源

    • XML 配置

    • Properties 配置

  • 面向注解

  • 面向 API

基于 Properties 配置元信息举例:

public class BeanMetadataConfigurationDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(factory);
        System.out.println(reader.loadBeanDefinitions("user.properties"));

        System.out.println(factory.getBean(User.class));
        System.out.println(Arrays.toString(factory.getBeanDefinitionNames()));
    }
}
usr.(class)=com.gcl.bean.lifecycle.bean.User
usr.name=foo
usr.age=1

9.2 Spring Bean 元信息解析阶段

  • 面向资源 BeanDefinition 解析

    • BeanDefinitionReader

    • XML 解析器——BeanDefinitionParser

  • 面向注解 BeanDefinition 解析

    • annotatedBeanDefinition

面向注解 BeanDefinition 解析举例:

public class AnnotatedBeanDefinitionParsingDemo {

    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        // 基于 Java 注解的 AnnotatedBeanDefinitionReader 实现
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(factory);
        // 注册类
        reader.register(AnnotatedBeanDefinitionParsingDemo.class, User.class);

        // 普通 Class 作为 Component 注册到 Spring IoC 容器后,通常 Bean 名称为类名首字母小写
        // Bean 名称生成来自于 BeanNameGenerator,注解实现 AnnotationBeanNameGenerator
        System.out.println(factory.getBean("annotatedBeanDefinitionParsingDemo", AnnotatedBeanDefinitionParsingDemo.class));
        System.out.println(factory.getBean("user", User.class));
    }
}

9.3 Spring Bean 注册阶段

Bean 注册是在 DefaultListableBeanFactory#registerBeanDefinition 中进行的,默认允许覆盖(重复注册),内部有两个数据结构:

/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
/** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

map 用于保存 BeanDefinition 和 name,list 用于顺序保存 Bean 的name,为后续实例化提供顺序(map无序)

9.4 Spring BeanDefinition 合并阶段

Spring 中 BeanDefinition 有两种类型:root 和 generic,其中 root 表示没有 parent,generic 则可有可不有,Spring 中 BeanDefiniton 的合并就是针对 generic 类型 BeanDefinition,如果有 parent,则将 parent 属性合并到 generic 中,当然该操作是一个递归的操作,会一直递归合并直到 root 或者直到 BeanDefinition 没有 parent 为止。

合并过程在 AbstractBeanFactory#getMergedBeanDefinition 中实现(唯一实现)

public BeanDefinition getMergedBeanDefinition(String name) throws BeansException {
    String beanName = transformedBeanName(name);
    // Efficiently check whether bean definition exists in this factory.
    if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
        return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName);
    }
    // Resolve merged bean definition locally.
    return getMergedLocalBeanDefinition(beanName);
}

找到存在该 BeanDefinition 定义的 BeanFactory,从中进行合并操作

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    // Quick check on the concurrent map first, with minimal locking.
    RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
    if (mbd != null && !mbd.stale) {
        return mbd;
    }
    return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
​
/** Map from bean name to merged RootBeanDefinition. */
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);

会将 mbd 保存在 mergedBeanDefinitions 中,如果存在,直接返回,否则进行合并

合并过程首先判断该 BeanDefinition 是否有 parent,如果没有,则用当前 BeanDefinition 创建一个 RootBeanDefinition,否则查询其 parent,利用其 parent BeanDefinition 创建 RootBeanDefinition,并且将子 BeanDefinition 和其合并,合并中利用子类已有的配置覆盖父类中的配置,即配置优先级为子类 > 父类。

9.5 Spring Bean Class 加载阶段

Spring Bean Class 加载通过 Java 的 ClassLoader 进行,通过 XML 配置的 BeanDefinition,其 beanClass 初始为 String,经过 ClassLoader 加载以后变成 Class 类型,并且此时 AbstractBeanDefinition#hasBeanClass 为 true

9.6 Spring Bean 实例化前阶段

public class BeanInstantiationLifecycleDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
​
        reader.loadBeanDefinitions("merged-bean-definition.xml");
​
        System.out.println(factory.getBean("superUser"));
    }
​
    static class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
        @Override
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            if (beanName.equals("superUser") && beanClass == SuperUser.class) {
                return new SuperUser();
            }
            return null;
        }
    }
}

该方法并不常用,因为该方法执行后,后续的实例化过程就不会执行

9.7 Spring Bean 实例化阶段

  • 传统方式

    • 实例化策略——InstantiationStrategy

  • 构造器依赖注入

AbstractBeanFactory#getBean ->

AbstractBeanFactory#doGetBean -> 得到 mbd,然后通过 mbd、beanName 以及 args 创建对象

AbstractBeanFactory#createBean ->

AbstractAutowireCapableBeanFactory#createBean ->

AbstractAutowireCapableBeanFactory#doCreateBean -> 返回 BeanWrapper,将对象包装成 BeanWrapper 后返回

AbstractAutowireCapableBeanFactory#createBeanInstance -> 构造器自动注入还是无参构造器创建对象

AbstractAutowireCapableBeanFactory#instantiateBean -> 获取策略实例化对象,默认cglib

CglibSubclassingInstantiationStrategy#instantiate -> cglib策略创建对象,调用父类策略创建对象

SimpleInstantiationStrategy#instantiate -> 此处通过反射创建对象

9.8 Spring Bean 实例化后阶段

InstantiationAwareBeanPostProcessor#postProcessorAfterInstantiation

该阶段决定 Bean 是否允许对属性进行赋值,默认允许

public class BeanInstantiationLifecycleDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
​
        reader.loadBeanDefinitions("merged-bean-definition.xml");
​
        System.out.println(factory.getBean("superUser"));
        System.out.println(factory.getBean("user"));
    }
​
    static class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
        @Override
        // 此处默认返回null,如果返回不为null,则表示后续的实例化过程不用进行,使用返回的对象当作bean进行注入
        // 该方法跟后续的 postProcessAfterInstantiation 互斥
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            if (beanName.equals("superUser") && beanClass == SuperUser.class) {
                return new SuperUser();
            }
            return null;
        }
​
        @Override
        public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
            // 默认返回 true,表示允许属性赋值,此处将 User 对象设置为 false,表示不允许对名称为"user",类型为User的对象赋值
            return !beanName.equals("user") || bean.getClass() != User.class;
        }
    }
}
​
// 运行结果:
SuperUser{level=0} User{name='null', age=0}
User{name='null', age=0}
<?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="com.gcl.bean.lifecycle.bean.User">
        <property name="name" value="foo"/>
        <property name="age" value="18"/>
    </bean>

    <bean id="superUser" class="com.gcl.bean.lifecycle.bean.SuperUser" parent="user">
        <property name="level" value="1"/>
    </bean>
</beans>

9.9 Spring Bean 属性赋值前阶段

  • Bean 属性元信息

    • PropertyValues

  • Bean 属性赋值前回调

    • Spring 1.2-5.0:InstantiationAwareBeanPostProcessor#postProcessPropertyValues

    • Spring 5.1:InstantiationAwareBeanPostProcessor#postProcessProperties

注意:如果 postProcessAfterInstantiation 方法返回 false,则该回调不会被调用,是互斥的。

postProcessBeforeInstantiation 方法如果返回对象不为 null,则不仅会跳过 postProcessAfterInstantiation 方法,同时还会跳过 postProcessProperties 方法,因为完全跳过实例化整个过程,所以后续有关实例化的所有回调都会被忽略

public class BeanInstantiationLifecycleDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);

        reader.loadBeanDefinitions("merged-bean-definition.xml");

        System.out.println(factory.getBean("superUser"));
        System.out.println(factory.getBean("user"));
    }

    static class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

        @Override
        public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
            if (ObjectUtils.nullSafeEquals("user", beanName) && bean.getClass() == User.class) {
                //假设 <property name="name" value="foo"/> 在 XML 中配置,那么此处 pvs 中有一个 PropertyValue(name="foo")
                MutablePropertyValues propertyValues = null;
                if (pvs instanceof MutablePropertyValues) {
                    propertyValues = (MutablePropertyValues) pvs;
                } else {
                    propertyValues = new MutablePropertyValues();
                }
                // 等价于 <property name="name" value="bar"/>,不允许配置不存在的属性,不然报错
                propertyValues.addPropertyValue("name", "bar");
                propertyValues.addPropertyValue("age", "19");
                return propertyValues;
            }
            return null;
        }
    }
}

// 运行结果:
SuperUser{level=1} User{name='foo', age=18}
User{name='bar', age=19}

9.10 Spring Bean Aware 接口回调阶段(属于初始化阶段)

Spring Aware 接口:(接口注入)(按照执行顺序排列)

  • BeanNameAware

  • BeanClassLoaderAware

  • BeanFactoryAware

  • EnvironmentAware

  • EmbeddedValueResolverAware

  • ResourceLoaderAware

  • ApplicationEventPublisherAware

  • MessageSourceAware

  • ApplicationContextAware

从 EnvironmentAware 开始,隶属 ApplicationContext 生命周期,因此只使用 BeanFactory 是无法注入这些 Bean 的。即 BeanFactory 只能注入前三个 Aware 回调,ApplicationContext 则可以注入所有回调。

class ApplicationContextAwareProcessor implements BeanPostProcessor {

	private final ConfigurableApplicationContext applicationContext;

	private final StringValueResolver embeddedValueResolver;


	/**
	 * Create a new ApplicationContextAwareProcessor for the given context.
	 */
	public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
		this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
	}


	@Override
	@Nullable
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
				bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
				bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
			return bean;
		}

		AccessControlContext acc = null;

		if (System.getSecurityManager() != null) {
			acc = this.applicationContext.getBeanFactory().getAccessControlContext();
		}

		if (acc != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareInterfaces(bean);
				return null;
			}, acc);
		}
		else {
			invokeAwareInterfaces(bean);
		}

		return bean;
	}

	private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof EnvironmentAware) {
			((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
		}
		if (bean instanceof EmbeddedValueResolverAware) {
			((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
		}
		if (bean instanceof ResourceLoaderAware) {
			((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
		}
		if (bean instanceof ApplicationEventPublisherAware) {
			((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
		}
		if (bean instanceof MessageSourceAware) {
			((MessageSourceAware) bean).setMessageSource(this.applicationContext);
		}
		if (bean instanceof ApplicationContextAware) {
			((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
		}
	}

}

AbstractApplicationContext#prepareBeanFactory 方法中通过 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); 添加上面的 Processor

9.11 Spring Bean 初始化前阶段

  • 已完成

    • Bean 实例化

    • Bean 属性赋值

    • Bean Aware 接口回调

  • 方法回调

    • BeanPostProcessor#postProcessorBeforeInitialization

9.12 Spring Bean 初始化阶段

  • @PostContructor

  • 实现 InitializingBean 接口的 afterProperties 方法

  • 自定义初始化方法

public class BeanInitializationLifecycleDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        factory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor());

        reader.loadBeanDefinitions("merged-bean-definition.xml");

        System.out.println(factory.getBean("user"));
    }
}
<?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="com.gcl.bean.lifecycle.bean.User" init-method="init">
        <property name="name" value="foo"/>
        <property name="age" value="18"/>
    </bean>

    <bean id="superUser" class="com.gcl.bean.lifecycle.bean.SuperUser" parent="user">
        <property name="level" value="1"/>
    </bean>
</beans>

9.13 Spring Bean 初始化后阶段

BeanPostProcessor#postProcessorAfterInitialization

初始化方法 AbstractAutowireCapableBeanFactory#initializeBean,该方法中调用了四个方法

  • invokeAwareMethods

  • applyBeanPostProcessorsBeforeInitialization

  • invokeInitMethods

  • applyBeanPostProcessorsAfterInitialization

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

因此生命周期会按照上述来进行。

9.14 Spring Bean 初始化完成阶段

Spring 4.1+:SmartInitializingSingleton#afterSingletonsInstantiated

public class BeanInstantiationLifecycleDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);

        reader.loadBeanDefinitions("merged-bean-definition.xml");

        factory.preInstantiateSingletons();

        System.out.println(factory.getBean("user"));
    }
}

public class User implements InitializingBean, SmartInitializingSingleton {
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

    @PostConstruct
    public void postConstructor() {
        System.out.println("post");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("initialization bean");
    }

    public void init() {
        System.out.println("init-method");
    }

    @Override
    public void afterSingletonsInstantiated() {
        System.out.println("after");
    }
}

// 运行结果:
initialization bean
init-method
initialization bean
init-method
after
after
User{name='bar', age=19}

使用 SmartInitializingSingleton 要么在 ApplicaitonContext 场景中使用,如果在 BeanFactory 场景中使用,需要手动调用 preInstantiateSingletons 方法触发 Bean 的实例化,在该方法中会通过 beanDefinitionNames 依次进行初始化操作

public void preInstantiateSingletons() throws BeansException {
    if (logger.isTraceEnabled()) {
        logger.trace("Pre-instantiating singletons in " + this);
    }
​
    // Iterate over a copy to allow for init methods which in turn register new bean definitions.
    // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
​
    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    final FactoryBean<?> factory = (FactoryBean<?>) bean;
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
                                                                    ((SmartFactoryBean<?>) factory)::isEagerInit,
                                                                    getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                       ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
            }
            else {
                // 此处执行 Bean 的初始化操作,初始化 Bean 中首先会创建依赖 Bean,创建完以后,再执行实例化操作
                // 实例化操作执行过程中,会创建 BeanWrapper,实例化后通过 populateBean 执行属性赋值,调用
                // InstantiationAwareBeanPostProcessors
                // BeanPostProcessors
                // 属性赋值完以后调用 initializeBean 方法进行初始化
                // invokeAwareMethods
                // applyBeanPostProcessorsBeforeInitialization
                // invokeInitMethods:InitializingBean#afterPropertiesSet -> init-method
                // applyBeanPostProcessorsAfterInitialization
                getBean(beanName);
            }
        }
    }
​
    // Trigger post-initialization callback for all applicable beans...
    for (String beanName : beanNames) {
        Object singletonInstance = getSingleton(beanName);
        if (singletonInstance instanceof SmartInitializingSingleton) {
            final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    smartSingleton.afterSingletonsInstantiated();
                    return null;
                }, getAccessControlContext());
            }
            else {
                // 此处执行 Smart 的回调
                smartSingleton.afterSingletonsInstantiated();
            }
        }
    }
}

9.15 Spring Bean 销毁前阶段

  • DestructionAwareBeanPostProcessor#postProcessBeforeDestruction

9.16 Spring Bean 销毁阶段

  • @PreDestroy(跟 DestructionAwareBeanPostProcessor 是在同一等级执行的)

  • 实现 DisposableBean 接口的 destroy 方法

  • 自定义销毁方法

销毁最终调用 DisposableBeanAdapter#destroy 方法进行

@Override
public void destroy() {
    if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
        for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
            // @PreConstructor 和 实现了 DestructionAwareBeanPostProcessor 接口的销毁逻辑在这里执行
            processor.postProcessBeforeDestruction(this.bean, this.beanName);
        }
    }

    // 实现 DispoableBean 接口的销毁逻辑在这里执行
    if (this.invokeDisposableBean) {
        if (logger.isTraceEnabled()) {
            logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'");
        }
        try {
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    ((DisposableBean) this.bean).destroy();
                    return null;
                }, this.acc);
            }
            else {
                ((DisposableBean) this.bean).destroy();
            }
        }
        catch (Throwable ex) {
            String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
            if (logger.isDebugEnabled()) {
                logger.warn(msg, ex);
            }
            else {
                logger.warn(msg + ": " + ex);
            }
        }
    }

    // 自定义销毁方法在这里执行
    if (this.destroyMethod != null) {
        invokeCustomDestroyMethod(this.destroyMethod);
    }
    else if (this.destroyMethodName != null) {
        Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
        if (methodToInvoke != null) {
            invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
        }
    }
}

9.17 Spring Bean 垃圾收集阶段

  • 关闭 Spring 容器(应用上下文)

  • 执行GC

  • Spring Bean 覆盖的 finalize 方法被回调(不保证一定被执行)

9.18 面试题

9.18.1 BeanPostProcessor 使用场景

BeanPostProcessor 提供 Spring Bean 初始化前和初始化后的生命周期回调,分别对应 postProcessBeforeInitialization 以及 postProcessAfterInitialization 方法,允许对关心的 Bean 进行扩展,甚至是替换

其中 ApplicationContext 相关的 Aware 回调也是基于 BeanPostProcessor 实现的,即 ApplicationContextAwareProcessor

BeanPostProcessor 针对所有 Bean,所有 Bean 初始化都会走这里的逻辑(前提是进行了自定义实现以及注入),可以在 Bean 实例化前后加入一些操作,例如 ApplicationContextAwareProcessor 做法如下:

class ApplicationContextAwareProcessor implements BeanPostProcessor {
​
    private final ConfigurableApplicationContext applicationContext;
​
    private final StringValueResolver embeddedValueResolver;
​
​
    /**
     * Create a new ApplicationContextAwareProcessor for the given context.
     */
    public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
    }
​
​
    @Override
    @Nullable
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
                bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
                bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
            return bean;
        }
​
        AccessControlContext acc = null;
​
        if (System.getSecurityManager() != null) {
            acc = this.applicationContext.getBeanFactory().getAccessControlContext();
        }
​
        if (acc != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareInterfaces(bean);
                return null;
            }, acc);
        }
        else {
            invokeAwareInterfaces(bean);
        }
​
        return bean;
    }
​
    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof EnvironmentAware) {
            ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
        }
        if (bean instanceof EmbeddedValueResolverAware) {
            ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
        }
        if (bean instanceof ResourceLoaderAware) {
            ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
        }
        if (bean instanceof ApplicationEventPublisherAware) {
            ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
        }
        if (bean instanceof MessageSourceAware) {
            ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
        }
        if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
        }
    }
​
}

会拦截所有 bean,如果 bean 实现了上面六种接口中至少一种,就进行响应的 aware 回调处理

9.18.2 BeanFactoryPostProcessor 和 BeanPostProcessor 区别

BeanFactoryPostProcessor 是 Spring BeanFactory(实际为 ConfigurableListableBeanFatory)的后置处理器,用于扩展 BeanFactory,或通过 BeanFactory 进行依赖查找和依赖注入

BeanFactoryPostProcessor 必须有 Spring ApplicationContext 执行,BeanFactory 无法直接于其进行交互

而 BeanPostProcessor 则直接与 beanFactory 关联,属于 N 对 1 的关系

public interface BeanFactoryPostProcessor {
​
    /**
     * Modify the application context's internal bean factory after its standard
     * initialization. All bean definitions will have been loaded, but no beans
     * will have been instantiated yet. This allows for overriding or adding
     * properties even to eager-initializing beans.
     * @param beanFactory the bean factory used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
​
}

从接口定义可知,该接口方法被回调时是 BeanDefiniton 都加载完成但还没有任何一个 Bean 进行实例化,此时可以对 BeanFactory 进行一定的操作

  • 增加 BeanPostProcessor

  • 依赖查找或依赖注入

通过 AbstractApplicationContext#refresh 方法中 invokeBeanFactoryPostProcessors 方法启动

BeanFactory 只能手动添加 BeanPostProcessor,但是在 ApplicationContext 中,在 AbstractApplicationContext#refresh 方法中会通过方法 registerBeanPostProcessors 来进行自动的扫描和注册

BeanPostProcessor 可以为其指定顺序 Order,可通过 @Order 注解、实现 Ordered 接口或者 PriorityOrdered 接口来指定顺序

9.18.3 BeanFactory 如何处理 Bean 生命周期

BeanFactory 默认实现为 DefaultListableBeanFactory,其中 Bean 生命周期与方法映射如下:

  • BeanDefinition 注册阶段——registerBeanDefinition

  • BeanDefinition 合并阶段——getMergedBeanDefinition

  • Bean 实例化前阶段——resolveBeforeInstantiation

  • Bean 实例化阶段——createBeanInstacence

  • Bean 实例化后阶段——populateBean

  • Bean 属性赋值前阶段——populateBean

  • Bean 属性复制阶段——populateBean

  • Bean Aware 接口回调阶段——initializeBean

  • Bean 初始化前阶段——initializeBean

  • Bean 初始化阶段——initializeBean

  • Bean 初始化后阶段——initializeBean

  • Bean 初始化完成阶段——preInstrantiateSingletons

  • Bean 销毁前阶段——destroyBean

  • Bean 销毁阶段——destroyBean

10. Spring 配置元信息

10.1 Spring 配置元信息

  • Spring Bean 配置元信息——BeanDefinition

  • Spring Bean 属性元信息——PropertyValues

  • Spring 容器配置元信息

  • Spring 外部化配置元信息+PropertySource

  • Spring Profile 元信息——@Profile

10.2 Spring Bean 配置元信息

  • GenericBeanDefinition:通用型 BeanDefinition

  • RootBeanDefinition:无 Parent 的 BeanDefinition 或者合并后的 BeanDefinition

  • AnnotatedBeanDefinition:注解标注的 BeanDefinition

GenericBeanDefinition 和 RootBeanDefinition 都继承了 AbstractBeanDefinition,AbstractBeanDefinition 提供了对于 Definiton 的写操作,GenericBeanDefinition 简单重写了 setParent 方法,RootBeanDefinition 则增加了许多字段(IoC 中 BeanDefitnition 通常需要 merge,merge 后的 BeanDefinition 就是 RootBeanDefinition,也叫做 mbd),这些字段用于创建 Bean 时提供一些辅助性操作(提升性能等)

DefaultListableBeanFactory 中存储所有 BeanDefinition,AbstractBeanDefinition 中存储所有 mergedBeanDefinition。

public interface AnnotatedBeanDefinition extends BeanDefinition {
​
    /**
     * Obtain the annotation metadata (as well as basic class metadata)
     * for this bean definition's bean class.
     * @return the annotation metadata object (never {@code null})
     */
    AnnotationMetadata getMetadata();
​
    /**
     * Obtain metadata for this bean definition's factory method, if any.
     * @return the factory method metadata, or {@code null} if none
     * @since 4.1.1
     */
    @Nullable
    MethodMetadata getFactoryMethodMetadata();
​
}

AnnotatedBeanDefinition 可以通过反射或者 ASM 方式获取 AnnotationMetadata,Spring 5.2 及以后 ASM 方式标记过时,新增 SimpleAnnotationMetadataReadingVisitor 类使用

getFactoryMethodMetadata 元数据是否来自某个方法,比如 Factory 中方法,可有可无,因为并不是每个 Bean 都是通过 Factory method 来生成的

10.3 Spring Bean 属性元信息

  • Bean 属性元信息——PropertyValues

    • 可修改实现——MutablePropertyValues

    • 元素成员——PropertyValue

  • Bean 属性上下文存储——AttributeAccessor

  • Bean 元信息元素——BeanMetadataElement

属性元信息中后两个属性上下文存储以及 Bean 元信息元素两种元信息都是起辅助作用的,上下文信息可以在创建 Bean 的过程中进行使用,元信息元素可以标记 BeanDefinition 的来源,具体使用可参照下例:

public class BeanConfigurationMetadataDemo {
    public static void main(String[] args) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        builder.addPropertyValue("name", "bar");

        // 获取 AbstractBeanDefinition
        AbstractBeanDefinition definition = builder.getBeanDefinition();
        // 附加属性(不影响 Bean populate、initialize)
        definition.setAttribute("name", "foo");
        // 当前 BeanDefinition 来自哪里,辅助作用
        definition.setSource(BeanConfigurationMetadataDemo.class);

        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        factory.registerBeanDefinition("user", definition);
        factory.addBeanPostProcessor(new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof User && beanName.equals("user")) {
                    User user = (User) bean;
                    BeanDefinition beanDefinition = factory.getBeanDefinition("user");
                    if (beanDefinition.getSource() == BeanConfigurationMetadataDemo.class) {
                        String name = (String) beanDefinition.getAttribute("name");
                        user.setName(name);
                        return user;
                    }
                }
                return bean;
            }
        });

        System.out.println(factory.getBean("user", User.class));
    }
}

// 运行结果:
User{name='foo', age=0}

10.4 Spring 容器配置元信息

Spring XML 配置元信息——beans 元素相关

beans 元素属性默认值使用场景
profilenullSpring Profile 配置值
default-lazy-initdefault当 outter beans "default-lazy-init" 属性存在时,继承该值,否则为"false"
default-mergedeafult当 outter beans "default-merge" 属性存在时,继承该值,否则为"false"
default-autowiredefault当 outter beans "default-autowire" 属性存在时,继承该值,否则为"false"
default-autowire-candidatesnull默认 Spring Beans 名称 pattern
default-init-methodnull默认 Spring Beans 自定义初始化方法
default-destroy-methodnull默认 Spring Beans 自定义销毁方法

上面这些属性,都是在 XML 的 beans 标签中设置的

Spring XML 配置元信息——应用上下文相关

XML 元素使用场景
<context:annotation-config/>激活 Spring 注解驱动
<context:component-scan/>Spring @Component 以及自定义注解扫描
<context:load-time-weaver/>激活 Spring LoadTimeWeaver
<context:mbean-export/>暴露 Spring Beans 作为 JMX Beans
<context:mbean-server/>将当前平台作为 MBeanServer
<context:property-placeholder/>加载外部化配置资源作为 Spring 属性配置
<context:property-override/>利用外部化配置资源覆盖 Spring 属性值

可以在 BeanDefinitionParserDelegate 中查看其解析规则

10.5 基于 XML 文件装载 Spring Bean 配置元信息

Spring Bean 配置元信息

XML 元素使用场景
<beans:beans>单 XML 资源下的多个 Spring Beans 配置
<beans:bean>单个 Spring Bean 定义(BeanDefinition)配置
<beans:alias>为 Spring Bean 定义(BeanDefinition)映射别名
<beans:import>加载外部 Spring XML 资源配置

底层实现:XmlBeanDefinitionReader

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    // ...
    // 从输入流注册 BeanDefinition
    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    // ...
}
​
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
    // ...
    // 获取 DOM 操作需要的 Document 元素
    Document doc = doLoadDocument(inputSource, resource);
    // 执行注册
    int count = registerBeanDefinitions(doc, resource);
    // ...
}
​
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // 创建 DocumentReader
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    // 保存现有 BeanDefinition 数量
    int countBefore = getRegistry().getBeanDefinitionCount();
    // 注册 BeanDefinitions
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    // 返回注册 BeanDefinition 数量
    return getRegistry().getBeanDefinitionCount() - countBefore;
}
​
​
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    doRegisterBeanDefinitions(doc.getDocumentElement());
}
​
protected void doRegisterBeanDefinitions(Element root) {
    // Any nested <beans> elements will cause recursion in this method. In
    // order to propagate and preserve <beans> default-* attributes correctly,
    // keep track of the current (parent) delegate, which may be null. Create
    // the new (child) delegate with a reference to the parent for fallback purposes,
    // then ultimately reset this.delegate back to its original (parent) reference.
    // this behavior emulates a stack of delegates without actually necessitating one.
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);
​
    if (this.delegate.isDefaultNamespace(root)) {
        // 获取设置的 profiles 跟 环境变量中 profile 进行比较,相同才往后执行,不同就注解返回不解析
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            // We cannot use Profiles.of(...) since profile expressions are not supported
            // in XML config. See SPR-12458 for details.
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                 "] not matching: " + getReaderContext().getResource());
                }
                return;
            }
        }
    }
​
    preProcessXml(root);
    // 真正解析的地方
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);
​
    this.delegate = parent;
}
​
// XML -> BeanDefinition
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {
                    parseDefaultElement(ele, delegate);
                }
                else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}
​
// Spring 默认解析的元素
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // recurse
        doRegisterBeanDefinitions(ele);
    }
}
​
// 自定义 XML 解析
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
    return parseCustomElement(ele, null);
}

10.6 基于 Properties 文件装载 Spring Bean 配置元信息(不推荐)

Spring Bean 配置元信息

Properties 属性名使用场景
(class)Bean 类全限定名
(abstract)是否为抽象的 BeanDefinition
(parent)指定 parent BeanDefinition 名称
(lazy-init)是否为延迟初始化
(ref)引用其他 Bean 的名称
(scope)设置 Bean 的 scope 属性
$nn 表示第 n+1 个构造器参数

底层实现:PropertiesBeanDefinitionReader

不推荐使用,配置很复杂并且局限性很大

10.7 基于 Java 注解装载 Spring Bean 配置元信息

Spring 模式注解

Spring 注解场景说明起始版本
@Repository数据仓储模式注解2.0
@Component通用组件模式注解2.5
@Service服务模式注解2.5
@ControllerWeb 控制器模式注解2.5
@Configuration配置类模式注解3.0

Spring Bean 依赖注入注解

Spring 注解场景说明起始版本
@AutowiredBean 依赖注入,支持多种依赖查找方式2.5
@Qualifier细粒度的 @Autowired 依赖查找2.5
Java 注解场景说明起始版本
@Resource类似于 @Autowired2.5
@Inject类似于 @Autowired2.5

AutowiredAnnotationBeanPostProcessor 处理 @Autowired、@Value、@Inject 注解

CommonAnnotationBeanPostProcessor 处理 @Resource 注解,javax.xml.ws.WebServiceRef注解、javax.ejb.EJB,主要处理 j2se 或者 Java EE 相关的注解或者 API

Spring Bean 条件装配注解

Spring 注解场景说明起始版本
@Profile配置化条件装配3.1
@Conditional编程条件装配4.0

@Profile 通过 @Conditional 实现

实现 Condition 接口

class ProfileCondition implements Condition {
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 读取注解所有的信息
		MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
		if (attrs != null) {
			for (Object value : attrs.get("value")) {
				if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
					return true;
				}
			}
			return false;
		}
		return true;
	}
}

Spring Bean 生命周期回调注解

Spring 注解场景说明起始版本
@PostConstructor替换 XML 元素 <bean init-method="..."/> 或 InitializingBean2.5
@PreDestroy替换 XML 元素 <bean destroy-method="..."/> 或 DisposableBean2.5

CommonAnnotationBeanPostProcessor 中处理这两个注解

10.8 Spring Bean 配置元信息底层实现

Spring BeanDefinition 解析与注册

实现场景实现类起始版本
XML 资源XmlBeanDefinitionReader1.0
Properties 资源PropertiesBeanDefinitionReader1.0
Java 注解AnnotatedBeanDefinitionReader3.0

Spring 注解驱动主要开始于 Spring 2.5 版本

Java 注解和类相关,并不和资源相关,因此 AnnotatedBeanDefinitionReader 和 BeanDefinitionReader 这个接口是没有关系的,因为 BeanDenitionReader 是基于资源(Resource)的,XmlBeanDefinitionReader 和 PropertiesBeanDefinitionReader 都属于 BeanDenitionReader 实现

10.8.1 Spring XML 资源 BeanDefinition 解析与注册

核心 API——XmlBeanDefinitionReader

  • 资源——Resource

  • 底层——BeanDefinitionDocumentReader

    • XML 解析——Java DOM Level 3 API

    • BeanDefinition 解析——BeanDefinitionParserDelegate

    • BeanDefinition 注册——BeanDefinitionRegistrar

10.8.2 Spring Properties 资源 BeanDefinition 解析与注册

核心 API——PropertiesBeanDefinitionReader

  • 资源

    • 字节流——Resource

    • 字符流——EncodedResource

  • 底层

    • 存储——java.util.Properties

    • BeanDefinition 解析——API 内部实现

    • BeanDefinition 注册——BeanDefinitionRegistry

10.8.3 Spring Java 注册 BeanDefinition 解析与注册

核心 API——AnnotatedBeanDefinitionReader

  • 资源——类对象 java.lang.Class

  • 底层

    • 条件评估——ConditionEvaluattor

    • Bean 范围解析——ScopeMetadataResolver

    • BeanDefinition 解析——内部 API 实现

    • BeanDefinition 处理——AnnotationConfigUtils.processCommonDefinitionAnnotations

    • BeanDefinition 注册——BeanDefinitionRegistry

public class AnnotatedBeanDefinitionReader {

    // Bean 注册容器
    private final BeanDefinitionRegistry registry;

    // Bean 名称生成器
    private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;

    // Scope 解析器,是否单例以及代理模式
    private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

    // 评估器,评估是否需要跳过注册
    private ConditionEvaluator conditionEvaluator;
    // ...
}

10.9 基于 XML 文件装载 Spring IoC 容器配置元信息

spring.handlers 文件中配置解析命名空间的 handler

http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

spring.schema 文件中配置命名空间对应的 xsd 文件

http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans.xsd

handler 继承 NamespaceHandlerSupport,重写 init 方法,init 方法内部注册具体的解析类

public class UtilNamespaceHandler extends NamespaceHandlerSupport {

	private static final String SCOPE_ATTRIBUTE = "scope";


	@Override
	public void init() {
        // 注册解析 list、map 等的 parser 类
		registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser());
		registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser());
		registerBeanDefinitionParser("list", new ListBeanDefinitionParser());
		registerBeanDefinitionParser("set", new SetBeanDefinitionParser());
		registerBeanDefinitionParser("map", new MapBeanDefinitionParser());
		registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser());
	}
    // ...
}

解析类继承 AbstractSingleBeanDefinitionParser,重写 doParse 方法

private static class ListBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

		@Override
		protected Class<?> getBeanClass(Element element) {
			return ListFactoryBean.class;
		}

		@Override
		protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
			List<Object> parsedList = parserContext.getDelegate().parseListElement(element, builder.getRawBeanDefinition());
			builder.addPropertyValue("sourceList", parsedList);

			String listClass = element.getAttribute("list-class");
			if (StringUtils.hasText(listClass)) {
				builder.addPropertyValue("targetListClass", listClass);
			}

			String scope = element.getAttribute(SCOPE_ATTRIBUTE);
			if (StringUtils.hasLength(scope)) {
				builder.setScope(scope);
			}
		}
	}

此处解析完成,BeanDefinitionBuilder 属性也设置完成,返回 AbstractBeanDefinitionParser#parse 方法将结果封装成 BeanDefinitionHolder 进行注册。整体流程如下:

XmlBeanDefinitionReader#loadBeanDefinition(Resource) -> doLoadBeanDefinitions() -> doLoadDocument() -> registerBeanDefinitions() -> 
DefaultBeanDefinitionDocumentReader#registerBeanDefinitions() -> doRegisterBeanDefinitions() -> parseBeanDefinitions() -> 
BeanDefinitionParserDelegate#parseCustomElement() -> 根据 namespaceUri 获取 handler -> 
NamespaceHandlerSupport#parse -> 查找 parser -> 
BeanDefinitionParser#parse()(此处将parseInternal 解析完成的 BeanDefinition 封装成 BeanDefinitionHolder 进行注册) -> parseInternal() -> doParse()(子类重写的方法,解析逻辑在此实现)

parseCustonElement 流程:

  • 获取 namespace

  • 通过 namespace 获取 handler

  • 构造 ParserContext

  • 解析元素,获取 BeanDefinition

10.10 基于 Java 注解装载 Spring IoC 容器配置元信息

10.10.1 Spring IoC 容器装配注解

Spring 注解场景说明起始版本
@ImportResource替换 XML 元素 <import>3.0
@Import导入 Configuration Class3.0
@ComponentScan扫描指定 package 下标注 Spring 模式注解的类3.1

@ImportResource 导入 xml 文件,@Import 导入 Java 类(配置类或者普通类)

10.10.2 Spring IoC 属性配置注解

Spring 注解场景说明起始版本
@PropertySource配置属性抽象 PropertySource3.1
@PropertySources@PropertySource 集合注解4.0

从 Java 8 开始,一个类上支持同时使用多个 @PropertySource 注解

10.11 基于 Extensible XML authoring 扩展原理

扩展步骤:

  • 编写 XML Schema 文件:定义 XML 结构

  • 自定义 NamespaceHandler 实现:命名空间绑定

  • 自定义 BeanDefinitionParser 实现:XML 元素与 BeanDefinition 解析

  • 注册 XML 扩展:命名空间与 XML Schema 映射

10.11.1 定义 xsd

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<xsd:schema xmlns="http://gcl.com/schema/users"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://gcl.com/schema/users">

    <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>

    <xsd:complexType name="User">
        <xsd:attribute name="id" type="xsd:long" use="required"/>
        <xsd:attribute name="name" type="xsd:string" use="required"/>
        <xsd:attribute name="age" type="xsd:int"/>
    </xsd:complexType>

    <xsd:element name="user" type="User"/>
</xsd:schema>

10.11.2 定义 handler

public class CustomHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("user", new CustomParser());
    }
}
http\://gcl.com/schema/users=com.gcl.configuration.metadata.handler.CustomHandler

10.11.3 定义 parser

public class CustomParser extends AbstractSingleBeanDefinitionParser {
​
    @Override
    protected Class<?> getBeanClass(Element element) {
        return User.class;
    }
​
    @Override
    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        setAttribute("name", element, builder);
        setAttribute("age", element, builder);
    }
​
    private void setAttribute(String attributeName, Element element, BeanDefinitionBuilder builder) {
        String attribute = element.getAttribute(attributeName);
        if (StringUtils.hasText(attribute)) {
            builder.addPropertyValue(attributeName, attribute);
        }
    }
}

10.11.4 注册 xml 扩展

http\://gcl.com/schema/users.xsd=META-INF/com/gcl/configuration/metadata/users.xsd

10.11.5 测试

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:users="http://gcl.com/schema/users"
       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
       http://gcl.com/schema/users
       http://gcl.com/schema/users.xsd" >
    <users:user id="1" name="foo" age="18"/>
</beans>
public class CustomXmlParserDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions("classpath:META-INF/users.xml");
        System.out.println(factory.getBean(User.class));
    }
}
​
// 运行结果:
User{name='foo', age=18}

10.12 基于 Properties 资源装载外部化配置

  • 注解驱动

    • @org.springframework.context.annotation.PropertySource

    • @org.springframework.context.annotation.PropertySources

  • API 编程

    • org.springframework.core.env.PropertySource

    • org.springframework.core.env.PropertySources

Java 8 支持重复注解功能,也就是支持一个类上重复使用相同的注解,因此可以使用多个 @PropertySource 替换 @PropertySources 注解

10.13 基于 YAML 资源装载外部化配置

API 编程

  • org.springframework.beans.factory.config.YamlProcessor

    • org.springframework.beans.factory.config.YamlMapFactoryBean

    • org.springframework.beans.factory.config.YamlPropertiesFactoryBean

10.14 面试题

10.14.1 Spring 内建 XML Schema 常见有哪些

命名空间所属模块Schema 资源 URL
beansspring-beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
contextspring-contexthttp://www.springframework.org/schema/context/spring-context.xsd
aopspring-aophttp://www.springframework.org/schema/sop/spring-aop.xsd
txspring-txhttp://www.springframework.org/schema/tx/spring-tx.xsd
utilspring-beanshttp://www.springframework.org/schema/util/spring-util.xsd
toolspring-beanshttp://www.springframework.org/schema/tool/spring-tool.xsd

10.14.2 Spring 配置元信息具体有哪些

  • Bean 配置元信息:通过媒介(如 XML、Properties等),解析 BeanDefinition

  • IoC 容器配置元信息:通过媒介(如:XML、Properties等),控制 IoC 容器行为,比如注解驱动、AOP等

  • 外部化配置:通过资源抽象(如:Properties、YAML等),控制 PropertySource

  • Spring Profile:通过外部化配置,提供条件分支流程

10.14.3 Extensible XML authoring 的缺点

  • 高复杂度:开发人员要熟悉 XML Schema,spring.handlers、spring.schemas 以及 Spring API

  • 嵌套元素支持较弱:通常需要使用方法递归或者其嵌套解析的方式处理嵌套元素

  • XML 处理性能较差:Spring XML 基于 DOM Level 3 API 实现,该 API 便于理解,然而性能较差

  • XML 框架移植性较差:很难适配高性能和便利性的 XML 框架,如 JAXB

11. Spring 资源管理

11.1 引入动机

  • Java 标准资源管理强大,然而扩展负载,资源存储方式并不统一

  • Spring 要自立门户

  • Spring 3C原则:抄、超、潮

11.2 Java 标准资源管理

Java 标准资源定位

职责说明
面向资源文件系统、artifact(jar、war、ear文件)以及远程资源(HTTP、FTP等)
API整合java.lang.ClassLoader#getResource、java.io.File 或 java.net.URL
资源定位java.net.URL 或 java.net.URI
面向流式存储java.net.URLConnection
协议扩展java.net.URLStreamHandler 或 java.net.URLStreamHandlerFactory

Java URL 协议扩展

  • 基于 java.net.URLStreamHandlerFactory

  • 基于 java.net.URLStreamHandler

基于 java.net.URLStreamHandler 扩展协议

11.3 Spring 资源接口

类型接口
输入流org.springframework.core.io.InputStreamSource
只读资源org.springframework.core.io.Resource
可写资源org.springframework.core.io.WritableResource
编码资源org.springframework.core.io.support.EncodedResource
上下文资源org.springframework.core.io.ContextResource

上下文资源是给 Servlet 来使用的,应用较为狭窄

11.4 Spring 内建 Resource 实现

资源来源资源协议实现类
Bean 定义org.springframework.beans.factory.support.BeanDefinitionResource
数组org.springframework.core.io.ByteArrayResource
类路径class path:/org.springframework.core.io.ClassPathResource
文件系统file:/org.springframework.core.io.FileSystemResource
URLURL 支持的协议org.springframework.core.io.UrlResource
ServletContextorg.springframework.web.context.support.ServletContextResource

重要的是类路径、文件系统、URL 三种,这三种都实现 Resource 接口,也就是说 Spring 将从类路径、文件系统以及 URL 中获取数据抽象成了 Resource 接口,用一个接口的不同实现就可以读取不同的资源

11.5 Spring Resource 接口扩展

  • 可写资源

    • org.springframework.core.io.WritableResource

      • org.springframework.core.io.FileSystemResource

      • org.springframework.core.io.FileUrlResource(@Since 5.0.2)

      • org.springframework.core.io.PathResource(@Since 4.0 & @Deprecated)

  • 编码资源

    • org.springframework.core.io.support.EncodedResource

11.6 Spring 资源加载器

Resource 加载器

  • org.springframework.core.io.ResourceLoader

    • org.springframework.core.io.DefaultResourceLoader

      • org.springframework.core.io.FileSystemResourceLoader

      • org.springframework.core.io.ClassRelativeResourceLoader

      • org.springframework.context.support.AbstractApplicationContext

11.7 Spring 通配路径资源加载器

  • 通配路径

    • org.springframework.core.io.support.ResourcePatternResolver

      • org.springframework.core.io.support.PathMatchingResourcePatternResolver

  • 路径匹配器

    • org.springframework.util.PathMatcher

      • Ant 模式匹配实现——org.springframework.util.AntPathMatcher

11.8 Spring 通配路径资源扩展

  • 实现 org.springframework.util.PathMatcher

  • 重置 PathMathcer——PathMatchingResourcePatternResolver#setPathMatcher

11.9 依赖注入 Spring Resource

基于 @Value 实现:

// 注入单个 Resource
@Value("classpath:/...")
private Resource resource;
​
// 注入多个 Resource
@Value("classpath*:/*.properties")
private Resource[] resources;

11.10 依赖注入 ResourceLoader

  • 方法一:实现 ResourceLoaderAware 回调

  • 方法二:@Autowired 注入 ResourceLoader

  • 方法三:注入 ApplicationContext 作为 ResourceLoader

public class InjectingResourceLoaderDemo implements ResourceLoaderAware {
​
    private ResourceLoader aware;
​
    @Resource
    private ResourceLoader loader;
​
    @Resource
    private AbstractApplicationContext abstractContext;
​
    @Resource
    private ApplicationContext context;
​
    @PostConstruct
    public void init() {
        System.out.println("aware == loader? " + (aware == loader));
        System.out.println("aware == abstractContext? " + (aware == abstractContext));
        System.out.println("aware == context? " + (aware == context));
    }
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(InjectingResourceLoaderDemo.class);
​
        context.refresh();
​
        context.close();
    }
​
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.aware = resourceLoader;
    }
}
​
// 运行结果:
aware == loader? true
aware == abstractContext? true
aware == context? true

11.11 面试题

11.11.1 Spring 配置资源中有哪些常见类型

  • XML 资源

  • Properties 资源

  • YAML 资源

11.11.2 请列举不同类型 Spring 配置资源

  • XML 资源

    • 普通 Bean Definition XML 配置资源——*.xml

    • Spring Schema 资源——*.xsd

  • Properties 资源

    • 普通 Properties 格式资源——*.properties

    • Spring Handler 实现类映射文件——META-INF/spring.hanlders

    • Spring Schema 资源映射文件+META-INF/spring.schemas

  • YAML 资源

    • 普通 YAML 配置资源——*.yaml 或 *.yml

11.11.3 Java 标准资源管理扩展的步骤

  • 简易实现

    • 实现 URLStreamHandler 并放置在 sun.net.protocol.${protocl}.Handlers 包下

  • 自定义实现

    • 实现 URLStreamHandler

    • 添加 -Djava.protocol.handler.pkgs 启动参数,指向 URLStreamHandler 实现类的包下

  • 高级实现

    • 实现 URLStreamHandlerFactory 并传递到 URL 之中

12. Spring 国际化(i18n)

12.1 Spring 国际化使用场景

  • 普通国际化文案

  • Bean Validation 校验国际化文案

  • Web 站点页面渲染

  • Web MVC 错误消息提示

12.2 Spring 国际化接口

  • 核心接口

    • org.springframework.context.MessageSource

  • 主要概念

    • 文案模板编码(code)

    • 文案模板参数(args)

    • 区域(Locale)

12.3 层次性 MessageSource

  • Spring 层次性接口回顾

    • org.springframework.beans.factory.HierarchicalBeanFactory

    • org.springframework.context.ApplicationContext

    • org.springframework.beans.factory.config.BeanDefinition

  • Spring 层次性国际化接口

    • org.springframework.context.HierarchicalMessageSource

12.4 Java 国际化标准实现

核心接口

  • 抽象实现——java.util.ResourceBundle

  • Properties 资源实现——java.util.PropertyResourceBundle

  • 例举实现——java.util.ListResourceBundle

ResourceBundle 核心实现

  • Key-Value 设计

  • 层次性设计

  • 缓存设计

  • 字符编码控制——java.util.ResourceBundle.Control(@Since 1.6)

  • Contrl SPI 扩展——java.util.spi.ResourceBundleControlProvider(@Since 1.8)

12.5 Java 文本格式

  • 核心接口

    • java.text.MessageFormat(非线程安全,每次使用 new 一个实例来使用)

  • 基本用法

    • 设置消息格式模式——new MessageFormat(...)

    • 格式化——format(new Object[]{...})

  • 消息格式模式

    • 格式元素:{ArgumentIndex(,FormatType,(FormatStyle))}

    • FormatType:消息格式类型,可选项,每种类型在 number、date、time 和 choice 类型选其一

    • FormatStyle:消息格式风格,可选项,包括:short、medium、long、full、integer、currency、percent

  • 高级用法

    • 重置消格式模式

    • 重置 java.util.Locale

    • 重置 java.text.Format

12.6 MessageSource 开箱即用实现

  • 基于 ResourceBundle + MessageFormat 组合 MessageSource 实现

    • org.springframework.context.support.ResourceBundleMessageSource

  • 可重载 Properties + MessageFormat 组合 MessageSource 实现

    • org.springframework.context.support.ReloadableResourceBundleMessageSource

12.7 MessageSource 内建实现

MessageReource 内建 Bean 可能来源

  • 预注册 Bean 名称为 "messageSource",类型为:MessageSource Bean

  • 默认内建实现——DelegatingMessageSource(层次性查找 MessageSource 对象)

12.8 课外资料

Spring Boot 为什么要新建 MessageSource Bean?

  • AbstractApplciationContext 的实现决定了 MessageSource 内建实现

  • Spring Boot 通过外部化配置简化 MessageSource Bean 构建

  • Spring Boot 基于 Bean Validation 校验非常普遍

12.9 面试题

12.9.1 Spring 国际化接口有哪些?

  • 核心接口——MessageSource

  • 层次性接口——org.springframework.context.HierarchicalMessageSource

12.9.2 Spring 有哪些 MessageSource 内建实现

  • org.springframework.context.support.ResourceBundleMessageSource

  • org.springframework.context.support.ReloadableResourceBundleMessageSource

  • org.springframework.context.support.StaticMessageSource

  • org.springframework.context.support.DelegatingMessageSource

12.9.3 如何实现配置自动更新 MessageSource

主要技术:

  • Java NIO 2:java.nio.file.WatchService

  • Java Concurrency:java.util.concurrent.ExecutorService

  • Spring:org.springframework.context.support.AbstractMessageSource

13. Spring 校验

13.1 Spring 校验使用场景

  • Spring 常规校验(Validation)

  • Spring 数据绑定(DataBinder)

  • Spring Web 参数绑定(WebDataBinder)

  • Spring WebMVC/WebFlux 处理方法参数校验

13.2 Validator 接口设计

接口职责

  • Spring 内部校验器接口,通过编程的方式校验目标对象

核心方法

  • supports(Class):校验目标类能否校验

  • validate(Object,Errors):校验目标对象,并将校验失败的内容输出至 Errors 对象

配套组件

  • 错误收集器:org.springfamework.validation.Errors

  • Validator 工具类:org.springframework.validation.ValidationUtils

13.3 Errors 接口设计

接口职责

  • 数据绑定和校验错误收集接口,与 Java Bean 和其属性有强关联性

核心方法

  • reject 方法(重载):收集错误文案

  • rejectValue 方法(重载):收集对象字段中的错误文案

配套组件

  • Java Bean 错误描述:org.springfamework.validation.ObjectError

  • Java Bean 属性错误描述:org.springframework.validation.FieldError

13.4 Errors 文案来源

  • 选择 Errors 实现(如:org.springframework.validation.BeanPropertyBindingResult)

  • 调用 reject 或 rejectValue 方法

  • 获取 Errors 对象中 ObjectError 或 FieldError

  • 将 ObjectError 或 FieldError 中的code 和args,管理啊 MessageSource 实现(如:ResourceBundleMessageSource)

13.5 自定义 Validator

实现 org.springframework.validation.Validator 接口

  • 实现 supports 方法

  • 实现 validate 方法

    • 通过 Errors 对象收集错误

      • ObjectError:对象(Bean)错误

      • FieldError:对象(Bean)属性(Property)错误

    • 通过 ObjectError 和 FieldError 关联 MessageSource 实现获取最终文案

13.6 Validator 的救赎

Bean Validation 与 Validator 适配

  • 核心组件——org.springframework.validation.beanvalidation.LoacalValidationFactoryBean

  • 依赖 Bean Validation——JSR-303 or JSR-349 provider

  • Bean 方法参数校验——org.springframework.validation.beanvalidation.MethodValidationProcessor

13.7 面试题

13.7.1 Spring 校验接口是什么?

org.springframework.validation.Validator

13.7.2 Spring 有哪些校验核心组件

  • 检验器:org.springframework.validation.Validator

  • 错误收集器:org.springframework.validation.Errors

  • Java Bean 错误描述:org.springframework.validation.ObjectError

  • Java Bean 属性错误描述:org.springframework.validation.FieldError

  • Bean Validation 适配:org.springframework.validation.beaanvalidation.LocalValidatorFactoryBean

13.7.3 请通过示例演示 Spring Bean 的校验

后续章节详细介绍

14. Spring 数据绑定

14.1 Spring 数据绑定使用场景

  • Spring BeanDefinition 到 Bean 实例创建

  • Spring 数据绑定(DataBinder)

  • Spring Web 参数绑定(WebDataBinder)

14.2 Spring 数据绑定组件

  • 标准组件

    • org.springframework.validation.DataBinder

  • Web 组件

    • org.springframework.web.bind.WebDataBinder

    • org.springframework.web.bind.ServletRequestDataBinder

    • org.springframework.web.bind.WebRequestDataBinder

    • org.springframework.web.bind.WebExchangeDataBinder

DataBinder 核心属性

属性说明
target关联目标 Bean
objectName目标 Bean 名称
bindingResult属性绑定结果
typeConverter类型转换器
conversionService类型转换服务
messageCodesResolver校验错误文案 Code 处理器
validators关联的 Bean Validator 实例集合

DataBinder 绑定方法

  • bind(PropertyValues):将 PropertyValues Key-Value 内容映射到关联 Bean(target)中的属性上

    • 假设 PropertyValues 中包含 "name=小马哥"的键值对,同时 Bean 对象 User 中存在 name 属性,当 bing 方法执行时,User 对象中的 name 属性值将被绑定为 "小马哥"

14.3 Spring 数据绑定元数据

DataBinder 元数据——PropertyValues

特征说明
数据来源BeanDefinition,主要来源 XML 资源配置 BeanDefinition
数据结构由一个或多个 PropertyValue 组成
成员结构PropertyValue 包含属性名称,以及属性值(包括原始值、类型转换后的值)
常见实现MutablePropertyValues
Web 扩展实现ServletConfigPropertyValues、ServletRequestParameterPropertyValues
相关生命周期InstantiationAwareBeanPostProcessor#postProcessProperties

14.4 Spring 数据绑定控制参数

数据绑定特殊场景分析

  • 当 PropertyValues 中包含名称 x 的 PropertyValue,目标对象 B 不存在 x 属性,当 bind 方法执行时,会发生什么?

    • 忽略未知属性

  • 当 PropertyValues 中包含名称 x 的 PropertyValue,目标对象 B 中存在 x 属性,当 bind 方法执行时,如何避免 B 属性 x 不被绑定

  • 当 PropertyValues 中包含名称 x.y 的 PropertyValue,目标对象 B 中存在 x 属性(嵌套 y 属性),当 bind 方法执行时,会发生什么?

    • 支持嵌套属性,嵌套对象被创建并被填充

DataBinder 绑定控制参数

参数名称说明
ignoreUnkonwnFields是否忽略未知字段,默认值:true
ignoreInvalidFields是否忽略非法字段,默认值:false
autoGrowNestedPaths是否自动增加嵌套路径,默认值:true
allowedFields绑定字段白名单
disallowedFields绑定字段黑名单
requiredFields必须绑定字段

14.5 Spring 底层 Java Bean 替换实现

JavaBeans 核心实现——java.beans.BeanInfo

  • 属性(Property)

    • java.beans.PropertyEditor

  • 方法(Method)

  • 事件(Event)

  • 表达式(Expression)

Spring 替代实现——org.springframework.beans.BeanWrapper

  • 属性(Property)

    • java.beans.PropertyEditor

  • 嵌套属性路径(nested path)

14.6 BeanWrapper 的使用场景

  • Spring 底层 JavaBeans 基础设施的中心化接口

  • 通常不会直接使用,间接用于 BeanFactory 和 DataBinder

  • 提供标准 javaBeans 分析和操作,能够单独或批量存储 Java Bean 的属性(propertues)

  • 支持嵌套型路径(nested path)

  • 实现类 org.springframework.beans.BeanWrapperImpl

14.7 课外资料

标准 javaBeans 是如何操作属性的?

API说明
java.beans.IntrospectorJava Bean 内省 API
java.beans.BeanInfoJava Bean 元信息 API
java.beans.BeanDescriptorJava Bean 信息描述符
java.beans.PropertyDescriptorJava Bean 属性描述符
java.beans.MethodDescriptorJava Bean 方法描述符
java.beans.EventSetDescriptorJava Bean 事件集合描述符

14.8 DataBinder 数据校验

DataBinder 与 BeanWrapper

  • bind 方法生成 BeanPropertyBindingResult

  • BeanPropertyBindingResult 关联 BeanWrapper

14.9 面试题

14.9.1 Spring 数据绑定的 API 是什么?

org.springframework.DataBinder

14.9.2 BeanWrapper 和 JavaBeans 之间关系是?

BeanWrapper 是 Spring 底层 JavaBeans 基础设施的中心化接口

14.9.3 DataBinder 是怎么完成属性类型转换的?

下章揭晓

15. Spring 类型转换

15.1 Spring 类型转换的实现

  • 基于 JavaBeans 接口的类型转换实现

    • 基于 java.beans.PropertyEditor 接口扩展

  • Spring 3.0+ 通用类型转换实现

15.2 使用场景

场景基于 JavaBeans 接口的类型转换实现Spring 3.0+ 通用类型转换实现
数据绑定YESYES
BeanWrapperYESYES
Bean 属性类型转换YESYES
外部化属性类型转换NOYES

15.3 基于 JavaBeans 接口的类型转换

  • 核心职责

    • 将 String 类型的内容转化为目标类型的对象

  • 扩展原理

    • Spring 框架将文本内容传递到 PropertyEditor 实现的 setAsText(String) 方法

    • PropertyEditor#setAsText(String) 方法实现将 String 类型给转化为目标类型的对象

    • 将目标类型的对象传入 PropertyEditor#setValue(Object) 方法

    • PropertyEditor#setValue(Object) 方法实现需要临时存储传入对象

    • Spring 框架将通过 PropertyEditor#getValue() 获取类型转换后的对象

15.4 Spring 内建 PropertyEditor 扩展

内建扩展(org.springframework.beans.propertyeditors包下)

转换场景实现类
String -> Byte 数组org.springframework.beans.propertyeditors.ByteArrayPropertyEditor
String -> Charorg.springframework.beans.propertyeditors.CharacterEditor
String -> Char 数组org.springframework.beans.propertyeditors.CharArrayPropertyEditor
String -> Charsetorg.springframework.beans.propertyeditors.CharsetEditor
String -> Classorg.springframework.beans.propertyeditors.ClassEditor
String -> Currencyorg.springframework.beans.propertyeditors.CurrencyEditor
......

15.5 自定义 PropertyEditor 扩展

扩展模式

  • 扩展 java.beans.PropertyEditorSupport 类

实现 org.springframework.beans.PropertyEditorRegistrar

  • 实现 registerCustomEditors(org.springframework.beans.PropertyEditorRegistrar) 方法

  • 将 PropertyEditorRegistrar 实现注册为 Spring Bean

向 org.springframework.beans.PropertyEditorRegistrar 注册自定义 PropertyEditor 实现

  • 通用类型实现 registerCustomEditor(Class<?>, PropertyEditor)

  • Java Bean 属性类型实现:registerCustomEditor(Class<?>, String, PropertyEditor)

15.6 Spring PropertyEditor 的设计缺陷

  • 违反职责单一原则:java.beans.PropertyEditor 接口职责太多,除了类型转换,还包括 Java Beans 事件和 Java GUI 交互

  • java.beans.PropertyEditor 实现类型局限:来源类型只能为 java.lang.String 类型

  • java.beans.PropertyEditor 实现缺少类型安全:除了实现类命名可以表达语义,实现类无法感知目标类型转换

15.7 Spring 3 通用类型转换接口

类型转换接口——org.springframework.core.convert.Converter<S, T>

  • 泛型参数 S:来源类型,参数 T:目标类型

  • 核心方法:T convert(S)

通用类型转换接口_org.springframework.core.convert.converter.GenericConverter

  • 和新方法:convert(Object, TypeDescriptor, TypeDescriptor)

  • 配对类型:org.springframework.core.convert.converter.GenericConverter.ConvertiblePair

  • 类型描述:org.springfamework.core.convert.TypeDescriptor

15.8 Spring 内建类型转换器

转换场景实现类所在包名(package)
日期/时间相关org.springframework.format.datetime
Java 8 日期/时间相关org.springframework.format.datetime.standard
通用实现org.springframework.core.convert.support

15.9 Convertor 接口的局限性

局限一:缺少 Source Type 和 Target Type 前置判断

  • 应对:增加 org.springframework.core.convert.converter.ConditionalConverter 实现

局限二:仅能转换单一的 Source Type 和 Target Type

  • 应对:使用 org.springframework.core.convert.converter.。GenericConverter 代替

15.10 GenericConverter 接口

org.springframework.core.convert.converter.GenericConverter

核心要素说明
使用场景用于“复合”类型转换场景,比如 Collection、Map、数组等
转换范围Set<ConvertiblePair> getConvertibleTypes()
配对类型org.springframework.core.convert.converter.GenericConverter.ConvertiblePair
转换方法convert(Object, TypeDescriptor, TypeDescriptor)
类型描述org.springframework.core.convert.TypeDescriptor

15.11 优化 GenericConverter 接口

GenericConverter 局限性

  • 缺少 SourceType 和 TargetType 前值判断

  • 单一类型转换实现复杂

GenericConverter 优化接口——ConditionalGenericConverter

  • 复合类型转换:org.springframework.core.convert.converter.GenericConverter

  • 类型条件判断:org.springframework.core.convert.converter.ConditionalConverter

15.12 扩展 Spring 类型转换器

实现转换器接口

  • org.springframework.core.converter.Converter

  • org.springframework.core.converter.ConverterFactory

  • org.springframework.core.converter.GenericConverter

注册转换器实现

  • 通过 ConversionServiceFactoryBean Spring Bean

  • 通过 org.springframework.core.converter.ConvertsionService API

15.13 统一类型转换服务

org.springframework.core.converter.ConversionService

实现类型说明
GenericConversionService通用 ConversionService 模板实现,不内置转换器实现
DefaultConversionService基础 ConversionService 实现,内置常用转化器实现
FormattingConversionService通用 Formatter + GenericConversionService 实现,不内置转化器和 Formatter 实现
DefaultFormattingConversionServiceDefaultConversionService + 格式化实现(如:JSR-354 Money & Currency,JSR-310 Date-Time)

15.14 ConversionService 作为依赖

类型转换器底层接口——org.springframework.beans.TypeConverter

  • 起始版本:Spring 2.0

  • 核心方法——convertIfNecessary

  • 抽象实现——org.springframework.beans.TypeConverterSupport

  • 简单实现——org.springframework.beans.SimpleTypeConverter

类型转换器底层抽象实现——org.springframework.beans.TypeConverterSupport

  • 实现接口——org.springframework.beans.TypeConverter

  • 扩展实现——org.springframework.beans.PropertyEditorRegistrySupport

  • 委派实现——org.springframework.beans.TypeConverterDelegate

类型转换器底层委派实现——org.springframework.beans.TypeConverterDelegate

  • 构造来源——org.springframework.beans.AbstractNestablePropertyAccessor 实现

    • org.springframework.beans.BeanWrapperImpl

  • 依赖——java.beans.PropertyEditor

    • 默认内建实现——PropertyEditorRegistrySupport#registerDefaultEditor

  • 可选依赖——org.springframework.core.convert.ConversionService

AbstractApplicationContext -> "conversionService" ConversionService Bean -> ConfigurableBeanFactory$setConversionService(ConversionService) -> AbstractAutowireCapableBeanFactory.instantiateBean -> BeanDefinition -> BeanWrapper -> 属性转换(数据来源:PropertyValues)-> setPropertyValues(PropertyValue) -> TypeConverter#convertIfNecessary -> TypeConverterDelegate#convertIfNecessary -> PropertyEditor or ConversionService

15.15 面试题

15.15.1 Spring 类型转换实现有哪些

  1. 基于 JavaBeans PropertyEditor 接口实现

  2. Spring 3.0+ 通用类型转换实现

15.15.2 Spring 类型转换器接口有哪些

  • 类型转换接口——org.springframework.core.converter.Converter

  • 通用类型转换接口——org.springframework.core.converter.GenericConverter

  • 类型条件接口——org.springframework.core.converter.ConditionalConverter

  • 综合类型转换接口——org.springframework.core.converter.ConditionalGenericConverter

15.15.3 TypeDescriptor 是如何处理泛型的?

下章揭晓

16. Spring 泛型处理

16.1 Java 泛型基础

  • 泛型类型

    • 泛型类型是在类型上参数化的泛型类或接口

  • 泛型使用场景

    • 编译时强类型检查

    • 避免类型强转

    • 实现通用算法

  • 泛型类型擦写

    • 反省被引入到 Java 中,以便在编译时提供更严格的类型检查并支持泛型编程。类型擦除确保不会为参数化类型创建新类;因此,泛型不会产生运行时开销。为了实现泛型,编译器将类型擦除应用于:

      • 将泛型类型中的所有类型参数替换为其边界,如果类型参数是无边界的,则将其替换为"Object"。因此,生成的字节码只包含普通类、接口和方法

      • 必要时插入类型转换以保持类型安全

      • 生成桥方法以保留扩展泛型中的多态性

16.2 Java 5 类型接口

派生类或接口说明
java.lang.CalssJava 类 API,如 java.lang.String
java.lang.reflect.GenericArrayType泛型数组类型
java.lang.reflect.ParameterizedType泛型参数类型
java.lang.reflect.TypeVariable泛型类型变量,如 Collection<E> 中的 E
java.lang.reflect.WildcardType泛型通配类型
类型API
泛型信息(Generics Info)java.lang.Class#getGenericInfo()
泛型参数(Parameters)java.lang.reflect.ParameterizedTpe
泛型父类(Super Classes)Java.lang.Class#getGenericSuperClass()
泛型接口(interfaces)Java.lang.Class#getGenericInterface()
泛型声明(Generics Declaration)java.lang.reflectGenericDeclaration

16.3 Spring 泛型类型辅助类

核心 API——org.springframework.core.GenericTypeResolver

  • 版本支持:[2.5.2,)

  • 处理类型相关(Type)相关方法

    • resolveReturnType

    • resolveType

  • 处理泛型参数类型(ParameterizedType)相关方法

    • resolveReturnTypeArgument

    • resolveTypeArgument

    • resolveTypeArguments

  • 处理泛型类型变量(TypeVariable)相关方法

    • getTypeVariableMap

16.4 Spring 泛型集合类型辅助类

核心 API——org.springframework.core.GenericCollectionTypeResolver

  • 版本支持:[2.0, 4.3]

  • 替换实现:org.springframework.core.ResolvableType

  • 处理 Collection 相关

    • getCollection*Type

  • 处理 Map 相关

    • getMapKey*Type

    • getMapValue*Type

16.5 Spring方法参数封装——MrthodParameter

核心 API——org.springframework.core.MethodParameter

  • 起始版本:[2.0,)

  • 元信息

    • 关联的方法——Method

    • 关联的构造器——Constructor

    • 构造器或方法参数索引——parameterIndex

    • 构造器或方法参数类型——parameterType

    • 构造器或方法参数泛型类型——genericParameterType

    • 构造器或方法参数参数名称——parameterName

    • 所在类——containingClass

16.6 Spring 4.2 泛型优化实现——ResolvableType

核心 API——org.springframework.core.ResolvableType

  • 起始版本:[4.0,)

  • 扮演角色:GenericTypeResolver 和 GenericCollectionTypeResolver 替代者

  • 工厂方法:for* 方法

  • 转换方法:as* 方法

  • 处理方法:resolve* 方法

16.7 ResolvableType 的局限性

16.8 面试题

16.8.1 Java 泛型擦除发生在编译时还是运行时

运行时

16.8.2 请介绍 Java 5 Type 类型的派生类或接口

java.lang.Calss——Java 类 API,如 java.lang.String

java.lang.reflect.GenericArrayType——泛型数组类型

java.lang.reflect.ParameterizedType——泛型参数类型

java.lang.reflect.TypeVariable——泛型类型变量,如 Collection<E> 中的 E

java.lang.reflect.WildcardType——泛型通配类型

16.8.3 请说明 ResolvableType 的设计优势

  • 简化 Java 5 Type API 开发,屏蔽复杂 API 的运用,如 ParameterizedType

  • 不变性设计

  • Fluent API 设计(Builder 模式),链式(流式)编程

17. Spring 事件

17.1 Java 事件/监听器编程模型

  • 设计模式——观察者模式扩展

    • 可观察对象(消息发送者)——java.util.Observable

    • 观察者——java.util.Observer

  • 标准化接口

    • 事件对象——java.util.EventObject

    • 事件监听器——java.util.EventListener

public class ObserverDemo {
    public static void main(String[] args) {
        EventObservable observable = new EventObservable();
        EventObserver observer = new EventObserver();
        observable.addObserver(observer);
        observable.notifyObservers("Event");
    }
​
    static class EventObservable extends Observable {
        @Override
        public void notifyObservers(Object arg) {
            setChanged();
            super.notifyObservers(new EventObject(arg));
            clearChanged();
        }
    }
​
    static class EventObserver implements Observer, EventListener {
​
        @Override
        public void update(Observable o, Object arg) {
            EventObject event = (EventObject) arg;
            System.out.println("receive msg: " + event);
        }
    }
}

17.2 面向接口的事件

一般事件系统中事件需要继承 EventObject 类,监听器接口需要继承 EventListener 接口

17.3 面向注解的事件

17.4 Spring 标准事件——ApplicationEvent

Spring 应用上下文 ApplicationEvent 扩展——ApplicationContextEvent

  • Spring 应用上下文(ApplicationContext)作为事件源

  • 具体实现:

    • org.springframework.context.event.ContextClosedEvent

    • org.springframework.context.event.ContextRefreshedEvent

    • org.springframework.context.event.ContextStartedEvent

    • org.springframework.context.event.ContextStoppedEvent

继承关系:ContextRefreshedEvent 继承 ApplicationContextEvent 继承 ApplicationEvent 继承 ObjectEvent

其中 ApplicationEvent 新增 timestamp 属性记录时间

ApplicationContextEvent 则将 ApplicationContext 作为事件源,方便 Listener 中和 ApplicationContext 做交互

public abstract class ApplicationContextEvent extends ApplicationEvent {
​
    /**
     * Create a new ContextStartedEvent.
     * @param source the {@code ApplicationContext} that the event is raised for
     * (must not be {@code null})
     */
    public ApplicationContextEvent(ApplicationContext source) {
        super(source);
    }
​
    /**
     * Get the {@code ApplicationContext} that the event was raised for.
     */
    public final ApplicationContext getApplicationContext() {
        return (ApplicationContext) getSource();
    }
​
}

17.5 基于接口的 Spring 事件监听器

Java 标准事件监听器 java.util.EventListener 扩展

  • 扩展接口——org.springframework.context.ApplicationListener

  • 设计特点:单一类型事件处理

  • 处理方法:onApplicationEvent(ApplicationEvent)

  • 事件类型:org.springframework.context.ApplicationEvent

public class ApplicationListenerDemo {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
​
        context.addApplicationListener(e -> {
            if (e instanceof ContextRefreshedEvent) {
                System.out.println("refresh");
            } else if (e instanceof ContextClosedEvent) {
                System.out.println("close");
            }
        });
​
        context.refresh();
​
        context.close();
    }
}

17.6 基于注解的 Spring 事件监听器

Spring 注解 @org.springframework.context.event.EventListener

特性说明
设计特点支持多 ApplicationEvent 类型,无需接口约束
注解目标方法
是否支持异步执行支持
是否支持泛型类型事件支持
是否支持顺序控制支持,配合 @Order 注解控制
@EnableAsync
public class AnnotationApplicationListenerDemo {
​
    @EventListener
    @Order(2)
    public void listener(ContextRefreshedEvent event) {
        println("refresh");
    }
​
    @EventListener
    @Order(1)
    public void listener1(ContextRefreshedEvent event) {
        println("refresh1");
    }
​
    @EventListener
    @Async
    public void listener(ContextStartedEvent event) {
        println("start");
    }
​
    @EventListener
    public void listener(ContextClosedEvent event) {
        println("close");
    }
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationApplicationListenerDemo.class);
        context.refresh();
        context.start();
        context.close();
    }
​
    private static void println(String msg) {
        System.out.printf("[%s] handle %s%n", Thread.currentThread().getName(), msg);
    }
}
​
// 运行结果
[main] handle refresh1
[main] handle refresh
2月 19, 2022 9:15:42 下午 org.springframework.aop.interceptor.AsyncExecutionAspectSupport getDefaultExecutor
信息: No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either
[SimpleAsyncTaskExecutor-1] handle start
[main] handle close

17.7 注册 Spring ApplicationListener

  • ApplicationListener 作为 Spring Bean 注册

  • 通过 ConfigurableApplicationContext API 注册

public class SpringBeanApplicationListener {
​
    @Bean
    public ApplicationListener<ContextRefreshedEvent> myListener() {
        return new MyApplicationListener();
    }
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(SpringBeanApplicationListener.class);
        context.refresh();
        context.close();
    }
​
    static class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            System.out.println("my refresh");
        }
    }
}

17.8 Spring 事件发布器

  • 方法一:通过 ApplicationEventPublisher 发布 Spring 事件

    • 获取 ApplicationEventPublisher(依赖注入)

  • 方法二:通过 ApplicationEventMulticaster 发布 Spring 事件

    • 获取 ApplicationEventMuiltcaster

      • 依赖注入

      • 依赖查找

public class ApplicationPublisherDemo implements ApplicationEventPublisherAware {
​
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        applicationEventPublisher.publishEvent(new ApplicationEvent("hello world") {
        });
        applicationEventPublisher.publishEvent("generic object");
    }
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.addApplicationListener(System.out::println);
        context.register(ApplicationPublisherDemo.class);
        context.refresh();
        context.start();
        context.close();
    }
}

17.9 Spring 层次性上下文事件传播

当 Spring 应用出现多层次 Spring 应用上下文(ApplicationContext)时,如 Spring WebMVC、Spring Boot 或 Spring Cloud 场景下,由子 ApplicationContext 发起的 Spring 事件可能会传递到其 Parent ApplicationContext(直到 Root)的过程

应该如何避免事件传播呢?可通过定位 Spring 事件源(ApllicationEvent)进行过滤处理,可通过缓存已处理事件来解决

public class HierarchicalEventDemo {
    public static void main(String[] args) {
        // create parent context
        AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
        parent.setId("parent");
​
        // create sub context
        AnnotationConfigApplicationContext current = new AnnotationConfigApplicationContext();
        current.setId("current");
​
        current.setParent(parent);
​
        parent.register(MyListener.class);
        current.register(MyListener.class);
​
        parent.refresh();
        current.refresh();
​
        current.close();
        parent.close();
    }
}
​
// 运行结果:
receive ContextRefreshedEvent event from id [parent]
receive ContextRefreshedEvent event from id [current]
receive ContextClosedEvent event from id [current]
receive ContextClosedEvent event from id [parent]

解决了事件重复被消费的问题

17.10 Spring 内建事件

ApplicationContextEvent 派生事件

  • ContextRefreashEvent:Spring 应用上下文就绪事件——context#refreash

  • ContextStartedEvent:Spring 应用上下文启动事件——context#start

  • ContextStopedEvent:Spring 应用上下文停止事件——context#stop

  • ContextClosedEvent:Spring 应用上下文关闭事件——context#close

17.11 Spring 4.2 Payload 事件

Spring Payload 事件——org.springframework.context.PayloadApplicationEvent

  • 使用场景:简化 Spring 事件发送,关注事件源主体

  • 发送方法:ApplicationEventPublisher#publishEvent(java.lang.Object)

17.12 自定义 Spring 事件

  • 扩展 org.springframework.context.ApplicationEvent

  • 实现 org.springframework.context.ApplicationListener

  • 注册 org.springframework.context.ApplicationListener

17.13 依赖注入 ApplicationEventPublisher

  • 通过 ApplicationEventPublisherAware 回调接口

  • 通过 @Autowired ApplicationEventPublisher

17.14 依赖查找 ApplicationEventPublisher

查找条件:

  • Bean 名称:"applicationEventMulticaster"

  • Bean 类型:"org.springframework.context.event.ApplicationEventMulticaster"

17.15 ApplicationEventPublisher 底层实现

底层实现:

  • 接口:org.springframework.context.event.ApplicationEventMulticaster

  • 抽象类:org.springframework.context.event.AbstractApplicationEventMulticaster

  • 实现类:org.springframework.context.event.SimpleApplicationEventMulticaster

ApplicationContext 继承了 ApplicationEventPublisher 接口,在 AbstractApplicationContext 内部持有一个 ApplicationEventMulticaster 对象,因此 ApplicationContext 的发布事件的功能底层是委托给 ApplicationEventMulticaster 对象来实现的

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
​
    // Decorate event as an ApplicationEvent if necessary
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        applicationEvent = new PayloadApplicationEvent<>(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
        }
    }
​
    // Multicast right now if possible - or lazily once the multicaster is initialized
    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    }
    else {
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }
​
    // Publish event via parent context as well...
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
        }
        else {
            this.parent.publishEvent(event);
        }
    }
}

17.16 同步和异步 Spring 事件广播

  • 事件广播基于 org.springframework.context.event.SimpleApplicationEventMulticaster 类(全局影响)

    • 模式切换:setTaskExcutor(java.util.concurrent.Excutor) 方法

      • 默认模式:同步

      • 异步模式:如 java.util.concurrentThreadPoolExecutor

    • 设计缺陷:非基于接口契约编程

  • 基于注解——@org.springframework.context.event.EventListener(局部影响)

    • 模式切换

      • 默认模式:同步

      • 异步模式:标注 @org.springframework.scheduling.annotation.Async

    • 实现限制:无法直接实现同步/异步动态切换

17.17 Spring 4.1 事件异常处理

Spring 3.0 错误处理接口——org.springframework.util.ErrorHandler

  • 使用场景

    • Spring 事件(Events)

      • SimpleApplicationEventMulticaster Spring 4.1 开始支持

    • Spring 本地调度(Scheduling)

      • org.springframework.scheduling.concurrent.ConcurrentTaskScheduler

      • org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler

17.18 Spring 事件/监听器实现原理

核心类——org.springframework.context.event.SimpleApplicationEventMulticaster

  • 设计模式:观察者模式扩展

    • 被观察者——org.springframework.context.ApplicationListener

      • API 添加

      • 依赖查找

    • 通知对象——org.springframework.context.ApplicationEvent

  • 执行模式:同步/异步

  • 异常处理:org.springframework.util.ErrorHandler

  • 泛型处理:org.springframework.core.ResolvableType

17.19 课外资料

Spring Boot 事件

事件类型发生时机
ApplicationStartingEvent当 Spring Boot 应用启动时
ApplicaitonStartedEvent当 Spring Boot 应用已启动时
ApplicationEnvironmentPreparedEvent当 Spring Boot Environment 实例已准备时
ApplicationPreparedEvent当 Spring Boot 应用预备时
ApplicationReadyEvent当 Spring Boot 应用完全可用时
ApplicationFailedEvent当 Spring Boot 应用启动失败时

Spring Cloud 事件

事件类型发生时机
EnvironmentChangeEvent当 Environment 示例配置属性发生变化时
HeartbeatEvent当 DiscoveryClient 客户端发送心跳时
InstancePreRegisteredEvent当服务实例注册前
InstanceRegisteredEvent当服务实例注册后
RefreshEvent当 RefreshEndpoint 被调用时
RefreshScopeRefreshedEvent当 Refresh Scope Bean 刷新后

17.20 面试题

17.20.1 Spring 事件核心接口/组件

  • Spring 事件——org.springframework.context.ApplicationEvent

  • Spring 事件监听器——org.springframework.context.ApplicationListener

  • Spring 事件发布器——org.springframework.context.ApplicationEventPublisher

  • Spring 事件广播器——org.springframework.context.event.ApplicationEventMulticaster

17.20.2 Spring 同步和异步事件处理的使用场景

  • Spring 同步事件——绝大多数 Spring 使用场景,如 ContextRefreshedEvent

  • Spring 异步事件——主要 @EventListener 与 @Async 配合,实现异步处理,不阻塞主线程,比如长时间的数据计算任务等,不要轻易调整 SimpleApplicationEventMulticaster 中关联的 taskExecutor 对象,除非使用者非常了解 Spring 的事件机制,否则容易出现异常行为

17.20.3 @EventListener 的工作原理

18. Spring 注解

18.1 Spring 注解驱动编程发展历程

  • 注解驱动启蒙时代:Spring Framework 1.x

  • 注解驱动过度时代:Spring Framework 2.x

  • 注解驱动黄静时代:Spring Framework 3.x

  • 注解驱动完善时代:Spring Framework 4.x

  • 注解驱动当下时代:Spring Framework 5.x

18.2 Spring 核心注解场景分类

Spring 模式注解

Spring 注解场景说明起始版本
@Repository数据仓储模式注解2.0
@Component通用组件模式注解2.5
@Service服务模式注解2.5
@ControllerWeb 控制器模式注解2.5
@Configuration配置类模式注解3.0

装配注解

Spring 注解场景说明起始版本
@ImportResource替换 XML 元素 <import>2.5
@Import导入 Configuration 类2.5
@ComponentScan扫描指定 package 下标注 Spring 模式注解的类3.1

依赖注入注解

Spring 注解场景说明起始版本
@AutowiredBean 依赖注入,支持多种依赖查找方式2.5
@Qulifier细粒度的 @Autowired 依赖查找2.5

18.3 Spring 注解编程模型

编程模型:

  • 元注解(Meta-Annotation)

  • Spring 模式注解(Stereotype Annotation)

  • Spring 组合注解(Composed Annotations)

  • Spring 注解属性别名和覆盖(Attribute Alias and Overrides)

18.4 Spring 元注解

元注解是负责对其它注解进行说明的注解,自定义注解时可以使用元注解。 Java 5 定义了4个注解,分别是 @Documented、@Target、@Retention 和 @Inherited。 Java 8 又增加了 @Repeatable 和 @Native 两个注解。

18.5 Spring 模式注解

理解 @Component "派生性":原标注 @Component 的注解在 XML 元素 <context:component-scan> 或注解 @ComponentScan 扫描中 "派生"了 @Component 的特性,并且从 Spring Framework 4.0 开始支持多层次性"派生性"。

举例说明:

  • @Repository

  • @Service

  • @Controller

  • @Configuration

  • @SpringBootConfiguration(SpringBoot)

@Component 派生性原理

  • 核心组件——orgspringframework.context.annotation.ClassPathBeanDefinitionScanner

    • orgspringframework.context.annotation.ClassPathScanningCandidateComponentProvider

  • 资源处理——org.springframework.core.io.support.ResourcePatternResolver

  • 资源-类元信息

    • org.springframework.core.type.classreading.MetadataReaderFactory

  • 类元信息——orgspringframework.core.type.ClassMetadata

    • ASM 实现——org.springframework.core.type.classreading.ClassMetadataReadingVisitor

    • 反射实现——org.springframework.core.type.StandardAnnotationMetadata

  • 注解元信息——orgspringframework.core.type.AnnotationMetadata

    • ASM 实现——org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor

    • 反射实现——org.springframework.core.type.StandardAnnotationMetadata

18.6 Spring 组合注解

Spring 组合注解(Component Annotations) 中的元标注允许是 Spring 模式注解(Stereotype Annotation)与其他 Spring 功能性注解的任意组合。

18.7 Spring 注解属性别名

显性别名、隐形别名、传递性别名

18.8 Spring 注解属性覆盖

18.9 Spring @Enable 模块驱动

@Enable 模块驱动:@Enable 模块驱动是以 @Enable 为前缀的注解驱动编程模型。所谓"模块"是指具备相同领域的功能组件集合,组合所形成一个独立的单元。比如 Web MVC 模块、AspectJ 代理模块、Caching(缓存)模块、JMX(Java 管理扩展)模块、Async(异步处理)模块等。

举例说明:

  • @EnableWebMvc

  • @EnableTransactionManagement

  • @EnableCaching

  • @EnableMBeanExport

  • @EnableAsync

@Enable 模块驱动编程模式

  • 驱动注解:@EnableXXX

  • 导入注解:@Import 具体实现

  • 具体实现:

    • 基于 Configuration Class

    • 基于 ImportSelector 接口实现

    • 基于 ImportBeanDefinitionRegistrar 接口实现

18.10 Spring 条件注解

基于配置条件注解——@org.springframewok.context.annotation.Profile

  • 关联对象——org.springframework.core.Environment 中的 Profiles

  • 实现变化:从 Spring 4.0 开始,@Profile 基于 @Confitional 实现

基于变成条件注解——@org.springframework.context.annotation.Conditional

  • 关联对象——org.springframewok.context.annotation.Condition 具体实现

@Conditional 实现原理:

  • 上下文对象——org.springframewok.context.annotation.ConditionalContext

  • 条件判断——org.springframewok.context.annotation.ConditionEvaluator

  • 配置阶段——org.springframewok.context.annotation.ConfigurationCondition.ConfigurationPhase

  • 判断入口——org.springframewok.context.annotation.ConfigurationClassPostProcessor

    • org.springframewok.context.annotation.ConfigurationClassParser

18.11 课外资料

Spring Boot 注解

注解场景说明起始版本
@SpringBootConfigurationSpring Boot 配置类1.4.0
@SpringBootApplicationSpring Boot 应用引导注解1.2.0
@EnableAutoConfigutaionSppring Boot 激活自动转配1.0.0

Spring Cloud 注解

注解场景说明起始版本
@SpringCloudApplicationSpring Cloud 应用引导注解1.0.0
@EnableDiscoveryClientSpring Cloud 激活服务发现客户端注解1.0.0
@EnableCircuitBreakerSpring Cloud 激活熔断注解1.0.0

18.12 面试题

18.12.1 Spring 模式注解有哪些

  • @org.springframework.stereotype.Component

  • @org.springframework.stereotype.Repository

  • @org.springframework.stereotype.Service

  • @org.springframework.stereotype.Controller

  • @org.springframework.stereotype.Configuration

18.12.2 @EventListener 的工作原理

18.12.3 @PropertySource 的工作原理

下章揭晓

19. Spring Environment 抽象

19.1 理解 Spring Environment 抽象

  • 统一的 Spring 配置属性管理

    Spring Framework 3.1 开始引入 Environment 抽象,它统一 Spring 配置属性的存储,包括占位符处理和类型转换,不仅完整地替换 PropertyPlaceholderConfigurer,而且还支持更丰富的配置属性源(PropertySource)

  • 条件化 Spring Bean 装配管理

    通过 Environment Profiles 信息,帮助 Spring 容器提供条件话地装配 Bean

19.2 Spring Environment 接口使用场景

  • 用于属性占位符处理

  • 用于转换 Spring 配置属性类型

  • 用于存储 Spring 配置属性源(PropertySource)

  • 用于 Profiles 状态维护

19.3 Environment 占位符处理

Spring 3.1 前占位符处理

  • 组件:org.springframework.beans.factory.config.PropertyPlaceholderConfigurer

  • 接口:org.springframework.util.StringValueResolver

Spring 3.1 后占位符处理

  • 组件:org.springframework.context.support.PropertySourcesPlaceholderConfigurer

  • 接口:org.springframework.beans.factory.config.EmbeddedValueResolver

19.4 理解条件配置 Spring Profiles

Spring 3.1 条件配置

  • API:org.springframework.core.env.ConfigurableEnvironment

    • 修改:addActiveProfile(String)、setActiveProfile(String ...) 和 setDefaultProfile(String ...)

    • 获取:getActiveProfiles() 和 getDefaultProfiles()

    • 匹配:$acceptsProfiles(String ...) 和 acceptsProfiles(Profiles)

  • 注解:@org.springframework.context.annotation.Profile

可以通过 -Dspring.profiles.active 来指定 activeProfile

19.5 Spring 4 重构 @Profile

基于 Spring 4 org.springframework.context.annotation.Condition 接口实现

  • org.springframework.context.annotation.ProfileCondition

19.6 依赖注入 Environment

直接依赖注入

  • 通过 EnvironmentAware 接口回调

  • 通过 @Autowired 注入 Environment

间接依赖注入

  • 通过 ApplicationContextAware 接口回调

  • 通过 @Autowired 注入 ApplicationContext

19.7 依赖查找 Environment

直接依赖查找

  • 通过 org.springframework.context.ConfigurableApplicationContext#ENVIRONMENT_BEAN_NAME

间接依赖查找

  • 通过 org.springframework.context.ConfigurableApplicationContext#getEnvironment

19.8 依赖注入 @Value

通过注入 @Value

  • 实现——org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor

19.9 Spring 类型转换在 Environment 中的运用

Environment 底层实现

  • 底层实现——org.springframework.core.env.PropertySourcesPropertyResolver

    • 核心方法——convertValueIfNecessary(Object, Class)

  • 底层服务——org.springframework.core.convert.ConversionService

    • 默认实现——org.springframework.core.convert.support.DefaultConversionService

19.10 Spring 类型转换在 @Value 中的运用

@Value底层实现

  • 底层实现——org.springframework.beans.factory.annotation.AutoworedAnnotationBeanPostProcessor

    • org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency

  • 底层服务——org.springframework.beans.TypeConverter

    • 默认实现——org.springframework.beans.TypeConverterDelegate

      • java.beans.PropertyEditor

      • org.springframework.core.convert.ConversionService

19.11 Spring 配置属性源 PropertySource

API

  • 单配置属性源——org.springframework.core.env.PropertySource

  • 多配置属性源——org.springframework.core.env.PropertySources

注解

  • 单配置属性源——@org.springframework.context.annotation.PropertySource

  • 多配置属性源——@org.springframework.context.annotation.PropertySources

关联

  • 存储对象——org.springframework.core.env.MutablePropertySources

  • 关联方法——org.springframework.core.env.ConfigurableEnvironment#getPropertySources()

19.12 Spring 内建的配置属性源

内建 PropertySource

PropertySource 类型说明
org.springframework.core.env.CommandLinePropertySource命令行配置属性源
org.springframework.jndi.JndiPropertySourceJNDI 配置属性源
org.springframework.core.env.PropertySourceProperties 配置属性源
org.springframework.web.context.support.ServletConfigPropertySourceServlet配置属性源
org.springframework.web.context.support.ServletContextPropertySourceServletContext 配置属性源
org.springframework.core.env.SystemEnvironmentPropertySource环境变量配置属性源
……

19.13 基于注解扩展 Spring 配置属性源

@org.springframework.context.annotation.PropertySource 实现原理

  • 入口——org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

    • org.springframework.context.annotation.ConfigurationClassParser#processPropertySource

  • 4.3 新增语义

    • 适配属性字符编码——encoding

    • org.springframework.core.io.support.PropertySourceFactory

  • 适配对象——org.springframework.core.env.CompositePropertySource

19.14 基于 API 扩展 Spring 配置属性源

  • Spring 应用上下文启动前装配 PropertySource

  • Spring 应用上下文启动后装配 PropertySource

19.15 课外资料

Spring 4.1 测试配置属性源——@TestPropertySource(优先级高于@PropertySource)

19.16 面试题

19.16.1 简单介绍 Spring Environment 接口

  • 核心接口——org.springframework.core.env.Environment

  • 父接口——org.springframework.core.env.PropertyResolver

  • 可配置接口——org.springframework.core.env.ConfigurabelRnvironment

  • 职责:

    • guanli Spring 配置属性源

    • 管理 Profiles

19.16.2 如何控制 PropertySource 优先级

可以通过 api addFirst、addLast 等进行控制

19.16.3 Environment 完整的生命周期是怎样的?

下章揭晓

20. Spring 应用上下文生命周期

20.1 Spring 应用上下文启动准备阶段

AbstractApplicationContext#prepareRefresh 方法

  • 启动时间——startupDate

  • 状态标识——closed(false)、active(true)

  • 初始化 PropertySource——initPropertySources()(扩展,初始化 Environment 并且初始化 Environment 关联的 PropertySource)

  • 检验 Environment 中必须的属性

  • 初始化事件监听器集合

  • 初始化早期 Spring 事件集合

20.2 BeanFactory 创建阶段

AbstractApplicationContext#obtainFreshBeanFactory 方法

  • 刷新 Spring 应用上下文底层 BeanFactory——refreshBeanFactory

    • 销毁或关闭 BeanFactory,如果已经存在的话

    • 创建 BeanFactory——createBeanFactory

    • 设置 BeanFactory ID

    • 设置"是否允许 BeanDefinition 重复定义"——customizeBeanFactory(DefaultListableBeanFactory)

    • 设置"是否允许循环引用(依赖)"——customizeBeanFactory(DefaultListableBeanFactory)

    • 加载 BeanDefinition——loadBeanDefinitions(DefaultListableBeanFactory)

    • 关联新建 BeanFactory 到 Spring 应用上下文

  • 返回 Spring 应用上下文底层 BeanFactory——getBeanFactory

20.3 BeanFactory 准备阶段

AbstractApplicationContext#prepareBeanFactory(ConfigurableListableBeanFactory)

  • 关联 ClassLoader

  • 设置 Bean 表达式处理器

  • 添加 PropertyEditorRegister 实现——ResourceEditorRegistrar

  • 添加 Aware 回调接口 BeanPostProcessor 实现——ApplicationContextAwareProcessor

  • 忽略 Aware 回调接口作为依赖注入接口

  • 注册 ResolvableDependency 对象——BeanFactory、ResourceLoader、ApplicationEventPublisher 以及 ApplicationContext

  • 注册 ApplicationListenerDetector 对象

  • 注册 LoadTimeWeaverAwareProcessor 对象

  • 注册单例对象——Environment、Java System Properites 以及 OS 环境变量

20.4 BeanFactory 后置处理阶段

AbstractApplicationContext#postProcessBeanFactory(ConfigurableListableBeanFactory)

  • 子类覆盖该方法

AbstractApplicationContext#invokeBeanFactoryPostProcessor(ConfigurableListableBeanFactory)

  • 调用 BeanFactoryPostProcessor 或 BeanDefinitionRegistry 后置处理方法

  • 注册 LoadTimeWeaverAwareProcessor 对象

20.5 BeanFactory 注册 BeanPostProcessor 阶段

AbstractApplicationContext#registerBeanPostProcessors(ConfigurableListableBeanFactory)

  • 注册 PriorityOrdered 类型的 BeanPostProcessor Beans

  • 注册 Ordered 类型的 BeanPostProcessor Beans

  • 注册普通 BeanPostProcessor Beans

  • 注册 MergedBeanDefinitionPostProcessor Beans

  • 注册 ApplicationListenerDetector 对象

20.6 初始化内建 Bean:MessageSource

AbstractApplicationContext#initMessageSource

20.7 初始化内建 Bean:Spring 事件广播器

AbstractApplicationContext#initApplicationEventMulticaster

20.8 Spring 应用上下文刷新阶段

AbstractApplicationContext#onRefresh(空实现,需要子类实现)

  • 子类覆盖该方法

20.9 Spring 事件监听器注册阶段

AbstractApplicationContext#registerListeners

  • 添加当前引用上下文所关联的 ApplicationListener 对象

  • 添加 BeanFactory 所注册的 ApplicaitonListener Beans

  • 广播早期 Spring 事件

20.10 BeanFactory 初始化完成阶段

AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory)

  • BeanFactory 关联 ConversionService Bean,如果存在

  • 添加 StringValueResolver 对象

  • 依赖查找 LoadTimeWeaverAware Bean

  • BeanFactory 临时 ClassLoader 置为 null

  • BeanFactory 冻结配置

  • BeanFactory 初始化非延迟单例 Beans

20.11 Spring 应用上下文刷新完成阶段

AbstractApplicationContext#finishRefreash

  • 清除 ResourceLoader 缓存——clearResourceCaches

  • 初始化 LifecycleProcessor 对象——initLifecycleProcessor

  • 调用 LifecycleProcessor#onRefeash 方法

  • 发布 Spring 应用上下文已刷新事件——ContextRefreashedEvent

  • 向 MBeanServer 托管 Live Beans

20.12 Spring 应用上下文启动完成阶段

20.13 Spring 应用上下文启动阶段(start 方法)

AbstractApplicationContext#start

  • 启动 LifecycleProcessor

    • 依赖查找 Lifecycle Beans

    • 启动 Lifecycle Beans

  • 发布 Spring 应用上下文已启动事件——ContextStartedEvent

20.14 Spring 应用上下文停止阶段

AbstractApplicationContext#stop

  • 停止 LifecycleProcessor

    • 依赖查找 Lifecycle Beans

    • 停止 Lifecycle Beans

  • 发布 Spring 应用上下文已启动事件——ContextStoppedEvent

20.15 Spring 应用上下文关闭阶段

AbstractApplicationContext#close

  • 状态标识:active(false)、closed(true)

  • Live Beans JMX 撤销托管

    • LiveBeansView.unregisterApplicationContext(configurableApplicationContext)

  • 发布 Spring 应用上下文已关闭事件——ContextClosedEvent

  • 关闭 LifecycleProcessor

    • 依赖查找 Lifecycle Beans

    • 停止 Lifecycle Beans

  • 销毁 Spring Beans

  • 关闭 BeanFactory

  • 回调 onClose

  • 注册 Shutdown Hook 线程(如果曾注册)

20.16 面试题

20.16.1 Spring 应用上下文生命周期有哪些阶段

  • 刷新阶段——ConfigurableApplicationContext#refresh

  • 启动阶段——ConfigurableApplicationContext#start

  • 停止阶段——ConfigurableApplicationContext#stop

  • 关闭阶段——ConfigurableApplicationContext#close

20.16.2 Environment 完整的生命周期是怎样的?

20.16.3 Spring 应用上下文生命周期执行动作

21 加餐

21.1 为什么说 ObjectFactory 提供的是延迟依赖查找

  • 原因

    • ObjectFactory(或ObjectProvider)可关联某一类型 Bean

    • ObejctFactory 和 ObjectProvider 对象在被依赖注入和依赖查询时并未试试查找关联类型的 Bean

    • 当 ObjectFactory(或 ObjectProvider)调用 getObject 方法时,目标 Bean 才被依赖查找

  • 总结

    • ObjectFactory(或 ObjectProvider)相当于某一类型 Bean 依赖查找对象

21.2 依赖查找(注入)的 Bean 会被缓存吗?

  • 单例 Bean(Singleton)——会缓存

    • 缓存位置:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#singletonObjects

  • 原型 Bean(Prototype)——不会缓存

    • 当以来查询或依赖注入时,根据 BeanDefinition 每次创建

  • 其他 Scope Bean

    • request:每个 ServletRequest 内部缓存,生命周期维持在每次 HTTP 请求

    • session:每个 HttpSession 内部缓存,生命周期维持在每个用户 HTTP 会话

    • application:当前 Servlet 应用内部缓存

21.3 @Bean 的处理流程是怎样的?

  • 解析范围——Configuration Class 中的 @Bean 方法

  • 方法类型——静态 @Bean 方法和实例 @Bean 方法

21.4 BeanFactory 如何处理循环依赖?

  • 预备知识

    • 循环依赖开关(方法)——AbstractAutowireCapableBeanFactory#setAllowCirclarReferences

    • 单例工厂(属性)——DefaultSingletonBeanRegistry#singletonFactories

    • 获取早期未处理 Bean (方法)——AbstractAutowireCapableBeanFactory#getEarlyBeanReference

    • 早期未处理 Bean (属性)——DefaultSingletonBeanRegistry#earlySingletonObjects

  • 案例分析

三个 Map:singletonObject、singletonFactories、earlySingletonObjects

21.5 MyBatis 与 Spring Feamework 是如何集成的?

AbstractAutowiredCapableBeanFactory:populateBean

DefaultListableBeanFacotry:doResolveDependency(解析占位符)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值