Spring源码阅读入门指引
本文大概的对IOC和AOP进行了解,入门先到这一点便已经有了大概的印象了,详细内容请看下文。
AD:
本文说明2点:
1.阅读源码的入口在哪里?
2.入门前必备知识了解:IOC和AOP
一、我们从哪里开始
1.准备工作:在官网上下载了Spring源代码之后,导入Eclipse,以方便查询。
2.打开我们使用Spring的项目工程,找到Web.xml这个网站系统配置文件,在其中找到Spring的初始化信息:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
由配置信息可知,我们开始的入口就这里ContextLoaderListener这个监听器。
在源代码中我们找到了这个类,它的定义是:
public class ContextLoaderListener extends ContextLoader
implements ServletContextListener {
…
/**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
...
}
该类继续了ContextLoader并实现了监听器,关于Spring的信息载入配置、初始化便是从这里开始了,具体其他阅读另外写文章来深入了解。
二、关于IOC和AOP
关于Spring IOC 网上很多相关的文章可以阅读,那么我们从中了解到的知识点是什么?
1)IOC容器和AOP切面依赖注入是Spring的核心。
IOC容器为开发者管理对象之间的依赖关系提供了便利和基础服务,其中Bean工厂(BeanFactory)和上下文(ApplicationContext)就是IOC的表现形式。
BeanFactory是个接口类,只是对容器提供的最基本服务提供了定义,而DefaultListTableBeanFactory、XmlBeanFactory、ApplicationContext等都是具体的实现。
接口:
public interface BeanFactory {
//这里是对工厂Bean的转义定义,因为如果使用bean的名字检索IOC容器得到的对象是工厂Bean生成的对象,
//如果需要得到工厂Bean本身,需要使用转义的名字来向IOC容器检索
String FACTORY_BEAN_PREFIX = "&";
//这里根据bean的名字,在IOC容器中得到bean实例,这个IOC容器就象一个大的抽象工厂,用户可以根据名字得到需要的bean
//在Spring中,Bean和普通的JAVA对象不同在于:
//Bean已经包含了我们在Bean定义信息中的依赖关系的处理,同时Bean是已经被放到IOC容器中进行管理了,有它自己的生命周期
Object getBean(String name) throws BeansException;
//这里根据bean的名字和Class类型来得到bean实例,和上面的方法不同在于它会抛出异常:如果根名字取得的bean实例的Class类型和需要的不同的话。
Object getBean(String name, Class requiredType) throws BeansException;
//这里提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean containsBean(String name);
//这里根据bean名字得到bean实例,并同时判断这个bean是不是单例,在配置的时候,默认的Bean被配置成单例形式,如果不需要单例形式,需要用户在Bean定义信息中标注出来,这样IOC容器在每次接受到用户的getBean要求的时候,会生成一个新的Bean返回给客户使用 - 这就是Prototype形式
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//这里对得到bean实例的Class类型
Class getType(String name) throws NoSuchBeanDefinitionException;
//这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}
实现:
XmlBeanFactory的实现是这样的:
public class XmlBeanFactory extends DefaultListableBeanFactory {
//这里为容器定义了一个默认使用的bean定义读取器,在Spring的使用中,Bean定义信息的读取是容器初始化的一部分,但是在实现上是和容器的注册以及依赖的注入是分开的,这样可以使用灵活的 bean定义读取机制。
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
//这里需要一个Resource类型的Bean定义信息,实际上的定位过程是由Resource的构建过程来完成的。
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
//在初始化函数中使用读取器来对资源进行读取,得到bean定义信息。这里完成整个IOC容器对Bean定义信息的载入和注册过程
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws
BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
我们可以看到IOC容器使用的一些基本过程:
如:DefaultListableBeanFactory
ClassPathResource res = new ClassPathResource("beans.xml");//读取配置文件
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
这些代码演示了以下几个步骤:
创建IOC配置文件的抽象资源
创建一个BeanFactory,这里我们使用DefaultListableBeanFactory;
创建一个载入bean定义信息的读取器,这里使用XmlBeanDefinitionReader来载入XML形式的bean定义信息,配置给BeanFactory;
从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成,这样完成整个载入和注册bean定义的过程。我们的IOC容器就建立起来了。
再简单的说,我的系统在启动时候,会完成的动作就是:
1.由ResourceLoader获取资源文件,也即bean的各种配置文件;
2.由BeanDefintion对配置文件的定义信息的载入;
3.用BeanDefinitionRegistry接口来实现载入bean定义信息并向IOC容器进行注册。
注意,IOC容器和上下文的初始化一般不包含Bean的依赖注入的实现。
2)AOP这个过程并不是在注册bean的过程实现的。
我们只看到在处理相关的Bean属性的时候,使用了RuntimeBeanReference对象作为依赖信息的纪录。
在IOC容器已经载入了用户定义的Bean信息前提下,这个依赖注入的过程是用户第一次向IOC容器索要Bean的时候触发的,或者是我们可以在Bean定义信息中通过控制lazy-init属性来使得容器完成对Bean的预实例化 - 这个预实例化也是一个完成依赖注入的过程。
我们说明一下过程:
1.用户向IOC容器请求Bean。
2.系统先在缓存中查找是否有该名称的Bean(去各个BeanFactory去查找)
3.没有的话就去创建Bean并进行依赖注入,并且这个请求将被记录起来。
请求Bean具体的实现:
代码入口在DefaultListableBeanFactory的基类AbstractBeanFactory中:
public Object getBean(String name, Class requiredType, final Object[] args) throwsBeansException {
...
Object sharedInstance = getSingleton(beanName);//先去缓存取
if (sharedInstance != null) {
...
if (containsBeanDefinition(beanName)) {
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false);
bean = getObjectForBeanInstance(sharedInstance, name,mergedBeanDefinition);
}
else {
bean = getObjectForBeanInstance(sharedInstance, name, null);
}
}
else {
}
...
}
注入Bean具体的实现:
具体的bean创建过程和依赖关系的注入在createBean中,这个方法在AbstractAutowireCapableBeanFactory中给出了实现:
protected Object createBean(String beanName, RootBeanDefinition
mergedBeanDefinition, Object[] args)
throws BeanCreationException {
// Guarantee initialization of beans that the current one depends on.
// 这里对取得当前bean的所有依赖bean,确定能够取得这些已经被确定的bean,如果没有被创建,那么这个createBean会被这些IOC
// getbean时创建这些bean
if (mergedBeanDefinition.getDependsOn() != null) {
for (int i = 0; i < mergedBeanDefinition.getDependsOn().length; i++) {
getBean(mergedBeanDefinition.getDependsOn()[i]);
}
}
........
// 这里是实例化bean对象的地方,注意这个BeanWrapper类,是对bean操作的主要封装类
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mergedBeanDefinition,args);
}
Object bean = instanceWrapper.getWrappedInstance();
......
//这个populate方法,是对已经创建的bean实例进行依赖注入的地方,会使用到在loadBeanDefinition的时候得到的那些propertyValue来对bean进行注入。
if (continueWithPropertyPopulation) {
populateBean(beanName, mergedBeanDefinition, instanceWrapper);
}
//这里完成客户自定义的对bean的一些初始化动作
Object originalBean = bean;
bean = initializeBean(beanName, bean, mergedBeanDefinition);
// Register bean as disposable, and also as dependent on specified "dependsOn"beans.
registerDisposableBeanIfNecessary(beanName, originalBean,mergedBeanDefinition);
return bean;
}
.........
}
这就是整个依赖注入的部分处理过程,在这个过程中起主要作用的是WrapperImp ,这个Wrapper不是一个简单的对bean对象的封装,因为它需要处理在beanDefinition中的信息来迭代的处理依赖注入。
到这里,这是简单的,大概的对IOC和AOP进行了解,入门先到这一点便已经有了大概的印象了。
Spring源码阅读入门指引的更多相关文章
- Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)
我们到底能走多远系列(33) 扯淡: 各位: 命运就算颠沛流离 命运就算曲折离奇 命运就算恐吓着你做人没趣味 别流泪 心酸 更不应舍弃 ... 主题: Spring源码阅读还在继 ...
- 初始化IoC容器(Spring源码阅读)
初始化IoC容器(Spring源码阅读) 我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? ...
- Spring源码阅读-ApplicationContext体系结构分析
目录 继承层次图概览 ConfigurableApplicationContext分析 AbstractApplicationContext GenericApplicationContext Gen ...
- Sping学习笔记(一)----Spring源码阅读环境的搭建
idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ...
- Spring源码阅读笔记02:IOC基本概念
上篇文章中我们介绍了准备Spring源码阅读环境的两种姿势,接下来,我们就要开始探寻这个著名框架背后的原理.Spring提供的最基本最底层的功能是bean容器,这其实是对IoC思想的应用,在学习Spr ...
- Spring源码阅读 之 配置的读取,解析
在上文中我们已经知道了Spring如何从我们给定的位置加载到配置文件,并将文件包装成一个Resource对象.这篇文章我们将要探讨的就是,如何从这个Resouce对象中加载到我们的容器?加载到容器后又 ...
- 搭建 Spring 源码阅读环境
前言 有一个Spring源码阅读环境是学习Spring的基础.笔者借鉴了网上很多搭建环境的方法,也尝试了很多,接下来总结两种个人认为比较简便实用的方法.读者可根据自己的需要自行选择. 方法一:搭建基础 ...
- Spring源码阅读系列总结
最近一段时间,粗略的查看了一下Spring源码,对Spring的两大核心和Spring的组件有了更深入的了解.同时在学习Spring源码时,得了解一些设计模式,不然阅读源码还是有一定难度的,所以一些重 ...
- Spring源码阅读笔记
前言 作为一个Java开发者,工作了几年后,越发觉力有点不从心了,技术的世界实在是太过于辽阔了,接触的东西越多,越感到前所未有的恐慌. 每天捣鼓这个捣鼓那个,结果回过头来,才发现这个也不通,那个也不精 ...
随机推荐
- UOJ#179. 线性规划[模板]
传送门 http://uoj.ac/problem/179 震惊,博主竟然还不会线性规划! 单纯形实在学不会啊……背个板子当黑盒用…… 学(chao)了NanoApe dalao的板子 #includ ...
- 【NOIP】2016 换教室
[算法]期望DP+floyd [题解]用floyd预处理最短距离. 注意重边与自环——图论双毒!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! QAQ 然后搞清楚方案和概率的问 ...
- ajax post请求json数据在spring-controller解析
1.前端post请求数据: userInfo=[{"id":"5","uname":"小李","phone&q ...
- bzoj 1079 DP
比较容易看出来是DP,但是如果我们记录每一种颜色还剩多少种的话,消耗的转移的时间复杂度5^15,但是我们考虑到每一种颜色,如果数量相同的话,其实是等效的,所以我们用w[a][b][c][d][e][l ...
- Eureka Server的高可用
Eureka Server进行互相注册的方式来实现高可用的部署,所以我们只需要将Eureke Server配置其他可用的serviceUrl就能实现高可用部署 创建application-peer1. ...
- Java常用开发思想与知识点小记(一)
1. 子类在覆盖父类的方法时,不能抛出比父类更多的异常(儿子不能比父亲干更多的坏事),所以只能捕捉异常,通常在web层捕获异常,给用户一个友好提示. 2.Java内存模型与并发编程三个特性 htt ...
- Java线程(一)
1. java什么叫线程安全?什么叫不安全? 就是线程同步的意思,就是当一个程序对一个线程安全的方法或者语句进行访问的时候,其他的不能再对他进行操作了,必须等到这次访问结束以后才能对这个线程安全的方法 ...
- 微信小程序实现图片上传,预览,删除
wxml: <view class='imgBox'> <image class='imgList' wx:for="{{imgs}}" wx:for-item= ...
- HTML+CSS+JS(+Vue)写一个通讯录组件
求各位大大的Star(*/ω\*). 没有录屏,所以上传的是图片.后面已补充录屏效果. 效果:(主要是参考小米Note3的通讯录的效果做的) 主要功能: 1. 滚动后,通讯录的模块标题会固定在顶部(图 ...
- js解析与序列化json数据(一)json.stringify()的基本用法
对象有两个方法:stringify()和parse().在最简单的情况下,这两个方法分别用于把JavaScript对象序列化为JSON字符串和把JSON字符串解析为原生JavaScript 早期的JS ...