Spring源码分析基本介绍
摘要:本⽂结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码。若有描述错误之处,欢迎指正。
前⾔
作为⼀名开发⼈员,阅读源码是⼀个很好的学习⽅式。本⽂将结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码,若有描述错误之处,欢迎指正。
Spring是2003年兴起的⼀个轻量级Java开源框架,旨在解决企业应⽤开发的复杂性。Spring发展⾄今,衍⽣出⾮常丰富的模块,并应⽤在多种场景,⽐如:桌⾯应⽤,Web应⽤等。Spring的模块化可以允许你只使⽤需要的模块,⽽不必全部引⼊。
⽬录
⼀、整体架构
1. 核⼼容器
2. 数据访问/集成
3. Web
4. AOP
5. Test
⼆、设计理念
三、使⽤场景
1. 典型的Spring web应⽤程序
2. Spring中间层使⽤第三⽅web框架
3. 远程调⽤
4. EJBs-包装现存POJOs
⼀、整体架构
Spring框架是⼀个分层架构,他包含⼀系列的功能要素,并被分为⼤约20个模块,如下图所⽰(很遗
憾,并没有到Spring5的架构图,下图是Spring4的,但结合Spring5的源码来看,该图还是能够体现Spring5的核⼼模块)
这些模块被总结为以下⼏部分。
1. 核⼼容器
Core Container(核⼼容器)包含有Core、Beans、Context和Expression Language模块。Core和Beans模块是框架的基础部分,提供IoC(控制反转)和DI(依赖注⼊)特性。这⾥的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。
Core模块主要包含Spring框架基本的核⼼⼯具类,Spring的其他组件都要使⽤到这个包⾥的类,Core模块是其他组件的基本核⼼。当然你也可以在⾃⼰的应⽤系统中使⽤这些⼯具类。
Beans模块是所有应⽤都要⽤到的,它包含访问配置⽂件、创建和管理Bean以及进⾏Inversion of Control/Dependency
Injection(IoC/DI)操作相关的所有类。
Context模块构建于Core和Beans模块基础之上,提供了⼀种类似于JNDI注册器的框架式的对象访问⽅法。Context模块继承了Beans 的特性,为Spring核⼼提供了⼤量扩展,添加了对国际化(例如资源绑定)、事件传播、资源加载和对Context的透明创建的⽀持。
Context同时也⽀持J2EE的⼀些特性,例如EJB、JMX和基础的远程处理。ApplicationContext接⼝是Context模块的关键。
Expression Language 模块提供了⼀个强⼤的表达式语⾔⽤于在运⾏时查询和操纵对象。它是JSP2.1规范中定义的unifed expression language的⼀个扩展。该语⾔⽀持设置/获取属性的值,属性的分配,⽅法的调⽤,访问数组上下⽂(accession the context of
arrays),容器和索引器,逻辑和算数运算符,命名变量以及从Spring的IoC容器中根据名称检索对象。它也⽀持list投影,选择和⼀般的list聚合。
2. 数据访问/集成
Data Access/Integration(数据访问/集成)层包含有JDBC、ORM、OXM、JMS和Transaction模块,其中:
JDBC模块提供了⼀个JDBC抽象层,它可以消除冗长的JDBC编码和解析数据库⼚商特有的错误代码。
这个模块包含了Spring对JDBC 数据访问进⾏封装的所有类。
ORM(Object Relational Mapping对象关系映射)模块为流⾏的对象-关系映射API,如JPA、JDO、Hibernate、iBatis等,提供了⼀个交互层。利⽤ORM封装包,可以混合使⽤所有Spring提供的特性进⾏O/R映射。如前边提到的简单声明性事务管理。
Spring框架插⼊了若⼲个ORM框架,从⽽提供了ORM的对象关系⼯具,其中包括JDO、Hibernate和iBatisSQL Map。所有这些都遵从Spring的通⽤事务和DAO异常层次结构。
OXM模块提供了⼀个对Object/XML映射实现的抽象层,Object/XML映射实现包括JAXB、Castor、XMLBeans、JiBX和XStream。
JMS(Java Messaging Service)模块主要包含了⼀些制造和消费消息的特性。
Transaction模块⽀持编程和声明性的事务管理,这些事务类必须实现特定的接⼝,并且对所有的POJO都适⽤。
3. Web
Web上下⽂模块建⽴在应⽤程序上下⽂模块之上,为基于Web的应⽤程序提供了上下⽂。所以Spring
框架⽀持与Jakarta Struts的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的⼯作。Web层包含了Web、Web-Servlet、Web-Struts和Web-Porlet模块,具体说明如下。
Web模块提供了基础的⾯向Web的集成特性。例如,多⽂件上传,使⽤servlet listeners初始化IoC容器以及⼀个⾯向Web的应⽤上下⽂。它还包含Spring远程⽀持中Web的相关部分。
Web-Servlet模块(web.servlet.jar)包含Spring的model-view-controller(MVC)的实现。Spring的MVC框架使得模型范围内的代码和web forms之间能够清楚地分离开来,并与Spring框架的其他特性集成在⼀起。
Web-Struts模块提供了对Struts的⽀持,使得类在Spring应⽤中能够与⼀个典型的Struts Web层集成在⼀起,注意,该⽀持在Spring3.0中是deprecated的。
Web-Porlet模块提供了⽤于Portlet环境和Web-Servlet模块的MVC的实现。
4. AOP
AOP模块提供了⼀个符合AOP联盟标准的⾯向切⾯编程的实现,它让你可以定义例如⽅法和切点。从⽽将逻辑代码分开,降低它们之间的耦合性。利⽤source-level的元数据功能,还可以将各种⾏为信息合并到你的代码中,这有点像.Net技术中的attribute概念。
通过配置管理特性,SpringAOP模块直接将⾯向切⾯的编程功能集成到了Spring框架中,所以可以很容易地使Spring框架管理的任何对象⽀持AOP。Spring AOP模块为基于Spring的应⽤程序中的对象提供了事务管理服务。通过使⽤Spring AOP,不⽤依赖EJB组件,就可以将声明性事务管理集成到应⽤程序中。
Aspects模块提供了对AspectJ(⼀个⾯向切⾯的框架,它扩展了Java语⾔)的集成⽀持。
Instrumentation模块提供了class instrumentation ⽀持和classloader实现,使得可以在特定的应⽤服务器上使⽤。
5. Test
Test模块⽀持使⽤JUnit和TestNG对Spring组件进⾏测试。
⼆、设计理念
Spring是⾯向Bean的编程(BOP:Bean Oriented Programming),Bean在Spring中才是真正的主⾓。Bean在Spring中作⽤就像Object对OOP的意义⼀样,没有对象的概念就像没有⾯向对象编程,Spring中没有Bean也就没有Spring存在的意义。Spring提供了IoC 容器通过配置⽂件或者注解的⽅式来管理对象之间的依赖关系。
控制反转(Inversion of Control,缩写为IoC),是⾯向对象编程中的⼀种设计原则,可以⽤来减低计算机代码之间的耦合度。其中最常见的⽅式叫做依赖注⼊(Dependency Injection,简称DI),还有⼀种⽅式叫“依赖查”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由⼀个调控系统内所有对象的外界实体,将其所依赖的对象的引⽤传递给它。也可以说,依赖被注⼊到对象中。
三、使⽤场景
前⾯描述的模块使得Spring成为许多场景中的合理选择,从在资源受限设备上运⾏的嵌⼊式应⽤程序到使⽤Spring事务管理功能和Web框架集成的全⾯的企业应⽤程序。
1. 典型的Spring web应⽤程序
Spring的声明式事务管理功能⽀持web应⽤程序全事务化,就同你使⽤EJB容器管理的事务⼀样。所有你的定制业务逻辑都可以由简单的POJOs实现,并由Spring IoC容器管理。其他服务包括发送email和独⽴于web层的校验,⽽你可以选择何处去执⾏校验规则。Spring的
ORM⽀持同JPA和Hibernate的整合,⽐如,当你使⽤Hibernate时,可以保持原有的映射⽂件及标准Hibernate SessionFactory配置。表单控制器⽆缝整合了web层和域模型,⽆需那些转换HTTP参数到域模型的ActionForms或其他类。
2. Spring中间层使⽤第三⽅web框架
有时情况并不允许你完全切换到⼀个不同的框架。Spring框架不是⼀个要么使⽤全部特性要么什么都⽤不了的解决⽅案,不强制使⽤其中的每个功能。现存的前端如Struts,Tapestry,JSF或其他UI框架都可以同基于Spring的中间层整合在⼀起,从⽽使你能够使⽤Spring事务功能。你只需要使⽤ApplicationContext连接你的业务逻辑以及通过WebApplicationContext整合你的web层。
3. 远程调⽤
你可以使⽤Spring的Hessian-,Rmi-或HttpInvokerProxyFactoryBean类来通过web服务访问现存的代码。远程访问现存应⽤程序并不困难。
4. EJBs-包装现存POJOs
Spring框架还为企业JavaBeans提供了⼀个访问抽象层,使你能够重⽤现有的POJO,并将其包装在⽆状态会话bean中,以便在可能需要声名式安全的可扩展,故障安全的web应⽤程序中使⽤。
摘要:本⽂结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码。若有描述错误之处,欢迎指正。
在正式分析Spring源码之前,我们有必要先来回顾⼀下Spring中最简单的⽤法。尽管我相信您已经对这个例⼦⾮常熟悉了。
Bean是Spring中最核⼼的概念,因为Spring就像是个⼤⽔桶,⽽Bean就像是⽔桶中的⽔,⽔桶脱离了⽔也就没什么⽤处了,那么我们先看看Bean的定义。
public class MySpringBean {
private String str = "mySpringBean";
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
很普通,Bean没有任何特别之处。的确,Spring的⽬的就是让我们的Bean能成为⼀个纯粹的POJO,这也是Spring所追求的。接下来看看配置⽂件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="/2001/XMLSchema-instance"
xsi:schemaLocation="/schema/beans
/schema/beans/spring-beans.xsd">
<bean id="mySpringBean" class="llphone.uc.MySpringBean"/>
</beans>
在上⾯的配置中我们看到了Bean的声明⽅式,接下来看测试代码:
public class BeanFactoryTest {
spring ioc注解
@Test
public void testSimpleLoad() {
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("l"));
MySpringBean bean = (MySpringBean) Bean("mySpringBean");
Assert.assertEquals("testSimpleLoad", "mySpringBean", Str());
}
}
XmlBeanFactory从Spring 3.1版本开始就被废弃了,但源码中未说明废弃的原因......
直接使⽤BeanFactory作为容器对于Spring的使⽤来说并不多见,因为在企业级的应⽤中⼤多数都会使⽤ApplicationContext(后续再介绍两者之间的差异),这⾥只是⽤于测试,让读者更快更好地分析Spring的内部原理。
通过上⾯⼀⾏简单的代码就拿到了MySpringBean实例,但这⾏代码在Spring中却执⾏了⾮常多的逻辑。接下来就来深⼊分析Bean⽅法的实现原理。
摘要:本⽂结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码。若有描述错误之处,欢迎指正。
在⽂章中,我们熟悉了容器的基本⽤法。在这⼀篇,我们开始分析Spring的源码。但是在正式开始熟悉源码之前,有必要了解⼀下Spring中最核⼼的两个类。
1. DefaultListableBeanFactory
XmlBeanFactory继承⾃DefaultListableBeanFactory,⽽DefaultListableBeanFactory是整个bean加载的核⼼部分,是Spring注册及加载bean的默认实现,⽽对于XmlBeanFactory与DefaultListableBeanFactory不同的地⽅其实是在XmlBeanFactory中使⽤了⾃定义的XML读取器XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取,DefaultListableBeanFactory继承了AbstractAutowireCapableBeanFactory并实现了ConfigurableListableBeanFactory以及BeanDefinitionRegistry接⼝。以下是DefaultListableBeanFactory的类图:
从上⾯的类图中,我们可以清晰地从全局⾓度了解DefaultListableBeanFactory的脉络。接下来先了解⼀下上⾯类图中各个类的作⽤。
AliasRegistry定义对alias的简单增删改查等操作
SimpleAliasRegistry主要使⽤map作为alias的缓存,并对接⼝AliasRegistry进⾏实现
SingletonBeanRegistry定义对单例的注册及获取
BeanFactory定义获取bean及bean的各种属性
DefaultSingletonBeanFactory对接⼝SingletonBeanRegistry各函数的实现
HierarchicalBeanFactory继承BeanFactory,也就是在BeanFactory定义的功能的基础上增加了对parentFactory的⽀持BeanDefinitionRegistry定义对BeanDefinition的各种增删改操作
FactoryBeanRegistrySupport在DefaultSingletonBeanRegistry的基础上增加了对FactoryBean的特殊处理功能ConfigurableBeanFactory提供配置Factory的各种⽅法
ListableBeanFactory根据各种条件获取bean的配置清单
AbstractBeanFactory综合FactoryBeanRegistrySupport和ConfigurableBeanFactory的功能AutowireCapableBeanFactory提供创建bean、⾃动注⼊,初始化以及应⽤bean的后处理器AbstractAu
towireCapableBeanFactory综合AbstractBeanFactory并对接⼝AutowireCapableBeanFactory进⾏实现ConfigurableListableBeanFactory BeanFactory配置清单,指定忽略类型及接⼝等
DefaultListableBeanFactory综合上⾯所有功能,主要是对Bean注册后的处理
XmlBeanFactory对DefaultListableBeanFactory进⾏了扩展,主要⽤于从XML⽂档中读取BeanDefinition,对于注册及获取Bean都是使⽤从⽗类DefaultListableBeanFactory继承的⽅法去实现,⽽唯独与⽗类不同的个性化实现就是增加了XmlBeanDefinitionReader类型的reader属性。在XmlBeanFactory中主要使⽤reader属性对资源⽂件进⾏读取和注册。
2. XmlBeanDefinitionReader
XML配置⽂件的读取时Spring的重要功能,因为Spring的⼤部分功能都是以配置作为切⼊点的,那么我们可以从XmlBeanDefinitionReader 中梳理⼀下资源⽂件读取、解析及注册的⼤致脉络。⾸先我们看看各个类的功能。
ResourceLoader定义资源加载器,主要应⽤于根据给定的资源⽂件地址返回对应的Resource BeanDefinitionReader主要定义资源⽂件读取并转换为BeanDefinition的各个功能
EnvironmentCapable定义获取Environment⽅法
DocumentLoader定义从资源⽂件加载到转换为Document的功能
AbstractBeanDefinitionReader对EnvironmentCapable、BeanDefinitionReader类定义的功能进⾏实现BeanDefinitionDocumentReader定义读取Document并注册BeanDefiniton功能
BeanDefinitionParserDelegate定义解析Element的各种⽅法
通过以上分析,我们可以梳理出整个XML配置⽂件读取的⼤致流程,如下图所⽰:
在XmlBeanDifinitonReader中主要包含以下⼏个步骤的处理:
1)通过继承⾃AbstractBeanDefinitionReader中的⽅法,来使⽤ResourceLoader将资源⽂件路径转换为对应的Resource⽂件。
2)通过DocumentLoader对Resource⽂件进⾏转换,将Resource⽂件转换为Document⽂件。
3)通过实现接⼝BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader类对Document进⾏解析,并使⽤BeanDefinitionParserDelegate对Element进⾏解析。
摘要:本⽂结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码。若有描述错误之处,欢迎指正。
经过和两篇⽂章,我们已经对Spring的容器功能有了⼀个⼤致的了解,尽管你可能还很迷糊,但是不要紧,接下来我们会详细探索每个步骤的实现。⾸先要深⼊分析的是以下功能的代码实现:
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("l"));
通过XmlBeanFactory初始化时序图,我们看下上⾯代码的执⾏逻辑:
时序图从BeanFactoryTest测试类开始,通过时序图我们可以⼀⽬了然地看到整个逻辑处理顺序。先调⽤了ClassPathResource的构造函数来构造Resource资源⽂件的实例对象,后续的资源处理就可以⽤Resource提供的各种服务来操作了,当我们有了Resource后就可以进⾏XmlBeanFactory的初始化了。那么Resource⽂件是如何封装的呢?
1. 配置⽂件封装
Spring的配置⽂件读取是通过ClassPathResource进⾏封装的,如new ClassPathResource("l"),那么ClassPathResource完成了什么功能呢?
在Java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHander)来处理不同来源的资源的读取逻辑,⼀般handler的类型使⽤不同的前缀(协议,Protocol)来识别,如“file:”、"http:
"、"jar:"等,然⽽URL没有默认定义相对Classpath或ServletContext等资源的handler,虽然可以注册⾃⼰的URLStreamHandler来解析特定的URL前缀(协议),⽐如“classpath:”,然⽽这需要了解URL的实现机制,⽽且URL也没有提供⼀些基本的⽅法,如检查当前资源是否存在、检查当前资源是否可读等⽅法。因⽽Spring对其内部使⽤到的资源实现了⾃⼰的抽象结构:Resource接⼝来封装底层资源。
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
boolean exists();
default boolean isReadable() {
return true;
}
default boolean isOpen() {
return false;
}
default boolean isFile() {
return false;
}
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
default ReadableByteChannel readableChannel() throws IOException {
wChannel(getInputStream());
}
long contentLength() throws IOException;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论