Spring
未读日志
在上一节中,出现了一个小问题:没法实例化有参构造对象
本章节有以下变动:
使用Cglib字节码方式、JDK内部方式进行有参对象的实例化
后续章节可采用策略模式优化策略的选择
这里不小心把下一节的 UML 画了。。。
因为比较简单,就只展示 Cglib 字节码和 JDK 两种实例化对象的代码了
Cglib方式:
@SuppressWarnings({"rawtypes"})public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy { @Override public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException { Enhancer enhancer = new Enhancer(); ...
Spring
未读三级缓存解决循环依赖问题
前言
目前我们的 Spring 可以完成一个基本功能。但是如果遇到 A、B 两个 Bean 对象相互依赖,就会报出 java.lang.StackOverflowError 错误。因为创建 A 时需要 B,而 B 的创建又依赖于 A 创建,死循环
循环依赖是 Spring 经典的场景。需要解决的主要是以下三种情况:
自身依赖
循环依赖
多组依赖
按照 Spring 框架的设计,用于解决循环依赖需要用到三个缓存 Map,这三个缓存分别存放了成品对象、半成品对象(未填充属性值)、代理对象,分阶段存放对象内容,来解决循环依赖问题。
singletonObjects:成品对象
earlySingletonObjects:半成品对象
singletonFactories:工厂对象(代理对象)
这里我们需要知道一个核心的原理,就是用于解决循环依赖就必须是三级缓存呢,二级行吗?一级可以不?其实都能解决,只不过 Spring 框架的实现要保证几个事情,如只有一级缓存处理流程没法拆分,复杂度也会增加,同时半成品对象可能会有空指针异常。而将半成品与成品对象分开,处理起来 ...
Spring
未读日志
上一节我们实现了往 Bean 对象注入属性,但所有的操作都需要用户手动填写,这显然是不合适的,因此这一节我们来优化
本章节主要有以下改动:
资源加载接口的定义和实现
解析 XML 处理 Bean 注册
问题
通过单元测试进行手动操作 Bean 对象的定义、注册和属性填充,以及最终获取对象调用方法。但这里会有一个问题,就是如果实际使用这个 Spring 框架,是不太可能让用户通过手动方式创建的,而是最好能通过配置文件的方式简化创建过程。需要完成如下操作:
如图中我们需要把步骤:2、3、4整合到Spring框架中,通过 Spring 配置文件的方式将 Bean 对象实例化。
接下来我们就需要在现有的 Spring 框架中,添加能解决 Spring 配置的读取、解析、注册Bean的操作。
XmlReader 的作用,就是为 Spring 自动配置 Bean 对象的注册、属性设置、Bean 对象实例化。整个过程用户无感,最终用户所需要做的操作就是调用所需的 Bean 对象就行!
设计
依照本章节的需求背景,我们需要在现有的 Spring 框架雏形中添加一个资源解析器,也就是能读 ...
Spring
未读前言
本章节正式从 IoC 的实现,转向关于 AOP 内容的开发。
AOP(Aspect Oriented Programming) 内容开发,意为:面向切面编程,通过预处理的方式以及运行期间动态代理实现程序功能的统一维护。AOP 也是 OOP 的延续,在 Spring 框架中是一个非常非常重要的内容,使用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得各模块之间的业务逻辑耦合度降低。
剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
举一个不太恰当的例子:可以把切面理解为用刀切韭菜,一根一根切总是有点慢,那么用手(代理)把韭菜捏成一把,用菜刀或者斧头这样不同的拦截操作来处理。而程序中其实也是一样,只不过韭菜变成了方法,菜刀变成了拦截方法。
实现 AOP 的技术,主要分为两大类:
一是采用动态代理技术(典型代表为Spring AOP),利用截取消息的 ...
Spring
未读FactoryBean
FactoryBean是一个工厂Bean,它使用到工厂方法模式,可以代理生成一个实例
为什么使用FactoryBean
在之前的设计中, Spring IoC 容器通过反射或字节码增强的方式实例化 Bean,但有些情况下,实例化较复杂,要往 xml 里 <bean> 填写大量的配置信息,且过程不能为用户支配。就像我们之前的实现,要注册 UserService,还要在 xml 配置内部的 UserDao,万一以后又多了一个 EventDao 了呢?当需要往 xml 配置里塞的东西越来越多,本来简单的操作,也会变成庞大,难以理解的屎山。
FactoryBean 很好的解决了这一点,它是一个对用户暴露的客制化 Bean 实例化的接口,用户可以通过实现该接口定制实例化Bean的逻辑。可以隐藏实例化一些复杂Bean的细节。给上层应用带来便利。说人话就是,将一个待实例化的 bean 内部所有自定义类型都丢到 FactoryBean 代理。
将对象交由Spring管理的方式:
使用XML配置文件标签;
在自定义类上使用@Component注解(将自定义对 ...
Spring
未读目标
在上一章节我们通过基于 Proxy.newProxyInstance 代理操作中处理方法匹配和方法拦截,对匹配的对象进行自定义的处理操作。并把这样的技术核心内容拆解到 Spring 中,用于实现 AOP 部分,通过拆分后基本可以明确各个类的职责,包括你的代理目标对象属性、拦截器属性、方法匹配属性,以及两种不同的代理操作 JDK 和 CGlib 的方式。
再有了一个 AOP 核心功能的实现后,我们可以通过单元测试的方式进行验证切面功能对方法进行拦截,但如果这是一个面向用户使用的功能,就不太可能让用户这么复杂且没有与 Spring 结合的方式单独使用 AOP,虽然可以满足需求,但使用上还是过去分散。
因此我们需要在本章节完成 AOP 核心功能与 Spring 框架的整合,最终能通过在 Spring 配置的方式完成切面的操作
设计
将 AOP 融入 Spring 需要解决几个问题
怎么通过 BeanPostProcessor 将动态代理融入 Bean 的生命周期中?
如何组装各项切点、拦截、前置以及适配对应的代理器?
为了可以让对象创建过程中,能把xml中配置的代理对象也就是切 ...
Spring
未读日志
在上一节中,只是简单创建了 BeanDefinition 和 BeanFactory
本章节有以下变动:
使用单例模式注册 Bean 容器,从而实现对象第二次获取时可以从内存中获取对象
使用工厂模式将 Bean 的创建过程交给容器,而不是在调用时期传递一个实例化好的 Bean 对象
使用模板模式统一通用核心方法的调用逻辑和标准定义
设计
完善 Spring Bean:注册 Register 和获取 Get
架构图
注册 Register
提示
非常重要的一点是 Bean 注册时只注册一个类信息,而不会直接将实例化信息注册到 Spring 容器中,所以我们修改 BeanDefinition 中的属性 bean,将类型从 Object 转换为 Class。
负责 bean 对象第一次注册,之后存储到 单例对象缓存中,Get 直接从内存中获取
获取 Get
提示
获取 Bean 对象时就需要处理 Bean 的对象实例化操作以及判断当前单例对象是否在容器里缓存起来。
定义 BeanFactory 也就是 Bean 工厂接口,提供 Bean 获取方法 getBean(String ...
前言
提示
说到spring容器,有的同学可能知道指的是BeanFactory,有的可能说是ApplicationContext,其实这二者都是容器类。
BeanFactory是底层基础类,位于spring-beans模块中,而ApplicationContext位于spring-context模块,是对BeanFactory的装饰,即包含一个BeanFactory实例。
ApplicationContext 本质上还是调用了 BeanFactory 内部的系列方法实现功能,并拓展了许多别的功能
日志
在手写 Spring 框架过程中,需要不断扩展新的功能,如一个 Bean 的定义和实例化过程前后,是否可以做到支持自定义扩展,能够对 Bean 对象执行一些修改、增强、记录操作?要做到能随时扩展新功能,本身架构就必须设计好,不能把代码写死、耦合
本章节有所改动的内容:
合并获取 BeanFactory、读取配置、注册 Bean 等操作,合并到 Spring 框架上下文中。让面向 Spring 的组件 DefaultListableBeanFactory 尽量不暴露给用户
在 Be ...
Spring
未读日志
重点:客制化 Bean 初始化阶段,用于接口暴漏、数据库数据读取、配置文件加载,链接注册中心暴露 RPC 接口以及在 Web 关闭时执行链接断开,内存销毁
目的:把这些操作交给 Spring 容器自动化处理,即满足用户可以在 xml 中配置初始化和销毁的方法,也可以通过实现类的方式处理,比如我们在使用 Spring 时用到的 InitializingBean, DisposableBean 两个接口。 其实还可以有一种是注解的方式处理初始化操作,不过目前还没有实现到注解的逻辑,后续再完善此类功能。
设计:初始化、销毁的生命周期在 Bean 加载以及注册阶段,如图所示
设计
适配器模式
销毁方法有两种甚至多种,目前有 实现接口 DisposableBean、配置信息 destroy-method 两种方式。销毁方法是交由 ApplicationContext 应用上下文在注册虚拟机钩子后,虚拟机关闭前执行的操作动作。在销毁执行时,不希望 Spring 还得关注要销毁哪些类型的方法。它的使用更希望有一个统一的接口执行销毁,所以这里新增了适配器模式做统一处理
详细代码:
适配器内部 ...
Spring
未读通过注解@Autowired注入属性信息
前言
在目前 IOC、AOP 两大核心功能模块的支撑下,完全可以管理 Bean 对象的注册和获取,不过这样的使用方式总感觉像是刀耕火种有点难用。因此在上一章节我们解决需要手动配置 Bean 对象到 spring.xml 文件中,改为可以自动扫描带有注解 @Component 的对象完成自动装配和注册到 Spring 容器的操作。
那么在自动扫描包注册 Bean 对象之后,就需要把原来在配置文件中通过 property name="token" 配置属性和Bean的操作,也改为可以自动注入。这就像我们使用 Spring 框架中 @Autowired、@Value 注解一样,完成我们对属性和对象的注入操作
本节需要添加 @Autowired、@Value、@Qualifier 等注解功能
@Autowired 默认根据类型进行 Bean 注入,因此如果有多个类型相同的 Bean 候选者,则需要指定其中一个候选者,不然 Spring 抛出异常
@Qualifier 限定描述符除了能根据名字进行注入,更能通过更细粒度的控制如何选择 ...