分享
  1. 首页
  2. 文章

Java高手提薪精选–Spring源码解析到手写核心组件|已完结

edc123 · · 360 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

获课♥》weiranit.fun/14707/

获取ZY↑↑方打开链接↑↑

Spring框架核心架构总览
### 2.1 整体架构剖析
Spring框架采用了精妙的分层架构设计,宛如一座精心构筑的大厦,各个层次与模块各司其职又紧密协作。整个框架大约包含20个功能各异的模块,这些模块可大致归纳为以下几个主要部分:
1. **核心容器(Core Container)**:作为整个框架的根基,核心容器承载着至关重要的职责。它涵盖了Core、Beans、Context和Expression Language(SpEL)等模块。其中,Core和Beans模块是框架的基石,为开发者提供了IoC(控制反转)和依赖注入这两大核心特性。BeanFactory作为这部分的核心概念,堪称设计模式中工厂模式的经典应用典范,它巧妙地消除了程序对单例模式的过度依赖,让开发者能够将程序逻辑与依赖关系及配置实现优雅分离。Core模块内汇聚了大量Spring框架底层通用的基础工具类,这些类就如同大厦建设中的基础建材,支撑着框架其他各个组件的正常运转,同时开发者也可按需将其引入自己的应用系统。Beans模块则与开发者日常打交道最为频繁,它囊括了访问配置文件、创建与管理bean以及执行IoC/DI操作的所有相关类。Context模块则是在Core和Beans模块坚实基础上的进一步拓展,它创新性地提供了一种类似JNDI注册器的框架式对象访问方式。Context模块不仅继承了Beans模块的诸多特性,还为Spring核心增添了丰富的扩展功能,如对国际化(支持多语言切换,适配全球不同地区用户)、事件传播(高效处理系统内各类事件通知)、资源加载(便捷读取各类资源文件)以及Context透明创建的全面支持。此外,它还对J2EE的部分特性,像EJB(企业级JavaBean,用于构建分布式应用)、JMX(Java管理扩展,实现对Java应用的管理与监控)和基础远程处理提供了良好兼容。而ApplicationContext接口则是Context模块的关键所在,犹如大厦的主入口,开发者通过它可以便捷地与整个Spring容器进行交互。SpEL模块提供了一种功能强大且灵活的表达式语言,允许开发者在运行时对对象进行查询与操作,极大地增强了框架在动态处理数据方面的能力。
2. **数据访问/集成(Data Access/Integration)**:这一层专注于解决企业级应用中与数据交互相关的复杂问题,涵盖了JDBC、ORM、OXM、JMS和Transaction等多个模块。JDBC模块精心构建了一个JDBC抽象层,将开发者从繁琐冗长的JDBC编码以及解析数据库厂商特定错误代码的困境中解放出来,其中包含了Spring对JDBC数据访问进行封装的一整套类。ORM模块则为当下流行的各类对象 - 关系映射API,如JPA(Java持久化API)、JDO(Java数据对象)、Hibernate、iBatis等,搭建了统一的交互桥梁。借助ORM封装包,开发者能够充分融合Spring提供的各类特性,轻松实现高效的O/R映射,例如结合简单声明性事务管理,确保数据操作的原子性与一致性。OXM模块提供了对Object/XML映射实现的抽象层,支持包括JAXB、Castor、XMLBeans、JiBX和XStream等多种映射框架,方便开发者在对象与XML数据格式之间进行灵活转换。JMS(Java Messaging Service)模块主要聚焦于制造和消费消息的相关特性,助力实现应用系统间可靠的异步通信。Transaction模块为编程式和声明式事务管理提供全面支持,事务类只需实现特定接口,即可在所有POJO(普通Java对象)上便捷应用事务管理功能,确保数据操作的完整性与可靠性。
3. **Web层**:Web上下文模块搭建在应用程序上下文模块之上,专门为基于Web的应用程序量身打造了适配的上下文环境。Spring框架在此层对与Jakarta Struts的集成提供了良好支持(尽管部分支持在后续版本中有调整),同时极大地简化了处理各类Web请求以及将请求参数精准绑定到域对象的复杂工作。Web层具体包含Web、Web - Servlet、Web - Struts和Web - Portlet等模块。Web模块提供了基础且通用的面向Web的集成特性,例如多文件上传功能、借助servlet listeners初始化IoC容器的便捷方式以及一个专为Web应用定制的上下文环境,同时还涵盖了Spring远程支持中与Web相关的部分。Web - Servlet模块中包含了Spring经典的model - view - controller(MVC)实现,它将模型范围内的代码与web forms清晰分离,并与Spring框架的其他特性实现深度集成,使Web应用的开发更加规范、高效。Web - Struts模块在早期为Struts框架提供了良好支持,方便开发者将Struts Web层与Spring应用进行集成,不过在Spring3.0版本中该支持已被弃用。Web - Portlet模块则针对Portlet环境,提供了与Web - Servlet模块类似的MVC实现,以满足特定场景下的开发需求。
4. **AOP与Instrumentation**:AOP模块遵循AOP联盟标准,提供了成熟的面向切面编程实现。通过AOP,开发者可以灵活定义方法拦截器和切点,将横切关注点(如日志记录、权限校验、事务管理等)从核心业务逻辑中抽离出来,有效降低代码之间的耦合度,提升代码的可维护性与可扩展性。借助source - level的元数据功能,开发者还能将各种行为信息巧妙地融入代码,这一特性与.Net技术中的attribute概念有着异曲同工之妙。通过配置管理特性,Spring AOP模块无缝地将面向切面编程功能集成到Spring框架中,使得Spring框架管理的任何对象都能轻松支持AOP。例如,通过AOP实现事务管理服务,开发者无需依赖EJB组件,便能在应用程序中便捷地实现声明式事务管理。Aspects模块则为AspectJ的集成提供了有力支持,进一步丰富了AOP的应用场景与实现方式。Instrumentation模块提供了class instrumentation支持和classloader实现,为在特定应用服务器上的使用提供了便利。
5. **Test模块**:Test模块专为使用JUnit和TestNG对Spring组件进行测试而设计,为开发者提供了一系列便捷的工具与支持,帮助开发者高效编写针对Spring组件的测试用例,确保Spring应用的质量与稳定性。

### 2.2 Spring核心组件的关键地位
在Spring框架庞大而复杂的体系中,Core、Context和Beans这三个核心组件犹如大厦的承重梁与基石,构建起了整个框架的基础架构。倘若没有它们,AOP、Web等上层特性功能将成为无本之木、无源之水,无法得以实现。

其中,Beans组件更是核心中的核心,可以毫不夸张地说,Spring本质上就是面向Bean的编程(BOP,Bean Oriented Programming)。Bean在Spring中的地位,就如同Object之于面向对象编程(OOP),是整个框架的主角。这一地位的奠定,源于Spring框架的核心设计目标——解决对象之间复杂的依赖关系管理问题。Spring创新性地引入依赖注入机制,通过将对象之间的依赖关系转移到配置文件中进行统一管理,而这些被管理的对象,正是被封装在Bean之中。IoC容器作为依赖注入机制的载体,其内部所管理的正是一个个被Bean包裹的对象。Spring正是借助这种巧妙设计,实现了对对象的高效管理以及一系列诸如生命周期控制、依赖关系解析等额外操作。

可以形象地将Bean比作一场精彩演出中的演员,他们各自承载着不同的业务逻辑与功能;Context则如同演出的舞台背景,为Bean提供了运行所需的环境,负责发现、建立并维护各个Bean之间的关系,其构建的IoC容器就像是舞台上的场景布置,为演员们的互动与协作提供了场所;而Core组件则像是演出所使用的各类道具,是发现、建立和维护Bean关系所不可或缺的工具集合,从这个角度理解,将Core组件称为Util或许会更加直观。这三个核心组件紧密协作,共同构成了Spring框架强大功能的基石,为上层丰富多样的特性与应用场景提供了坚实支撑。

## 三、Spring IoC容器源码深度解析
### 3.1 BeanDefinition解析流程探秘
当Spring容器启动之时,一项至关重要的初始任务便是将配置源(无论是基于注解的配置方式,还是传统的XML配置文件)解析为BeanDefinition对象。BeanDefinition在Spring IoC容器中扮演着极为关键的角色,它就像是一份详细的Bean蓝图,存储了关于bean的丰富元数据信息,包括但不限于bean的类名(指定该bean对应的Java类)、作用域(例如单例模式下整个容器中仅存在一个该bean实例,原型模式下每次获取都会创建一个新的实例等)、是否为懒加载(决定是在容器启动时就创建bean实例,还是在首次被请求时才创建)等关键信息。其接口定义如下:
```java
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
void setBeanClassName(String beanClassName);
String getBeanClassName();
void setScope(String scope);
String getScope();
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
// 其他众多用于设置和获取bean相关元数据的方法...
}
```
整个解析流程犹如一场精心编排的交响乐,涉及多个关键步骤与组件的协同工作:
1. **初始化容器入口**:开发者通常会根据项目所采用的配置方式,选择使用ClassPathXmlApplicationContext(针对XML配置文件,从类路径下加载配置文件)或AnnotationConfigApplicationContext(针对注解配置方式)来初始化Spring容器。这两个类分别代表了从XML和注解配置源启动Spring容器的入口点。
2. **触发容器刷新**:在初始化容器实例后,紧接着调用其refresh()方法,这一操作犹如按下了整个容器启动流程的“启动键”,触发了一系列复杂而有序的初始化步骤,其中就包括至关重要的BeanDefinition解析过程。
3. **BeanDefinition读取与注册**:BeanDefinitionReader组件此时闪亮登场,它的职责是依据配置源的类型(XML或注解),采用对应的解析策略读取配置信息,并将解析后的BeanDefinition注册到DefaultListableBeanFactory中。DefaultListableBeanFactory是Spring IoC容器的核心实现类之一,它负责实际存储和管理所有的BeanDefinition,就像是一个精心管理的仓库,为后续bean的创建、实例化等操作提供了基础数据支撑。在这一过程中,对于XML配置文件,会通过一系列的XML解析技术,将配置文件中的标签、属性等信息解析并转化为对应的BeanDefinition对象;对于注解配置方式,则会借助反射机制以及Spring自定义的注解解析逻辑,扫描并解析类、方法、字段上的注解,从而生成相应的BeanDefinition。

### 3.2 依赖注入实现原理揭秘
依赖注入作为Spring IoC容器的核心特性之一,其实现过程同样蕴含着精妙的设计与复杂的逻辑。在Spring中,依赖注入主要通过AutowiredAnnotationBeanPostProcessor这个后置处理器来实现。该后置处理器会在bean的生命周期的特定阶段介入,完成依赖注入的关键操作。其核心处理方法postProcessProperties的实现逻辑如下:
```java
public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
@Override
public PropertyValues postProcessProperties(
PropertyValues pvs, Object bean, String beanName) {
// 查找与当前bean相关的依赖注入元数据
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 根据元数据执行依赖注入操作
metadata.inject(bean, beanName, pvs);
} catch (Throwable ex) {
// 若注入过程出现异常,抛出Bean创建异常
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
// 其他众多用于支持依赖注入的方法...
}
```
具体而言,当Spring容器在创建bean的过程中,执行到填充bean属性这一阶段时,AutowiredAnnotationBeanPostProcessor会被触发。它首先会通过findAutowiringMetadata方法,依据bean的名称、类型以及当前的属性值集合,查找与该bean相关的所有依赖注入元数据。这些元数据详细描述了该bean中需要进行依赖注入的字段、方法等信息,例如哪些字段需要自动装配其他bean实例,装配的方式是按类型匹配还是按名称匹配等。随后,通过调用metadata.inject方法,根据这些元数据,从Spring容器中获取对应的依赖对象,并将其注入到当前bean的相应字段或方法参数中,从而完成依赖注入的过程。在这个过程中,如果出现无法找到匹配的依赖对象、依赖对象创建失败等异常情况,AutowiredAnnotationBeanPostProcessor会捕获异常,并抛出BeanCreationException,提醒开发者依赖注入过程出现了问题,以便及时排查与解决。

## 四、手写实现简易IoC容器
### 4.1 搭建容器基础结构
为了深入理解Spring IoC容器的底层原理,我们尝试从零开始手写一个简易的IoC容器,以此来模拟Spring IoC容器的核心功能。首先,构建容器的基础结构,定义一个MiniContainer类,该类将承担起管理bean定义和创建bean实例的重任。在MiniContainer类中,我们使用两个关键的Map集合来存储相关信息:
```java
public class MiniContainer {
// 用于存储单例模式下的bean实例,key为bean名称,value为bean实例对象
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 用于存储bean的定义信息,key为bean名称,value为BeanDefinition对象
private Map<String, BeanDefinition> beanDefinitions = new ConcurrentHashMap<>();

// 提供方法用于向容器中注册bean定义
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitions.put(beanName, beanDefinition);
}

// 提供方法用于从容器中获取bean实例
public Object getBean(String beanName) {
// 先尝试从单例池中获取bean实例
Object bean = singletonObjects.get(beanName);
if (bean != null) {
return bean;
}
// 若单例池中不存在,则根据bean定义创建新的bean实例
return createBean(beanName, beanDefinitions.get(beanName));
}
// 其他后续用于实现bean创建、生命周期管理等功能的方法将在此逐步添加...
}
```
在上述代码中,singletonObjects这个ConcurrentHashMap用于存储单例模式下的bean实例,确保在整个容器生命周期内,单例bean只会被创建一次,后续通过相同的bean名称获取时,直接从该Map中返回已创建的实例,从而实现单例模式的效果。beanDefinitions这个ConcurrentHashMap则用于存储所有bean的定义信息,每个bean定义都被封装在BeanDefinition对象中,BeanDefinition类可自行定义,用于存储诸如bean的类名、作用域、构造函数参数、属性值等关键元数据信息。registerBeanDefinition方法为外部提供了向容器中注册bean定义的入口,而getBean方法则是获取bean实例的主要途径,它先检查单例池中是否已存在目标bean实例,若存在则直接返回;若不存在,则调用createBean方法,根据bean定义创建新的bean实例。

### 4.2 实现Bean生命周期管理
在Spring IoC容器中,bean有着完整而严谨的生命周期管理机制,这对于确保bean在不同阶段的正确初始化、使用以及销毁至关重要。为了在我们的简易IoC容器中模拟这一过程,在MiniContainer类中添加createBean方法,该方法将负责bean从实例化、属性填充、初始化到最终加入单例池(若为单例bean)的全过程,具体实现如下:
```java
private Object createBean(String beanName, BeanDefinition bd) {
// 1. 实例化bean
Object bean = instantiateBean(bd);
// 2. 对bean进行属性填充
populateBean(beanName, bd, bean);
// 3. 对bean进行初始化操作
initializeBean(beanName, bean, bd);
// 4. 若bean为单例模式,则将其加入单例池
if (bd.isSingleton()) {
singletonObjects.put(beanName, bean);
}
return bean;
}
```
1. **实例化bean**:在instantiateBean方法中,通过反射机制,根据BeanDefinition中存储的bean类名,创建对应的bean实例对象。例如:
```java
private Object instantiateBean(BeanDefinition bd) {
try {
// 获取bean的类名
String className = bd.getBeanClassName();
// 使用反射创建bean实例
Class<?> clazz = Class.forName(className);
return clazz.getConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to instantiate bean", e);
}
}
```
2. **属性填充**:populateBean方法负责将BeanDefinition中定义的属性值,填充到已实例化的bean对象中。这一过程需要解析BeanDefinition中的


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
360 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏