• 20201108 小马哥讲Spring核心编程思想


    第一章:Spring Framework总览(Overview)

    Spring Framework 版本 Java 标准版 Java 企业版
    1.x 1.3+ J2EE 1.3 +
    2.x 1.4.2+ J2EE 1.3 +
    3.x 5+ J2EE 1.4 和 Java EE 5
    4.x 6+ Java EE 6 和 7
    5.x 8+ Java EE 7

    Spring 编程模型:

    • 面向对象编程

      • 契约接口:Aware、BeanPostProcessor ...

      • 设计模式:观察者模式、组合模式、模板模式 ...

      • 对象继承:Abstract* 类

    • 面向切面编程

      • 动态代理:JdkDynamicAopProxy
      • 字节码提升:ASM、CGLib、AspectJ...
    • 面向元编程

      • 注解:模式注解(@Component、@Service、@Respository ...)
      • 配置:Environment 抽象、PropertySources、BeanDefinition ...
      • 泛型:GenericTypeResolver、ResolvableType ...
    • 函数驱动

      • 函数接口:ApplicationEventPublisher
      • Reactive:Spring WebFlux
    • 模块驱动

      • Maven Artifacts
      • OSGI Bundles
      • Java 9 Automatic Modules
      • Spring @Enable*

    第二章:重新认识 IoC

    IoC 容器的职责:

    • 通用职责
      • 依赖处理
        • 依赖查找
        • 依赖注入
      • 生命周期管理
        • 容器
        • 托管的资源(Java Beans 或其他资源)
      • 配置
        • 容器
        • 外部化配置
        • 托管的资源(Java Beans 或其他资源)

    Spring 作为 IoC 容器的优势:

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

    第三章:Spring IoC 容器概述

    Spring IoC 依赖查找:

    • 根据 Bean 名称查找
      • 实时查找
      • 延迟查找
    • 根据 Bean 类型查找
      • 单个 Bean 对象
      • 集合 Bean 对象
    • 根据 Bean 名称 + 类型查找
    • 根据 Java 注解查找
      • 单个 Bean 对象
      • 集合 Bean 对象

    Spring IoC 依赖注入:

    • 根据 Bean 名称注入
    • 根据 Bean 类型注入
      • 单个 Bean 对象
      • 集合 Bean 对象
    • 注入容器內建 Bean 对象
    • 注入非 Bean 对象
    • 注入类型
      • 实时注入
      • 延迟注入

    延迟查找和延迟注入,使用到接口 org.springframework.beans.factory.ObjectProviderorg.springframework.beans.factory.ObjectFactoryObjectProvider 继承 ObjectFactory

    BeanFactoryApplicationContext 谁才是 Spring IoC 容器?

    • BeanFactory 是 Spring 底层 IoC 容器
    • ApplicationContext 是具备应用特性的 BeanFactory 超集
    • BeanFactory 是基本的 IoC 容器,ApplicationContext 实现 BeanFactory 接口,并在内部使用 ConfigurableListableBeanFactory 实现接口方法。

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

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

    第四章:Spring Bean 基础

    什么是 BeanDefinition

    • org.springframework.beans.factory.config.BeanDefinition
    • BeanDefinition 是 Spring Framework 中定义 Bean 的配置元信息接口,包含:
      • Bean 的类名
      • Bean 行为配置元素,如作用域、自动绑定的模式,生命周期回调等
      • 其他 Bean 引用,又可称作合作者(collaborators)或者依赖(dependencies)
      • 配置设置,比如 Bean 属性(Properties)
    • BeanDefinition 构建
      • 通过 BeanDefinitionBuilder
      • 通过 AbstractBeanDefinition 以及派生类

    Bean 名称生成器:org.springframework.beans.factory.support.BeanNameGenerator

    注册 Spring Bean:

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

    Bean 实例化(Instantiation)

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

    Bean 初始化(Initialization),同时存在时,顺序从上到下:

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

    Bean 延迟初始化(Lazy Initialization)

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

    Bean 销毁(Destroy),同时存在时,顺序从上到下:

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

    第五章:Spring IoC 依赖查找

    单一类型依赖查找接口 - BeanFactory

    • 根据 Bean 名称查找
      • getBean(String)
      • Spring 2.5 覆盖默认参数:getBean(String,Object...)
    • 根据 Bean 类型查找
      • Bean 实时查找
        • Spring 3.0 : getBean(Class)
        • Spring 4.1 覆盖默认参数:getBean(Class,Object...)
      • Spring 5.1 Bean 延迟查找
        • getBeanProvider(Class)
        • getBeanProvider(ResolvableType)
    • 根据 Bean 名称 + 类型查找:getBean(String,Class)

    集合类型依赖查找接口 - ListableBeanFactory

    • 根据 Bean 类型查找
      • 获取同类型 Bean 名称列表
        • getBeanNamesForType(Class)
        • Spring 4.2 getBeanNamesForType(ResolvableType)
      • 获取同类型 Bean 实例列表
        • getBeansOfType(Class) 以及重载方法
    • 通过注解类型查找
      • Spring 3.0 获取标注类型 Bean 名称列表
        • getBeanNamesForAnnotation(Class<? extends Annotation>)
      • Spring 3.0 获取标注类型 Bean 实例列表
        • getBeansWithAnnotation(Class<? extends Annotation>)
      • Spring 3.0 获取指定名称 + 标注类型 Bean 实例
        • findAnnotationOnBean(String,Class<? extends Annotation>)

    层次性依赖查找接口 - HierarchicalBeanFactory

    • 双亲 BeanFactory:getParentBeanFactory()
    • 层次性查找
      • 根据 Bean 名称查找
        • 基于 containsLocalBean 方法实现
      • 根据 Bean 类型查找实例列表
        • 单一类型:BeanFactoryUtils#beanOfType
        • 集合类型:BeanFactoryUtils#beansOfTypeIncludingAncestors
      • 根据 Java 注解查找名称列表
        • BeanFactoryUtils#beanNamesForTypeIncludingAncestors

    Bean 延迟依赖查找接口

    • org.springframework.beans.factory.ObjectFactory
    • org.springframework.beans.factory.ObjectProvider
      • Spring 5 对 Java 8 特性扩展
        • 函数式接口
          • getIfAvailable(Supplier)
          • ifAvailable(Consumer)
        • Stream 扩展 - stream()

    依赖查找安全性对比:

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

    注意:层次性依赖查找的安全性取决于其扩展的单一或集合类型的 BeanFactory 接口

    AbstractApplicationContext 内建可查找的依赖

    Bean 名称 Bean 实例 使用场景
    environment Environment 对象 外部化配置以及 Profiles
    systemProperties java.util.Properties 对象 Java 系统属性
    systemEnvironment java.util.Map 对象 操作系统环境变量
    messageSource MessageSource 对象 国际化文案
    lifecycleProcessor LifecycleProcessor 对象 Lifecycle Bean 处理器
    applicationEventMulticaster ApplicationEventMulticaster 对 象 Spring 事件广播器

    注解驱动 Spring 应用上下文内建可查找的依赖

    Bean 名称 Bean 实例 使用场景
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor ConfigurationClassPostProcesso 处理 Spring 配置类
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor AutowiredAnnotationBeanPostProcessor 对象 处理 @Autowired 以及 @Value 注解
    org.springframework.context.annotation.internalCommonAnnotationProcessor CommonAnnotationBeanPostProcessor 对象 (条件激活)处理 JSR-250 注解,如 @PostConstruct 等
    org.springframework.context.event.internalEventListenerProcessor EventListenerMethodProcessor 对象 处理标注 @EventListener 的 Spring 事件监听方法
    org.springframework.context.event.internalEventListenerFactory DefaultEventListenerFactory 对象 @EventListener 事件监听方法适配为 ApplicationListener
    org.springframework.context.annotation.internalPersistenceAnnotationProcessor PersistenceAnnotationBeanPostProcessor 对象 (条件激活)处理 JPA 注解场景

    依赖查找中的经典异常,BeansException 子类型

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

    BeanFactory.getBean 方法的执行是线程安全的,超过过程中会增加互斥锁

    第六章:Spring IoC依赖注入(Dependency Injection)

    依赖注入的模式和类型

    • 手动模式 - 配置或者编程的方式,提前安排注入规则
      • XML 资源配置元信息
      • Java 注解配置元信息
      • API 配置元信息
    • 自动模式 - 实现方提供依赖自动关联的方式,按照內建的注入规则
      • Autowiring(自动绑定)

    依赖注入类型

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

    自动绑定(Autowiring)模式,Autowiring modes

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

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

    Java 注解配置元信息

    • @Autowired

    • @Resource

    • @Inject

      可选,需要环境中存在 JSR-330 依赖

      <dependency>
          <groupId>javax.inject</groupId>
          <artifactId>javax.inject</artifactId>
          <version>1</version>
      </dependency>
      
    • @Bean

    Aware 系列接口回调

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

    依赖注入类型选择

    • 低依赖:构造器注入
    • 多依赖:Setter 方法注入
    • 便利性:字段注入
    • 声明类:方法注入

    各种类型注入:

    • 基础类型

      • 原生类型(Primitive):boolean、byte、char、short、int、float、long、double
      • 标量类型(Scalar):Number、Character、Boolean、Enum、Locale、Charset、Currency、Properties、UUID
      • 常规类型(General):Object、String、TimeZone、Calendar、Optional 等
      • Spring 类型:Resource、InputSource、Formatter 等
    • 集合类型

      • 数组类型(Array):原生类型、标量类型、常规类型、Spring 类型
      • 集合类型(Collection)
        • Collection:List、Set(SortedSet、NavigableSet、EnumSet)
        • Map:Properties
    • 限定注入

      • 使用注解 @Qualifier 限定
        • 通过 Bean 名称限定
        • 通过分组限定
      • 基于注解 @Qualifier 扩展限定
        • 自定义注解,如 Spring Cloud @LoadBalanced
    • 延迟依赖注入

    • 使用 API ObjectFactory 延迟注入

      • 单一类型
      • 集合类型
    • 使用 API ObjectProvider 延迟注入(推荐)

      • 单一类型
      • 集合类型

    依赖处理过程

    • 入口 - DefaultListableBeanFactory#resolveDependency
    • 依赖描述符 - DependencyDescriptor
    • 自定绑定候选对象处理器 - AutowireCandidateResolver

    @Autowired、@Inject 注入,参考 AutowiredAnnotationBeanPostProcessor

    Java通用注解注入原理:

    • CommonAnnotationBeanPostProcessor
    • 注入注解
      • javax.xml.ws.WebServiceRef
      • javax.ejb.EJB
      • javax.annotation.Resource
    • 生命周期注解
      • javax.annotation.PostConstruct
      • javax.annotation.PreDestroy

    自定义依赖注入注解

    • 基于 AutowiredAnnotationBeanPostProcessor 实现

    • 自定义实现

      • 生命周期处理
        • InstantiationAwareBeanPostProcessor
        • MergedBeanDefinitionPostProcessor
      • 元数据
        • InjectedElement
        • InjectionMetadata
    • org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor

      • org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
        • @Autowired
        • @Value
        • @Inject
      • org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
        • @PostConstruct
        • @PreDestroy

    初始化 Bean 时,AutowiredAnnotationBeanPostProcessor 先解析 Bean 中的依赖(@Autowire@Value),然后 CommonAnnotationBeanPostProcessor 调用初始化方法 @@PostConstruct

    @Bean 方法设置为 static ,可以让 Bean 提前初始化。

    • 依赖查找:ApplicationContext#getBean

    • 依赖处理过程:org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency

    第七章:Spring IoC依赖来源(Dependency Sources)

    Spring IoC依赖来源

    来源 配置元数据 注册API Spring Bean 对象 生命周期管理 配置元信息 使用场景
    Spring BeanDefinition <bean id="user" class="org.geekbang...User">
    @Bean public User user(){...}
    BeanDefinitionBuilder
    BeanDefinitionRegistry#registerBeanDefinition 依赖查找、依赖注入
    单例对象 API 实现 SingletonBeanRegistry#registerSingleton 依赖查找、依赖注入
    非 Spring 容器管理对象 Resolvable Dependency ConfigurableListableBeanFactory#registerResolvableDependency 依赖注入
    外部化配置 @Value Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); 依赖注入

    依赖注入比依赖查找多一个来源,Resolvable Dependency。也就是说,可以通过注入的方式获取这类对象,但不能通过 BeanFactory#getBean 方法从容器中获取。

    Spring 內建 BeanDefintion

    在使用 AnnotationConfigApplicationContext 或者在 XML 配置中配置了注解驱动 <context:annotation-config/> ,或组件扫描 <context:component-scan base-package="org.acme" /> ,会触发org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors 注入一些 Spring 内建的 Bean:

    • org.springframework.context.annotation.ConfigurationClassPostProcessor

    • org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor

    • org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

    • org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor

      需要环境中有 JPA 依赖

    • org.springframework.context.event.EventListenerMethodProcessor

    • org.springframework.context.event.DefaultEventListenerFactory

    Spring 內建单例对象

    Spring 启动时,refresh() 方法会调用 org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory ,会注入一些单例对象,名称为:

    • environment
    • systemProperties
    • systemEnvironment

    单例对象由 org.springframework.beans.factory.config.SingletonBeanRegistry 注册,org.springframework.beans.factory.support.AbstractBeanFactory 实现了这个接口,从容器中获取 Bean 的方法 org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 中,会先从单例对象中查找,如果查找到,直接返回;查找不到,则从 Spring BeanDefinition 中获取,并执行生命周期函数

    Resolvable Dependency / 非 Spring 容器管理对象 / 可解析依赖

    org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory

    // 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);
    

    后三个实际上都是同一个 ApplicationContext

    order 值越大,优先级越小

    第八章:Spring Bean 作用域

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

    注意事项

    • Spring 容器没有办法管理 prototype Bean 的完整生命周期,也没有办法记录示例的存
      在。销毁回调方法将不会执行,可以利用 BeanPostProcessor 进行清扫工作。
    • 无论是 Singleton 还是 Prototype Bean 均会执行初始化方法回调,不过仅 Singleton Bean 会执行销毁方法回调

    @Scope 注解定义原型 Bean :

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public static User prototypeUser() {
    	return createUser();
    }
    

    "request" Bean 作用域

    • XML - <bean class= "..." scope = "request" />
    • Java 注解 - @RequestScope@Scope(WebApplicationContext.SCOPE_REQUEST)

    每次使用的 CGLIB 代理对象是同一个,但是被代理的对象每次都会重新生成。

    使用 IDEA 进行远程调试:

    • 在 Edit Configurations 中新增一个 Remote ,使用命令启动 jar 时,在启动命令中增加 Remote 里的内容,启动 jar 以及 Remote,打断点进行调试。

    实现 API

    • @RequestScope
    • RequestScope

    "session" Bean 作用域

    配置

    • XML - <bean class= "..." scope = "session" />
    • Java 注解 - @RequestScope@Scope(WebApplicationContext.SCOPE_REQUEST)

    实现 API

    • @SessionScope
    • SessionScope

    "application" Bean 作用域

    配置

    • XML - <bean class= "..." scope = "application" />
    • Java 注解 - @ApplicationScope@Scope(WebApplicationContext.SCOPE_APPLICATION)

    实现 API

    • @ApplicationScope
    • ServletContextScope

    实现方式与 request 和 session 不同,这里直接将 Bean 放入 ServletContext

    自定义 Bean 作用域

    实现 Scope

    • org.springframework.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="...">
                  </entry>
              </map>
          </property>
      </bean>
      

    第九章:Spring Bean生命周期(Bean Lifecycle)

    Spring Bean 元信息配置阶段

    BeanDefinition 配置

    • 面向资源
      • XML 配置
      • Properties 资源配置
    • 面向注解
    • 面向 API

    Spring Bean 元信息解析阶段

    BeanDefinition 解析

    • 面向资源 BeanDefinition 解析 - BeanDefinitionReader
      • XML 解析器 - XmlBeanDefinitionReader
      • Properties 解析器 - PropertiesBeanDefinitionReader
    • 面向注解 BeanDefinition 解析 - AnnotatedBeanDefinitionReader

    Spring Bean 注册阶段

    BeanDefinition 注册接口

    BeanDefinitionRegistry

    Spring BeanDefinition 合并阶段

    BeanDefinition 合并

    父子 BeanDefinition 合并

    • 当前 BeanFactory 查找
    • 层次性 BeanFactory 查找
    <bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User">
        <property name="id" value="1"/>
        ...
    </bean>
    
    <bean id="superUser" class="org.geekbang.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user"
          primary="true">
        <property name="address" value="杭州"/>
    </bean>
    

    XmlBeanDefinitionReader#loadBeanDefinitions 加载 XML 文件时,赋值 DefaultListableBeanFactory#beanDefinitionMap,这个 Map 中的 BeanDefinition 还没有合并,也就是说 superUser 的属性值还没有从 user 中继承过来。

    AbstractBeanFactory#getBean 获取 bean 时,执行 AbstractBeanFactory#getMergedBeanDefinition ,对 superUser 进行合并,放入 AbstractBeanFactory#mergedBeanDefinitions 中。

    Spring Bean Class 加载阶段

    • ClassLoader 类加载
    • Java Security 安全控制
    • ConfigurableBeanFactory 临时 ClassLoader

    AbstractBeanDefinition#beanClass 被定义为 Object ,有两种形式,一种是 全类名 的 String,另一种是 Class 对象

    Java Security 安全控制 相关

    if (System.getSecurityManager() != null) {
    	return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>) () ->
    doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
    }
    

    临时 ClassLoader 与 load-time weaving 技术有关,用于进行类型检查时(即尚未创建实际实例)

    Spring Bean 实例化阶段

    • 传统实例化方式

      • 实例化策略 - InstantiationStrategy
    • 构造器依赖注入

      实例化阶段,如果使用构造器注入,将解析构造器注入的依赖

    AbstractAutowireCapableBeanFactory#createBeanInstance

    Spring Bean 实例化前阶段

    • 非主流生命周期 - Bean 实例化前阶段
      • InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation

    返回非 null 时,阻止 bean 的默认实例化过程及以下生命周期

    唯一可以进一步生命周期处理的是 BeanPostProcessor#postProcessAfterInitialization

    Spring Bean 实例化后阶段

    • Bean 属性赋值(Populate)判断
      • InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation

    在给 bean 实例做属性赋值的方法 AbstractAutowireCapableBeanFactory#populateBean 的最开始调用,如果返回 false ,阻止 bean 的属性赋值及以下生命周期

    Spring Bean 属性赋值前阶段

    • Bean 属性值元信息

      • PropertyValues
    • Bean 属性赋值前回调

      • Spring 1.2 - 5.0:InstantiationAwareBeanPostProcessor#postProcessPropertyValues

        此方法已过期,使用 postProcessProperties 替代,为了兼容,只有在 postProcessProperties 返回 null 时(默认实现),才会调用此方法

      • Spring 5.1:InstantiationAwareBeanPostProcessor#postProcessProperties

    在工厂将给定属性值应用于给定 bean 之前,对它们进行处理。

    在依赖注入( byNamebyType )之后,在将配置的属性赋值给 bean 实例 AbstractAutowireCapableBeanFactory#applyPropertyValues 之前执行此阶段方法

    Spring Bean 初始化阶段

    AbstractAutowireCapableBeanFactory#initializeBean

    Spring Bean Aware 接口回调阶段

    Spring Aware 接口,执行顺序从上到下

    • BeanNameAware
    • BeanClassLoaderAware
    • BeanFactoryAware

    依赖于 ApplicationContext

    • EnvironmentAware
    • EmbeddedValueResolverAware
    • ResourceLoaderAware
    • ApplicationEventPublisherAware
    • MessageSourceAware
    • ApplicationContextAware

    在初始化 Bean 实例 AbstractAutowireCapableBeanFactory#initializeBean 的最开始执行此阶段,前三个接口直接调用,而依赖于 ApplicationContext 的几个 Aware 接口,在 ApplicationContext 的生命周期中,会在 beanFactory 中加入 ApplicationContextAwareProcessor ,在其 postProcessBeforeInitialization 方法中执行调用

    ApplicationContextAwareProcessor 是包权限的

    Spring Bean 初始化前阶段

    已完成

    • Bean 实例化

    • Bean 属性赋值

    • Bean Aware 接口回调

    方法回调

    • BeanPostProcessor#postProcessBeforeInitialization

    Spring Bean 初始化阶段

    Bean 初始化(Initialization)

    • @PostConstruct 标注方法
    • 实现 InitializingBean 接口的 afterPropertiesSet() 方法
    • 自定义初始化方法

    @PostConstruct 的处理需要依赖于注解驱动,CommonAnnotationBeanPostProcessor#postProcessBeforeInitialization

    Spring Bean 初始化后阶段

    方法回调

    • BeanPostProcessor#postProcessAfterInitialization

    Spring Bean 初始化完成阶段

    方法回调

    • Spring 4.1 +:SmartInitializingSingleton#afterSingletonsInstantiated

    SmartInitializingSingleton 通常在 Spring ApplicationContext 场景使用

    使用 BeanFactory 时,需要显式的调用此方法;在 ApplicationContext 启动时,调用了此方法 AbstractApplicationContext#finishBeanFactoryInitialization ,这个方法做了两件事情:

    1. 将已注册的 BeanDefinition 初始化成 Spring Bean
    2. 调用所有 SmartInitializingSingleton#afterSingletonsInstantiated

    Spring Bean 销毁前阶段

    • 方法回调
      • DestructionAwareBeanPostProcessor#postProcessBeforeDestruction

    执行 ConfigurableBeanFactory#destroyBean 时,触发 Bean 前销毁阶段

    @PreDestroy 的处理需要依赖于注解驱动,CommonAnnotationBeanPostProcessor#postProcessBeforeDestruction

    Spring Bean 销毁阶段

    Bean 销毁(Destroy)

    • @PreDestroy 标注方法
    • 实现 DisposableBean 接口的 destroy() 方法
    • 自定义销毁方法

    @PreDestroy 的处理需要依赖于注解驱动,CommonAnnotationBeanPostProcessor#postProcessBeforeDestruction

    CommonAnnotationBeanPostProcessorDestructionAwareBeanPostProcessor 的实现类之一

    如果其他 DestructionAwareBeanPostProcessor 排序在 CommonAnnotationBeanPostProcessor 后,会先执行 @PreDestroy 标注方法,后执行其他 DestructionAwareBeanPostProcessor 销毁前阶段方法

    Spring Bean 垃圾收集

    Bean 垃圾回收(GC)

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

    面试题

    BeanPostProcessor 的使用场景有哪些?

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

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

    BeanFactoryPostProcessor 与 BeanPostProcessor 的区别

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

    加分项:BeanFactoryPostProcessor 必须有 Spring ApplicationContext 执行,BeanFactory 无法与其直接交互。而 BeanPostProcessor 则直接与 BeanFactory 关联,属于 N 对 1 的关系。

    BeanFactory 是怎样处理 Bean 生命周期?

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

    • BeanDefinition 注册阶段 - registerBeanDefinition
    • BeanDefinition 合并阶段 - getMergedBeanDefinition
    • Bean 实例化前阶段 - resolveBeforeInstantiation
    • Bean 实例化阶段 - createBeanInstance
    • Bean 实例化后阶段 - populateBean
    • Bean 属性赋值前阶段 - populateBean
    • Bean 属性赋值阶段 - populateBean
    • Bean Aware 接口回调阶段 - initializeBean
    • Bean 初始化前阶段 - initializeBean
    • Bean 初始化阶段 - initializeBean
    • Bean 初始化后阶段 - initializeBean
    • Bean 初始化完成阶段 - preInstantiateSingletons
    • Bean 销毁前阶段 - destroyBean
    • Bean 销毁阶段 - destroyBean

    第十章:Spring配置元信息(Configuration Metadata)

    Spring 配置元信息

    • Spring Bean 配置元信息 - BeanDefinition
    • Spring Bean 属性元信息 - PropertyValues
    • Spring 容器配置元信息
    • Spring 外部化配置元信息 - PropertySource
    • Spring Profile 元信息 - @Profile

    Spring Bean 配置元信息

    Bean 配置元信息 - BeanDefinition

    • GenericBeanDefinition:通用型 BeanDefinition
    • RootBeanDefinition:无 Parent 的 BeanDefinition 或者合并后 BeanDefinition
    • AnnotatedBeanDefinition:注解标注的 BeanDefinition

    Spring Bean 属性元信息

    • Bean 属性元信息 - PropertyValues
      • 可修改实现 - MutablePropertyValues
      • 元素成员 - PropertyValue
    • Bean 属性上下文存储 - AttributeAccessor
    • Bean 元信息元素 - BeanMetadataElement

    AttributeAccessorSupport#attributes 是附加属性(不影响 Bean populate、initialize)

    BeanMetadataAttributeAccessor#source 存储当前 BeanDefinition 来自于何方(辅助作用)

    Spring 容器配置元信息

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

    beans 元素属性 默认值 使用场景
    profile null(留空) Spring Profiles 配置值
    default-lazy-init default 当 outter beans “default-lazy-init” 属性存在时,继承该值,否则为“false”
    default-merge default 当 outter beans “default-merge” 属性存在时,继承该值,否则为“false”
    default-autowire default 当 outter beans “default-autowire” 属性存在时,继承该值,否则为“no”
    default-autowire-candidates null(留空) 默认 Spring Beans 名称 pattern
    default-init-method null(留空) 默认 Spring Beans 自定义初始化方法
    default-destroy-method null(留空) 默认 Spring 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 属性值

    org.springframework.beans.factory.xml.BeanDefinitionParserDelegate

    populateDefaults

    基于 XML 资源装载 Spring Bean 配置元信息

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

    底层实现 - XmlBeanDefinitionReader

    加载 BeanDefinition 入口方法 loadBeanDefinitions

    使用 DOM 来解析 XML 文件,实现为 BeanDefinitionDocumentReader

    解析方法:XmlBeanDefinitionReader#parseBeanDefinitions

    1. 判断 beans 标签的 profile 属性,如果不在激活状态,直接返回,不再向下解析
    2. 如果是 beans 标签下的特殊元素,进行特殊处理,方法为 DefaultBeanDefinitionDocumentReader#parseDefaultElement
      • beans
      • bean
      • alias
      • import
    3. 否则,BeanDefinitionParserDelegate#parseCustomElement

    基于 Properties 资源装载 Spring Bean 配置元信息

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

    底层实现 - PropertiesBeanDefinitionReader

    如果出现重复的 Bean 定义,后者不会被注册进 BeanFactory

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

    Spring 模式注解

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

    org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters@Component 及其派生注解加入筛选

    Spring Bean 定义注解

    Spring 注解 场景说明 起始版本
    @Bean 替换 XML 元素 <bean> 3.0
    @DependsOn 替代 XML 属性 <bean depends-on="..."/> 3.0
    @Lazy 替代 XML 属性 <bean lazy-init="true|falses" /> 3.0
    @Primary 替换 XML 元素 <bean primary="true|false" /> 3.0
    @Role 替换 XML 元素 <bean role="..." /> 3.1
    @Lookup 替代 XML 属性 <bean lookup-method="..."> 4.1

    Spring Bean 依赖注入注解

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

    AutowiredAnnotationBeanPostProcessor@Autowired 相关

    Java 注解 场景说明 起始版本
    @Resource 类似于 @Autowired 2.5
    @Inject 类似于 @Autowired 2.5

    CommonAnnotationBeanPostProcessor@Resource 相关

    AutowiredAnnotationBeanPostProcessor@Inject 相关

    Spring Bean 条件装配注解

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

    @Profile 基于 @Conditional 实现

    @Conditional 相关 API,ConditionEvaluator ,用于判断 Bean 是否满足条件,满足则注册

    Spring Bean 生命周期回调注解

    Spring 注解 场景说明 起始版本
    @PostConstruct 替换 XML 元素 <bean init-method="..." />InitializingBean 2.5
    @PreDestroy 替换 XML 元素 <bean destroy-method="..." />DisposableBean 2.5

    CommonAnnotationBeanPostProcessor

    Spring Bean 配置元信息底层实现

    Spring BeanDefinition 解析与注册

    实现场景 实现类 起始版本
    XML 资源 XmlBeanDefinitionReader 1.0
    Properties 资源 PropertiesBeanDefinitionReader 1.0
    Java 注解 AnnotatedBeanDefinitionReader 3.0
    • XmlBeanDefinitionReaderPropertiesBeanDefinitionReader 都继承自 AbstractBeanDefinitionReader,实现了 BeanDefinitionReader 接口,与资源(Resource)相关联

    • AnnotatedBeanDefinitionReader 是独立的类,与 Resource 无关

    • Spring XML 资源 BeanDefinition 解析与注册

      • 核心 API - XmlBeanDefinitionReader
        • 资源 - Resource
        • 底层 - BeanDefinitionDocumentReader
          • XML 解析 - Java DOM Level 3 API
          • BeanDefinition 解析 - BeanDefinitionParserDelegate
          • BeanDefinition 注册 - BeanDefinitionRegistry
    • Spring Properties 资源 BeanDefinition 解析与注册

      • 核心 API - PropertiesBeanDefinitionReader
        • 资源
          • 字节流 - Resource
          • 字符流 - EncodedResouce
        • 底层
          • 存储 - java.util.Properties
          • BeanDefinition 解析 - API 内部实现
          • BeanDefinition 注册 - BeanDefinitionRegistry
    • Spring Java 注册 BeanDefinition 解析与注册

      • 核心 API - AnnotatedBeanDefinitionReader
        • 资源
          • 类对象 - java.lang.Class
        • 底层
          • 条件评估 - ConditionEvaluator
          • Bean 范围解析 - ScopeMetadataResolver
          • BeanDefinition 解析 - 内部 API 实现
          • BeanDefinition 处理 - AnnotationConfigUtils.processCommonDefinitionAnnotations
          • BeanDefinition 注册 - BeanDefinitionRegistry

    Properties 资源加载默认编码是 ISO-8859-1

    AnnotatedBeanDefinitionReader 使用 ConditionEvaluator 判断 Bean 的元信息,如果其中存在 @Conditional 条件,判断此条件通过才会将 Bean 加入容器

    基于 XML 资源装载 Spring IoC 容器配置元信息

    Spring IoC 容器相关 XML 配置

    命名空间 所属模块 Schema 资源 URL
    beans spring-beans https://www.springframework.org/schema/beans/spring-beans.xsd
    context spring-context https://www.springframework.org/schema/context/spring-context.xsd
    aop spring-aop https://www.springframework.org/schema/aop/spring-aop.xsd
    tx spring-tx https://www.springframework.org/schema/tx/spring-tx.xsd
    util spring-beans https://www.springframework.org/schema/util/spring-util.xsd
    tool spring-beans https://www.springframework.org/schema/tool/spring-tool.xsd

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

    Spring IoC 容器装配注解

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

    Spring IoC 配属属性注

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

    基于 Extensible XML authoring 扩展 Spring XML 元素

    Spring XML 扩展

    • 编写 XML Schema 文件:定义 XML 结构
      • users.xsd
    • 自定义 NamespaceHandler 实现:命名空间绑定
      • spring.handlers
      • org.geekbang.thinking.in.spring.configuration.metadata.UsersNamespaceHandler
    • 自定义 BeanDefinitionParser 实现:XML 元素与 BeanDefinition 解析
      • org.geekbang.thinking.in.spring.configuration.metadata.UserBeanDefinitionParser
    • 注册 XML 扩展:命名空间与 XML Schema 映射
      • spring.schemas
      • users-context.xml

    触发时机:BeanDefinitionParserDelegate#parseCustomElement

    • 获取 namespace
    • 通过 namespace 解析 NamespaceHandler
    • 构造 ParserContext
    • 解析元素,获取 BeanDefinintion

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

    • 注解驱动
      • @org.springframework.context.annotation.PropertySource
      • @org.springframework.context.annotation.PropertySources
    • API 编程
      • org.springframework.core.env.PropertySource
      • org.springframework.core.env.PropertySources

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

    API 编程

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

    Requires SnakeYAML 1.18 or higher, as of Spring Framework 5.0.6

    通过 PropertySourceFactory 接口,引入 PropertySource

    org.springframework.core.io.support.PropertySourceFactory

    @PropertySource(
            name = "yamlPropertySource",
            value = "classpath:/META-INF/user.yaml",
            factory = YamlPropertySourceFactory.class)
    

    面试题

    Spring 內建 XML Schema 常见有哪些?

    命名空间 所属模块 Schema 资源 URL
    beans spring-beans https://www.springframework.org/schema/beans/spring-beans.xsd
    context spring-context https://www.springframework.org/schema/context/spring-context.xsd
    aop spring-aop https://www.springframework.org/schema/aop/spring-aop.xsd
    tx spring-tx https://www.springframework.org/schema/tx/spring-tx.xsd
    util spring-beans https://www.springframework.org/schema/util/spring-util.xsd
    tool spring-beans https://www.springframework.org/schema/tool/spring-tool.xsd

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

    • Bean 配置元信息:通过媒介(如 XML、Proeprties 等),解析 BeanDefinition
    • IoC 容器配置元信息:通过媒介(如 XML、Proeprties 等),控制 IoC 容器行为,比如注解驱动、AOP 等
    • 外部化配置:通过资源抽象(如 Proeprties、YAML 等),控制 PropertySource
    • Spring Profile:通过外部化配置,提供条件分支流程

    Extensible XML authoring 的缺点?

    • 高复杂度:开发人员需要熟悉 XML Schema,spring.handlers,spring.schemas 以及 Spring API 。
    • 嵌套元素支持较弱:通常需要使用方法递归或者其嵌套解析的方式处理嵌套(子)元素。
    • XML 处理性能较差:Spring XML 基于 DOM Level 3 API 实现,该 API 便于理解,然而性能较差。
    • XML 框架移植性差:很难适配高性能和便利性的 XML 框架,如 JAXB。

    第十一章:Spring 资源管理

    引入动机

    为什么 Spring 不使用 Java 标准资源管理,而选择重新发明轮子?

    • Java 标准资源管理强大,然而扩展复杂,资源存储方式并不统一
    • Spring 要自立门户(重要的话,要讲三遍)
    • Spring “抄”、“超” 和 “潮”

    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

    img

    基于 java.net.URLStreamHandler 扩展协议

    JDK 1.8 內建协议实现

    协议 实现类
    file sun.net.www.protocol.file.Handler
    ftp sun.net.www.protocol.ftp.Handler
    http sun.net.www.protocol.http.Handler
    https sun.net.www.protocol.https.Handler
    jar sun.net.www.protocol.jar.Handler
    mailto sun.net.www.protocol.mailto.Handler
    netdoc sun.net.www.protocol.netdoc.Handler

    实现类名必须为 Handler

    实现类命名规则 说明
    默认 sun.net.www.protocol.${protocol}.Handler
    自定义 通过 Java Properties java.protocol.handler.pkgs 指定实现类包名,实现类名必须为 Handler。如果存在多包名指定,通过分隔符 |

    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

    Spring 内建 Resource 实现

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

    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

    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

    Spring 通配路径资源加载器

    • 通配路径 ResourceLoader
      • org.springframework.core.io.support.ResourcePatternResolver
      • org.springframework.core.io.support.PathMatchingResourcePatternResolver
    • 路径匹配器
      • org.springframework.util.PathMatcher
        • Ant 模式匹配实现 - org.springframework.util.AntPathMatcher

    Spring 通配路径资源扩展

    1. 实现 org.springframework.util.PathMatcher
    2. 重置 PathMatcher
      • PathMatchingResourcePatternResolver#setPathMatcher

    依赖注入 Spring Resource

    基于 @Value 实现

    @Value("classpath:/...") 
    private Resource resource;
    
    @Value("classpath*:/META-INF/*.properties")
    private Resource[] propertiesResources;
    

    依赖注入 ResourceLoader

    • 方法一:实现 ResourceLoaderAware 回调
    • 方法二:@Autowired 注入 ResourceLoader
    • 方法三:注入 ApplicationContext 作为 ResourceLoader

    ApplicationContext 接口继承 ResourcePatternResolver 继承 ResourceLoader

    ResourceLoaderAware 回调在 实例初始化(@PostConstruct 等)之前

    面试题

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

    • XML 资源
    • Properties 资源
    • YAML 资源

    请例举不同类型 Spring 配置资源?

    • XML 资源
      • 普通 Bean Definition XML 配置资源 - *.xml
      • Spring Schema 资源 - *.xsd
    • Properties 资源
      • 普通 Properties 格式资源 - *.properties
      • Spring Handler 实现类映射文件 - META-INF/spring.handlers
      • Spring Schema 资源映射文件 - META-INF/spring.schemas
    • YAML 资源
      • 普通 YAML 配置资源 - *.yaml*.yml

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

    • 简易实现
      • 实现 URLStreamHandler 并放置在 sun.net.www.protocol.${protocol}.Handler 包下
    • 自定义实现
      • 实现 URLStreamHandler
      • 添加 -Djava.protocol.handler.pkgs 启动参数,指向 URLStreamHandler 实现类的包下
    • 高级实现
      • 实现 URLStreamHandlerFactory 并传递到 URL 之中
    简易实现实例
    1. 扩展 x 协议,新建类 sun.net.www.protocol.x.Handler,类名格式必须符合规范

      public class Handler extends URLStreamHandler {
          @Override
          protected URLConnection openConnection(URL u) throws IOException {
              return new XURLConnection(u);
          }
      }
      
    2. 新建类 XURLConnection ,实现 java.net.URLConnection

      public class XURLConnection extends URLConnection {
      
          private final ClassPathResource resource;
      
          // URL = x:///META-INF/default.properties
          protected XURLConnection(URL url) {
              super(url);
              this.resource = new ClassPathResource(url.getPath());
          }
      
          @Override
          public void connect() throws IOException {
      
          }
      
          public InputStream getInputStream() throws IOException {
              return resource.getInputStream();
          }
      }
      
    3. 测试使用

      public class HandlerTest {
      
          public static void main(String[] args) throws IOException {
              URL url = new URL("x:///META-INF/default.properties"); // 类似于 classpath:/META-INF/default.properties
              InputStream inputStream = url.openStream();
              System.out.println(StreamUtils.copyToString(inputStream, Charset.forName("UTF-8")));
          }
      }
      
    自定义实现
    1. 新建类 Handler,继承 sun.net.www.protocol.x.Handle,类名必须为 Handler,包名无限制

      public class Handler extends sun.net.www.protocol.x.Handler {
      
          // -Djava.protocol.handler.pkgs=org.geekbang.thinking.in.spring.resource
          public static void main(String[] args) throws IOException {
              // springx 协议
              URL url = new URL("springx:///META-INF/production.properties"); // 类似于 classpath:/META-INF/default.properties
              InputStream inputStream = url.openStream();
              System.out.println(StreamUtils.copyToString(inputStream, Charset.forName("UTF-8")));
          }
      }
      
    2. 运行时增加 VM 参数,-Djava.protocol.handler.pkgs=org.geekbang.thinking.in.spring.resource

    高级实现
    public class MyURLStreamHandlerFactory implements URLStreamHandlerFactory {
        @Override
        public URLStreamHandler createURLStreamHandler(String protocol) {
            return new Handler();
        }
    
        public static void main(String[] args) throws IOException {
            // URL 设置 URLStreamHandlerFactory,必须在创建 URL 实例之前
            URL.setURLStreamHandlerFactory(new MyURLStreamHandlerFactory());
            // springx 协议
            URL url = new URL("springx:///META-INF/production.properties"); // 类似于 classpath:/META-INF/default.properties
            
            InputStream inputStream = url.openStream();
            System.out.println(StreamUtils.copyToString(inputStream, Charset.forName("UTF-8")));
        }
    }
    
  • 相关阅读:
    一个 redis 异常访问引发 oom 的案例分析
    从粗放式到精益化编程
    又是一年校招
    xUtils框架的介绍(四)
    xUtils框架的介绍(三)
    hdu 4283 区间dp
    hdu 4632区间dp 回文字串计数问题
    poj 1651 区间dp
    NYOJ 石子合并(一) 区间dp入门级别
    区间dp 整数划分问题
  • 原文地址:https://www.cnblogs.com/huangwenjie/p/13945988.html
Copyright © 2020-2023  润新知