⑴ [Spring boot源码解析] 2 启动流程分析
在了解 Spring Boot 的启动流程的时候,我们先看一下一个Spring Boot 应用是如何启动的,如下是一个简单的 SpringBoot 程序,非常的简洁,他是如何做到的呢,我们接下来就将一步步分解。
我们追踪 SpringApplication.run() 方法,其实最终它主要的逻辑是新建一个 SpringApplication ,然后调用他的 run 方法,如下:
我们先来看一下创建 SpringApplication 的方法:
在将Main class 设置 primarySources 后,调用了 WebApplicationType.deceFromClasspath() 方法,该方法是为了检查当前的应用类型,并设置给 webApplicationType 。 我们进入 deceFromClasspath 方法 :
这里主要是通过类加载器判断是否存在 REACTIVE 相关的类信息,假如有就代表是一个 REACTIVE 的应用,假如不是就检查是否存在 Servelt 和 ,假如都没有,就代表应用为非 WEB 类应用,返回 NONE ,默认返回 SERVLET 类型,我们这期以我们目前最常使用的 SERVLET 类型进行讲解,所以我们在应用中引入了 spring-boot-starter-web 作为依赖:
他会包含 Spring-mvc 的依赖,所以就包含了内嵌 tomcat 中的 Servlet 和 Spring-web 中的 ,因此返回了 SERVLET 类型。
回到刚才创建 SpringApplication 的构建方法中,我们设置完成应用类型后,就寻找所有的 Initializer 实现类,并设置到 SpringApplication 的 Initializers 中,这里先说一下 getSpringFactoriesInstances 方法,我们知道在我们使用 SpringBoot 程序中,会经常在 META-INF/spring.factories 目录下看到一些 EnableAutoConfiguration ,来出发 config 类注入到容器中,我们知道一般一个 config 类要想被 SpringBoot 扫描到需要使用 @CompnentScan 来扫描具体的路径,对于 jar 包来说这无疑是非常不方便的,所以 SpringBoot 提供了另外一种方式来实现,就是使用 spring.factories ,比如下面这个,我们从 Springboot-test 中找到的例子,这里先定义了一个ExampleAutoConfiguration,并加上了 Configuration 注解:
然后在 spring.factories 中定义如下:
那这种方式是怎么实现的你,这就要回到我们刚才的方法 getSpringFactoriesInstances :
我们先来看一下传入参数,这里需要注意的是 args,这个是初始化对应 type 的时候传入的构造参数,我们先看一下 SpringFactoriesLoader#loadFactoryNames 方法:
首先是会先检查缓存,假如缓存中存在就直接返回,假如没有就调用 classLoader#getResources 方法,传入 META-INF/spring.factories ,即获取所有 jar 包下的对应文件,并封装成 UrlResource ,然后使用 PropertiesLoaderUtils 将这些信息读取成一个对一对的 properties,我们观察一下 spring.factories 都是按 properties 格式排版的,假如有多个就用逗号隔开,所以这里还需要将逗号的多个类分隔开来,并加到 result 中,由于 result 是一个 LinkedMultiValueMap 类型,支持多个值插入,最后放回缓存中。最终完成加载 META-INF/spring.factories 中的配置,如下:
我们可以看一下我们找到的 initializer 有多少个:
在获取到所有的 Initializer 后接下来是调用 方法进行初始化。
这里的 names 就是我们上面通过类加载器加载到的类名,到这里会先通过反射生成 class 对象,然后判断该类是否继承与 ApplicationContextInitializer ,最后通过发射的方式获取这个类的构造方法,并调用该构造方法,传入已经定义好的构造参数,对于 ApplicationContextInitializer 是无参的构造方法,然后初始化实例并返回,回到原来的方法,这里会先对所有的 ApplicationContextInitializer 进行排序,调用 #sort(instances) 方法,这里就是根据 @Order 中的顺序进行排序。
接下来是设置 ApplicationListener ,我们跟进去就会发现这里和上面获取 ApplicationContextInitializer 的方法如出一辙,最终会加载到如图的 15 个 listener (这里除了 外,其他都是 SpringBoot 内部的 Listener):
在完成 SpringApplication 对象的初始化后,我们进入了他的 run 方法,这个方法几乎涵盖了 SpringBoot 生命周期的所有内容,主要分为九个步骤,每一个步骤这里都使用注解进行标识:
主要步骤如下:
第一步:获取 SpringApplicationRunListener, 然后调用他的 staring 方法启动监听器。
第二步:根据 SpringApplicationRunListeners以及参数来准备环境。
第三步:创建 Spring 容器。
第四步:Spring 容器的前置处理。
第五步:刷新 Spring 容器。
第六步: Spring 容器的后置处理器。
第七步:通知所有 listener 结束启动。
第八步:调用所有 runner 的 run 方法。
第九步:通知所有 listener running 事件。
我们接下来一一讲解这些内容。
我们首先看一下第一步,获取 SpringApplicationRunListener :
这里和上面获取 initializer 和 listener 的方式基本一致,都是通过 getSpringFactoriesInstances , 最终只找到一个类就是: org.springframework.boot.context.event.EventPublishingRunListener ,然后调用其构造方法并传入产生 args , 和 SpringApplication 本身:
我们先看一下构造函数,首先将我们获取到的 ApplicationListener 集合添加到initialMulticaster 中, 最后都是通过操作 来进行广播,我,他继承于 ,我们先看一下他的 addApplicationListener 方法:
我们可以看出,最后是放到了 applicationListenters 这个容器中。他是 defaultRetriever 的成员属性, defaultRetriever 则是 的私有类,我们简单看一下这个类:
我们只需要看一下这里的 getApplicationListeners 方法,它主要是到 beanFactory 中检查是否存在多的 ApplicationListener 和旧的 applicationListeners 组合并返回,接着执行 listener 的 start 方法,最后也是调用了 的 multicastEvent 查找支持对应的 ApplicationEvent 类型的通知的 ApplicationListener 的 onApplicationEvent 方法 ,这里除了会:
筛选的方法如下,都是调用了对应类型的 supportsEventType 方法 :
如图,我们可以看到对 org.springframework.boot.context.event.ApplicationStartingEvent 感兴趣的有5个 Listener
环境准备的具体方法如下:
首先是调用 getOrCreateEnvironment 方法来创建 environment ,我们跟进去可以发现这里是根据我们上面设置的环境的类型来进行选择的,当前环境会创建 StandardServletEnvironment
我们先来看一下 StandardServletEnvironment 的类继承关系图,我们可以看出他是继承了 AbstractEnvironment :
他会调用子类的 customizePropertySources 方法实现,首先是 StandardServletEnvironment 的实现如下,他会添加 servletConfigInitParams , servletContextInitParams , jndiProperties 三种 properties,当前调试环境没有配置 jndi properties,所以这里不会添加。接着调用父类的 customizePropertySources 方法,即调用到了 StandardEnvironment 。
我们看一下 StandardEnvironment#customizePropertySources 方法,与上面的三个 properties 创建不同,这两个是会进行赋值的,包括系统环境变量放入 systemEnvironment 中,jvm 先关参数放到 systemProperties 中:
这里会添加 systemEnvironment 和 systemProperties 这两个 properties,最终拿到的 properties 数量如下 4个:
在创建完成 Environment 后,接下来就到了调用 configureEnvironment 方法:
我们先看一下 configurePropertySources 方法,这里主要分两部分,首先是查询当前是否存在 defaultProperties ,假如不为空就会添加到 environment 的 propertySources 中,接着是处理命令行参数,将命令行参数作为一个 CompositePropertySource 或则 添加到 environment 的 propertySources 里面,
接着调用 ConfigurationPropertySources#attach 方法,他会先去 environment 中查找 configurationProperties , 假如寻找到了,先检查 configurationProperties 和当前 environment 是否匹配,假如不相等,就先去除,最后添加 configurationProperties 并将其 sources 属性设置进去。
回到我们的 prepareEnvironment 逻辑,下一步是通知观察者,发送 事件,调用的是 SpringApplicationRunListeners#environmentPrepared 方法,最终回到了 #multicastEvent 方法,我们通过 debug 找到最后对这个时间感兴趣的 Listener 如下:
其主要逻辑如下:
这个方法最后加载了 PropertySourceLoader , 这里主要是两种,一个是用于 Properties 的,一个是用于 YAML 的如下:
其中 apply 方法主要是加载 defaultProperties ,假如已经存在,就进行替换,而替换的目标 PropertySource 就是 load 这里最后的一个 consumer 函数加载出来的,这里列一下主要做的事情:
1、加载系统中设置的所有的 Profile 。
2、遍历所有的 Profile ,假如是默认的 Profile , 就将这个 Profile 加到 environment 中。
3、调用load 方法,加载配置,我们深入看一下这个方法:
他会先调用 getSearchLocations 方法,加载所有的需要加载的路径,最终有如下路径:
其核心方法是遍历所有的 propertySourceLoader ,也就是上面加载到两种 propertySourceLoader ,最红 loadForFileExtension 方法,加载配置文件,这里就不展开分析了,说一下主要的作用,因为每个 propertySourceLoader 都有自己可以加载的扩展名,默认扩展名有如下四个 properties, xml, yml, yaml,所以最终拿到文件名字,然后通过 - 拼接所有的真实的名字,然后加上路径一起加载。
接下来,我们分析 BackgroundPreinitializer ,这个方法在接收 ApplicationPrepareEnvironment 事件的时候真正调用了这份方法:
1、 ConversionServiceInitializer 主要负责将包括 日期,货币等一些默认的转换器注册到 formatterRegistry 中。
2、 ValidationInitializer 创建 validation 的匹配器。
3、 MessageConverterInitializer 主要是添加了一些 http 的 Message Converter。
4、 JacksonInitializer 主要用于生成 xml 转换器的。
接着回到我们将的主体方法, prepareEnvironment 在调用完成 listeners.environmentPrepared(environment) 方法后,调用 bindToSpringApplication(environment) 方法,将 environment 绑定到 SpirngApplication 中。
接着将 enviroment 转化为 StandardEnvironment 对象。
最后将 configurationProperties 加入到 enviroment 中, configurationProperties 其实是将 environment 中其他的 PropertySource 重新包装了一遍,并放到 environment 中,这里主要的作用是方便 进行解析。
它主要是检查是否存在 spring.beaninfo.ignore 配置,这个配置的主要作用是设置 javaBean 的内省模式,所谓内省就是应用程序在 Runtime 的时候能检查对象类型的能力,通常也可以称作运行时类型检查,区别于反射主要用于修改类属性,内省主要用户获取类属性。那么我们什么时候会使用到内省呢,java主要是通过内省工具 Introspector 来完成内省的工作,内省的结果通过一个 Beaninfo 对象返回,主要包括类的一些相关信息,而在 Spring中,主要是 BeanUtils#Properties 会使用到,Spring 对内省机制还进行了改进,有三种内省模式,如下图中红色框框的内容,默认情况下是使用 USE_ALL_BEANINFO。假如设置为true,就是改成第三中 IGNORE_ALL_BEANINFO
首先是检查 Application的类型,然后获取对应的 ApplicationContext 类,我们这里是获取到了 org.springframework.boot.web.servlet.context. 接着调用 BeanUtils.instantiateClass(contextClass); 方法进行对象的初始化。
最终其实是调用了 的默认构造方法。我们看一下这个方法做了什么事情。这里只是简单的设置了一个 reader 和一个 scanner,作用于 bean 的扫描工作。
我们再来看一下这个类的继承关系
这里获取 ExceptionReporter 的方式主要还是和之前 Listener 的方式一致,通过 getSpringFactoriesInstances 来获取所有的 SpringBootExceptionReporter 。
其主要方法执行如下:
⑵ Spring Tx源码解析(二)
上一篇 我们介绍了 spring-tx 中的底层抽象,本篇我们一起来看看围绕这些抽象概念 spring-tx 是如何打造出声明式事务的吧。笼统的说, spring-tx-5.2.6.RELEASE 的实现主要分为两个部分:
这两部分彼此独立又相互成就,并且每个部分都有着大量的源码支撑,本篇我们先来分析 spring-tx 中的AOP部分吧。
EnableTransactionManagement 注解想必大家都很熟悉了,它是启用 Spring 中注释驱动的事务管理功能的关键。
EnableTransactionManagement 注解的主要作用是向容器中导入 ,至于注解中定义的几个属性在 Spring AOP源码解析 中有过详细分析,这里就不再赘述了。
由于我们并没有使用 AspectJ ,因此导入容器的自然是 这个配置类。
这个配置类的核心是向容器中导入一个类型为 的Bean。这是一个 PointcutAdvisor ,它的 Pointcut 是 , Advice 是 TransactionInterceptor 。
利用 TransactionAttributeSource 解析 @Transactional 注解的能力来选取标注了 @Transactional 注解的方法,而 TransactionInterceptor 则根据应用提出的需求(来自对 @Transactional 注解的解析)将方法增强为事务方法,因此 可以识别出那些标注了 @Transactional 注解的方法,为它们应用上事务相关功能。
TransactionInterceptor 能对方法进行增强,但是它却不知道该如何增强,比如是为方法新开一个独立事务还是沿用已有的事务?什么情况下需要回滚,什么情况下不需要?必须有一个‘人’告诉它该如何增强,这个‘人’便是 TransactionAttributeSource 。
@Transactional 注解定义了事务的基础信息,它表达了应用程序期望的事务形态。 TransactionAttributeSource 的主要作用就是解析 @Transactional 注解,提取其属性,包装成 TransactionAttribute ,这样 TransactionInterceptor 的增强便有了依据。
前面我们已经见过, spring-tx 使用 来做具体的解析工作,其父类 定义了解析 TransactionAttribute 的优先级,核心方法是 computeTransactionAttribute(...) 。
默认只解析 public 修饰的方法,这也是导致 @Transactional 注解失效的一个原因,除此之外它还实现了父类中定义的两个模板方法:
同时为了支持 EJB 中定义的 javax.ejb.TransactionAttribute 和 JTA 中定义的 javax.transaction.Transactional 注解, 选择将实际的提取工作代理给 TransactionAnnotationParser 。Spring 提供的 @Transactional 注解由 进行解析。
的源码还是很简单的,它使用 AnnotatedElementUtils 工具类定义的 find 语义来获取 @Transactional 注解信息。 RuleBasedTransactionAttribute 中 rollbackOn(...) 的实现还是挺有意思的,其它的都平平无奇。
RollbackRuleAttribute 是用来确定在发生特定类型的异常(或其子类)时是否应该回滚,而 NoRollbackRuleAttribute 继承自 RollbackRuleAttribute ,但表达的是相反的含义。 RollbackRuleAttribute 持有某个异常的名称,通过 getDepth(Throwable ex) 算法来计算指定的 Throwable 和持有的异常在继承链上的距离。
程序猿只有在拿到需求以后才能开工, TransactionInterceptor 也一样,有了 TransactionAttributeSource 之后就可以有依据的增强了。观察类图, TransactionInterceptor 实现了 MethodInterceptor 接口,那么自然要实现接口中的方法:
可以看到, TransactionInterceptor 本身是没有实现任何逻辑的,它更像一个适配器。这样分层以后, TransactionAspectSupport 理论上就可以支持任意类型的 Advice 而不只是 MethodInterceptor 。实现上 TransactionAspectSupport 确实也考虑了这一点,我们马上就会看到。
invokeWithinTransaction(...) 的流程还是非常清晰的:
第一步前文已经分析过了,我们来看第二步。
TransactionInfo 是一个非常简单的类,我们就不费什么笔墨去分析它了。接着看第三步,这一步涉及到两个不同的操作——提交或回滚。
至此, TransactionInterceptor 于我们而言已经没有任何秘密了。
本篇我们一起分析了 spring-tx 是如何通过 spring-aop 的拦截器将普通方法增强为事务方法的,下篇就该说道说道 PlatformTransactionManager 抽象下的事务管理细节啦,我们下篇再见~~
⑶ 急求!spring源码怎么运行,怎么看
1、在你的MyEclipse上安装一个反编译插件,这样,所有的class文件都能看到它的源代码
2、建议你用jad MyEclipse反编译插件。
3、要相应jar包,我上传给你了。
4、MyEclipse10是在(MyEclipse安装目录)\MyEclipse 10\dropins下建立一个文件夹jad,可能myeclipse的版本不一样,路径会有所区别。在新建的jad文件夹中新建两个文件夹:plugins,features,然后将下载来的jar放入到plugins文件夹中。
5、还要用的jad.exe,在上面的压缩包里也有。把这个最好放到你的jdk的bin目录下。
6、打开:Window->Preferences->Java->JadClipse.
1)Path to decompiler,这里设置反编译工具jad的全路径名,我的是
D:\Program Files\Java\jdk1.7.0\bin\jad.exe
2)Directory for temporary files,这里设置临时文件路径。
这个不用管。就默认的哪个样子就可以。
7、注意:安装完成后,myeclipse没有自动将JadClipse Class File Viewer设置成class文件的缺省打开方式,设置方法如下:
在Eclipse的Windows—> Perference—>General->Editors->File Associations中修改“*.class”默认关联的编辑器为“JadClipse Class File Viewer”。
9、设置完成,找一个jar包中的*.class文件,myeclipse将自动反编译
⑷ 如何查看spring源码
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);
}
⑸ 如何阅读Spring源码
如果你是一名JAVA开发人员,你一定用过Spring Framework。 作为一款非常经典的开源框架,从2004年发布的1.0版本到现在的5.0版本,已经经历了16年的洗礼。可以说这是一款老框架了(当然这里的老仅仅指的是时间).
在这16年中,涌现了很多技术,大多昙花一现,但Spring经久不衰。随着技术的演进与版本的更迭,Spring框架总能不落后于技术的潮流,总能紧跟时代的步伐。
究竟是什么样的设计可以让它经久不衰?究竟它体现了一种什么样的设计哲学?
阅读Spring源码之前的准备:
熟练使用Spring框架:只有当你熟练了,阅读其源码水到渠成
扎实的JAVA基础:如果你不具备扎实的JAVA基础,那么请不要轻易尝试,否则只是浪费宝贵的时间。那么怎么样才算是基础扎实呢?此处暂列出一些基本精通的东西,如集合框架(数据结构的实现),多线程技术(线程池、线程安全、并发容器等),反射与注解(Spring中大量的使用到了反射),面向对象(面向对象不仅仅指的是对象抽象,还包括对象集成、多态等各种机制)以及JAVA的一些其他高级特性(如基于java接口的动态代理等)
设计模式:Spring中大量的使用了各种设计模式已提高其健壮性,因此对于设计模式需要一些深刻的理解.
绘制UML能力:在阅读的时候,肯定是要探究其核心对象之间的关系,而UML类图不失为一种非常直观的方式.
激情与毅力:学习一件事物的时候激情是非常重要的,但毅力才是体现成败的关键.
如何开始阅读:
了解其整体架构与核心概念以便建立Spring的模型
从框架入口开始抽丝剥茧,理解其每一个核心概念以及作用,并将这些核心技术点融汇起来
探究每一个核心的实现细节(UML图、跑单元测试用例、DEBUG,体悟)
以上,仅为我自己阅读源码的方式。
工作中一直得益于Spring带来的便利,对于Spring的使用应该很是熟练了,但对于它的设计理念与整体架构却没有一个非常清晰明了的认知。
很久之前就想过要阅读Spring的源码了,但一直是无从下手的状态。也曾尝试着debug去强行阅读, 但不了解的地方甚多。 于是反省自己java的基础,反省自己阅读的方式。
经过一段很长时间的查缺补漏, 终于可以顺利的去阅读Spring的源码了,所以在此处加以记录。
⑹ 怎么阅读Spring源码
学习源码是一件非常耗时费力的事情,需要有足够的时间和持久的耐心,下面是我阅读郝佳老师的《Spring源码深度解析》所做的记录,书中以Spring3.2讲解,使用jdk1.7。
准备工作
1. 安装github:现在spring源代码都在github管理,所以首先需要下载githup,下;
2. 安装gradle构建工具: 下载完后进行解压到任意盘符,然后增加环境变量GRADLE_HOME,并在环境变量bin中增加%GRADLE_HOME%/bin,打开DOS窗口,运行gradle -v,出现版本号等信息,表示安装成功;
3. 下载Spring源码:首先打开git shell,切换到你的工作目录,然后输入以下命令:git clone git://github.com/SpringSource/Spring-framework.git,后面一串是源码下载地址。大概半小时的样子,就可以下载完成,这时候在你的工作目录中就会出现Spring-framework的目录,里面有Spring各组件的源码包;
4. 构建导入:下载下来的代码不能直接导入Eclipse,要先转换成Eclipse能读取的形式。因为所有组件都会依赖spring-core,所有我们首先要转换Spring-core工程,在命令窗口切换到Spring-core工程,运行gradle cleanidea eclipse命令,我们会看到开始下载工程所依赖的jar包,几分钟后执行完毕,再来看Spring-core文件夹,多了.classpath、.project等文件,这是Eclipse工程所必须的,然后可以把他导入到eclipse。因为大部分Spring组件都会用到 spring-beans、spring-context、spring-aop,而他们又依赖spring-expression、spring-instrument,所以我们干脆先把这些工程都进行转换并导入eclipse。
⑺ Spring源码分析(一) XmlWebApplicationContext
spring是大家都会用的ioc框架,但是要真的了解spring还是需要好好研究一下才行,为此看了一些spring源码,所以开始写spring源码分析的文章,这个是第一篇,先从ioc容器的启动开始。
我们都知道,spring的ioc容器的最基本的接口就是BeanFactory,而ApplicationContext是包含了BeanFactory的所有信息,所以ioc容器在启动的时候就是从AbstractApplicationContext的refresh方法开始的
具体的启动流程就不说了,主要是这里有一个onRefresh方法,我们来看这个类,在这个类中覆写了onRefresh方法
这是什么东西?别急,我们来看看themeSource是什么。
还是不太明白?那我们来看看的结构
原来ThemeSource是一个接口,而则实现了这个接口,在onRefresh把自己传进去了,好吧,这块就先看到这里。
我们直接到XmlWebApplicationContext这个类里,我们发现类有一个方法loadBeanDefinitions,而XmlWebApplicationContext覆写了这个方法,我们来看看XmlWebApplicationContext是怎么实现的
这里我们要介绍ioc容器里的一个接口BeanDefinitionReader,而XmlBeanDefinitionReader是BeanDefinitionReader的一个实现类,负责对xml的配置文件进行读取,并放到ioc容器中。当读取完配置文件后,通过loadBeanDefinitions方法将bean注册到ioc容器中。
至此,ioc容器就启动完成。
XmlWebApplicationContext的分析就到这里了。