Spring源码分析
Posted 陈彦斌
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码分析相关的知识,希望对你有一定的参考价值。
Spring介绍
什么是Spring?
百度百科的介绍
Spring官方网址: http://spring.io/
我们经常说的Spring其实指的是 Spring Framework (Spring 框架)
为什么学习Spring?
好处
耦合和内聚介绍
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。
在软件工程中,耦合指的就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类与架构之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。
内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当适当做一件好事。
内聚和耦合是密切相关的,同其他模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其他模块之间是低耦合。在进行软件设计时,应力争做到高内聚,低耦合。
Spring体系结构
Spring核心概念介绍
IoC(核心中的核心):Inverse of Control,控制反转。对象的创建权力由程序反转给Spring框架。
AOP:Aspect Oriented Programming,面向切面编程。在不修改目标对象的源代码情况下,增强IoC容器中Bean的功能。
DI:Dependency Injection,依赖注入。在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件中!!
Spring容器:指的就是IoC容器。
Spring IoC原理分析
什么是IoC容器?
所谓的IoC容器就是指的是Spring中Bean工厂里面的Map存储结构(存储了Bean的实例)。
Spring框架中的工厂有哪些?
ApplicationContext接口()
实现了BeanFactory接口
实现ApplicationContext接口的工厂,可以获取到容器中具体的Bean对象
BeanFactory工厂(是Spring架构早期的创建Bean对象的工厂接口)
实现BeanFactory接口的工厂也可以获取到Bean对象
其实通过源码分析,不管是BeanFactory还是ApplicationContext,其实最终的底层BeanFactory都是DefaultListableBeanFactory
ApplicationContext和BeanFactory的区别?
创建Bean对象的时机不同:
BeanFactory采取延迟加载,第一次getBean时才会初始化Bean。
ApplicationContext是加载完applicationContext.xml时,就创建具体的Bean对象的实例。(只对BeanDefition中描述为时单例的Bean,才进行饿汉堡式加载)
如何创建Web环境中的IoC容器?
创建方式
- ApplicationContext接口常用实现类
ClassPathXmlApplicationContext:
它是从类的根路径下加载配置文件 推荐使用这种
FileSystemXmlApplicationContext:
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
AnnotationConfigApplicationContext:
当我们使用注解配置容器对象时,需要使用此类来创建Spring容器。它用来读取注解。
- Java应用中创建IoC容器:(了解)
ApplicationContext context=new ClassPathXmlApplicationContext(xml路径);
- Web应用中创建IoC容器:(重点)
web.xml中配置ContextLoaderListener接口,并配置ContextConfigLocation参数
web容器启动之后加载web.xml,此时加载ContextLoaderListener监听器(实现了ServletContextListener接口,该接口的描述请见下面的《三类八种监听器》)
ContextLoaderListener监听器会在web容器启动的时候,出发ContextInitialized()方法
ContextInitialized()方法会调用initWebApplicationContext()方法,该方法负责创建Spring容器(DefaultListableBeanFactory)
【Web三类八种监听器】
监听域对象的生命周期
ServletContextListener:
创建:服务器启动
销毁:服务器正常关闭
spring ContextLoaderListener(服务器启动时负责加载spring配置文件)
HttpSessionListener
创建:第一次访问request.getHttpSession()
销毁:调用invalidate();非法关闭;过期
ServletRequestListener
创建:每一次访问
销毁:相应结束
监听域对象的属性:(添加、删除、替换)
ServletContextAttributeListener
HttpSessionAttributeListener
ServletRequestAttributeListener
监听HttpSession中JavaBean的改变:
HttpSessionBindingListener(HttpSession和JavaBean对象的绑定和解绑)
HttpSessionActivationListener(HttpSession的序列化,活化,纯化)
源码分析
参考资料中的源码中的工程《Spring-sourcecode》
1.web服务器(tomcat)启动会加载web.xml(启动ContextLoaderListener监听器):
2.创建web环境中的Spring容器
3.ContextLoader类中创建Spring容器并初始化容器中的Bean实例
4.configureAndRefreshWebApplicationContext方法中调用初始化Bean的refresh方法
图示
该图主要是分析上面第三步骤中【创建Spring容器】的图示
IoC容器如何创建Bean对象?
源码分析
源码来源于AbstractApplicationContext类:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); //1.创建真正的Spring容器(DefaultListableBeanFactory) //2.加载BeanDefition(描述要初始化的Bean的信息) //3.将BeanDefition注册到BeanDefitionRegistry // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); //执行实现了BeanFactoryPostProcessor接口的Bean //比如PropertyPlaceHolderConfigurer(context:property-placeholer)就是此处被调用的,替换掉BeanDefition中的占位符(${})中的内容 // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); //注册BeanPostProcessor(后置处理器) //比如容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器(实现@Autowired注解功能) // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); //初始化非懒加载方式的单例Bean实例 // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset \'active\' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring\'s core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
图示
Spring 容器初始化源码分析
容器初始化主流程分析
主流程入口
ApplicationContext context = new ClassPathXmlApplicationContext(“spring.xml”)
ClassPathXmlApplicationContext类:重载的构造方法依次调用,进入下面代码
AbstractApplicationContext的refresh方法:初始化spring容器的核心代码
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { //1、 Prepare this context for refreshing. prepareRefresh(); //创建DefaultListableBeanFactory(真正生产和管理bean的容器) //加载BeanDefition并注册到BeanDefitionRegistry //通过NamespaceHandler解析自定义标签的功能(比如:context标签、aop标签、tx标签) //2、 Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //3、 Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { //4、 Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); //实例化并调用实现了BeanFactoryPostProcessor接口的Bean //比如:PropertyPlaceHolderConfigurer(context:property-placeholer) //就是此处被调用的,作用是替换掉BeanDefinition中的占位符(${})中的内容 //5、 Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); //创建并注册BeanPostProcessor到BeanFactory中(Bean的后置处理器) //比如:AutowiredAnnotationBeanPostProcessor(实现@Autowired注解功能) // RequiredAnnotationBeanPostProcessor(实现@d注解功能) //这些注册的BeanPostProcessor //6、 Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); //7、 Initialize message source for this context. initMessageSource(); //8、 Initialize event multicaster for this context. initApplicationEventMulticaster(); //9、 Initialize other special beans in specific context subclasses. onRefresh(); //10、 Check for listener beans and register them. registerListeners(); //创建非懒加载方式的单例Bean实例(未设置属性) //填充属性 //初始化实例(比如调用init-method方法) //调用BeanPostProcessor(后置处理器)对实例bean进行后置处理 //11、 Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); //12、 Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset \'active\' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring\'s core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
图示
创建BeanFactory流程分析
获取新的BeanFactory子流程
子流程入口(从主流程refresh方法中的第二步开始)
调用AbstractApplicationContext中的obtainFreshBeanFactory方法
调用AbstractRefreshableApplicationContext的refreshBeanFactory方法
加载解析BeanDefinition子流程(loadDefinitions方法)
源码分析
子流程入口(AbstractRefreshableApplicationContext类的方法)
此处依次调用多个类的loadBeanDefinitions方法(AbstractXmlApplicationContextà AbstractBeanDefinitionReaderà XmlBeanDefinitionReader),一直调用到XmlBeanDefinitionReader 类的doLoadBeanDefinitions方法
对于doLoadDocument方法不是我们关注的重点,我们进入到该类的registerBeanDefinitions方法看看
此处有两个地方是我们关注的:一个createRederContext方法,一个是DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法,先进入createRederContext方法看看
至此,14个NamespaceHandlerResolver初始化成功。然后我们再进入DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法
继续进入到该类的doRegisterBeanDefinitions方法看看,这是真正干活的方法
继续进入parseBeanDefinitions方法
我们看到有两种解析方案,先看看parseDefaultElement方法
不过我们重点看看BeanDefinitionParserDelegate类的parseCustomElement方法(AOP标签、tx标签的解析都是在该步骤中完成的)
getNamespaceURI方法的作用一目了然,我们就不去追踪了,接下来我们进入DefaultNamespaceHandlerResolver类的resolve方法看看:
在上面代码中,我们看到了一行代码:namespaceHandler.init();这个方法是很重要的。它实现了自定义标签到处理类的注册工作,不过NamespaceHandler是一个接口,具体的init方法需要不同的实现类进行实现,我们通过AopNamespaceHandler了解一下init的作用,其中aop:config标签是由ConfigBeanDefinitionParser类进行处理:
至此,我们了解到了xml中的aop标签都是由哪些类进行处理的了。不过init方法只是注册了标签和处理类的对应关系,那么什么时候调用处理类进行解析的呢?我们再回到BeanDefinitionParserDelegate类的parseCustomElement方法看看
我们看到,最后一行执行了parse方法,那么parse方法,在哪呢?我们需要到NamespaceHandlerSupport类中去看看,它是实现NamespaceHandler接口的,并且AopNamespaceHandler是继承了NamespaceHandlerSupport类,那么该方法也会继承到AopNamespaceHandler类中。
至此,整个XML文档的解析工作,包括bean标签以及自定义标签如何解析为BeanDefinition信息的过程,我们已经了解了。
后续具体想了解哪个自定义标签的处理逻辑,可以自行去查找xxxNamespaceHandler类进行分析。
图示
创建Bean流程分析
子流程入口
我们进入finishBeanFactoryInitialization方法看看
继续进入DefaultListableBeanFactory类的preInstantiateSingletons方法,我们找到下面部分的代码,看到工厂Bean或者普通Bean,最终都是通过getBean的方法获取实例的。
继续跟踪下去,我们进入到了AbstractBeanFactory类的doGetBean方法,这个方法中的代码很多,我们直接找到核心部分
接着进入到AbstractAutowireCapableBeanFactory类的方法,找到以下代码部分
我们终于找到核心的地方了,进入doCreateBean方法看看,该方法我们关注两块重点区域
对于如何创建Bean的实例,和填充属性,暂时先不去追踪了,我们先去看看initializeBean方法是如何调用BeanPostProcessor的,因为这个牵扯到我们对于AOP动态代理的理解。
Spring Ioc基于XML的使用
创建工程
环境准备
- maven
- jdk
- spring
- Eclipse
工程搭建
POM文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.kkb</groupId> <artifactId>spring</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- spring 核心组件中的4个依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.0.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.0.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.0.7.RELEASE</version> </dependency> <!-- 单元测试Junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> <build> <plugins> <!-- 配置Maven的JDK编译级别 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
Spring配置文件(只编写配置文件头)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
具体实现
在Spring的XML配置文件中配置一个bean标签,该标签最终会被加载为一个BeanDefition对象(描述对象信息)
思路:
编写UserService接口的实现类
将UserService实现类交给Spring IoC容器管理
从Spring IoC容器中获取UserService实现类
编写接口:UserService
编写实现类:UserServiceImpl
编写XML文件:applicationContext.xml
编写单元测试代码:TestSpring
bean标签详解
bean标签作用:
用于配置对象让Spring来创建
默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
bean标签属性:
id:给对象在容器中提供一个唯一标识。用于获取对象。
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
scope:指定对象的作用范围。
singleton:默认值,单例的(在整个容器中只有一个对象)。
prototype:多例的。
request:web项目中,Spring创建一个Bean的对象,将对象存入到request域中。
session:web项目中,Spring创建一个Bean的对象,将对象存入到session域中。
global session:web项目中,应用在Portlet环境,如果没有Portlet环境那么globalSession相当于session。
init-method:指定类中的初始化方法名称。
destroy-method:指定类中销毁方法名称。比如DataSource的配置中一般需要指定destroy-method="close"。
bean的作用范围:
单例对象:scope="singleton"
一个应用只有一个对象的实例。它的作用范围就是整个应用
生命周期:
对象出生:当应用加载,创建容器时,对象就被创建了。
对象活着:只要容器在,对象一直活着。
对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
多例对象:scope="prototype"
每次访问对象时,都会重新创建对象实例。
生命周期:
对象出生:当使用对象时,创建新的对象实例。
对象活着:只要对象在使用中,就一直活着。
对象死亡:当对象长时间不用时,被java的垃圾回收期回收了。
实例化bean的三种方式
第一种:使用默认无参构造函数(重点)
在默认情况下:它会根据默认无参构造函数来创建类对象。
如果bean中没有默认无参构造函数,将会创建失败
<bean id="userService" class="com.chenyanbin.spring.service.UserServiceImpl">
第二种:静态工厂(了解)
/** * 模拟一个静态工厂,创建业务层实现类 */ public class StaticFactory { public static UserService createUserService(){ return new UserServiceImpl(); } }
<!--此种方法时: 使用StaticFactory类中的静态方法createUserService创建对象,并存入Spring容器 id属性:指定bean的id,用于从容器中获取 class属性:指定静态工厂的全限定类名 factory-method属性:指定生产对象的静态方法 --> <bean id="userService" class="com.chenyanbin.spring.factory.StaticFactory" factory-method="createUserService"></bean>
第三种:实例工厂(了解)
/** * 模拟一个实例工厂,创建业务层实现类 * 此工厂创建对象,必须现有工厂实例对象,再调用方法 */ public class InstanceFactory { public UserService createUserService(){ return new UserServiceImpl(); } }
<!-- 此种方式是: 先把工厂的创建交给spring来管理。 然后在使用工厂的bean来调用里面的方法 factory-ben属性:用于指定实例工厂bean的id。 factory-method属性:用于指定实例工厂中创建对象的方法。 --> <bean id="instanceFactory" class="com.chenyanbin.factory.InstanceFactory"></bean> <bean id="userService" factory-bean="instanceFactory" factory-method="createUserService"></bean>
Spring DI(依赖注入)介绍
概述
什么是依赖?
依赖指的就是Bean实例中的属性
属性分为:简单类型(8种基本类型和String类型)的属性、POJO类型的属性、集合数组类型的属性。
什么是依赖注入
依赖注入:Dependency Injection。它是Spring框架核心IoC的具体实现。
为什么要进行依赖注入?
我们的程序在编写时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。
IoC解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。那这种业务层和持久层的依赖关系,在使用Spring之后,就让Spring来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
依赖注入的方式(基于XML)
构造函数注入
顾名思义,就是使用类中的构造函数,给成员变量赋值。
注意,赋值的操作不是我们自己做的,而是通过配置的方式,让Spring框架来为我们注入。
具体代码如下:
public class UserServiceImpl implements UserService { private int id; private String name; public UserServiceImpl(int id, String name) { this.id = id; this.name = name; } @Override public void saveUser() { System.out.println("保存用户:id为"+id+",name为"+name+" Service实现"); } }
<!--使用构造函数的方式,给Service中的属性传值要求:类中需要提供一个对应参数列表的构造函数。 涉及的标签:constructor-arg index:指定参数在构造函数参数列表的索引位置 name:指定参数在构造函数中的名称 value:它能赋的值是基本数据类型和String类型 ref:他能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean --> <bean id="userService" class="com.chenyanbin.spring.service.UserServiceImpl"> <constructor-arg name="id" value="1"></constructor-arg> <constructor-arg name="name" value="zhangsan"></constructor-arg> </bean>
set方法注入(重点)
set方法注入又分为手动装配方式注入和自动装配方式注入。
手动装配方式(XML方式):bean标签的子标签property,需要在类中指定set方法。
自动装配方式(注解方式):@Autowired注解、@Resource注解。
@Autowired:一部分功能是查询实例,从spring容器中根据类型(java类)获取对应的实例。另一部分功能就是赋值,将找到的实例,装配和另一个实例的属性值。(注意事项:一个java类型在同一个spring容器中,只能有一个实例)
@Resource:一部分功能是查询实例,从spring容器中根据Bean的名称(Bean标签的名称)获取对应的实例。另一部分功能就是赋值,将找到的实例,装配给另一个实例的属性值。
使用p名称空间注入数据(本质上还是调用set方法)
1.步骤一:需要先引入p名称空间
在schema的名称空间中加入该行:
xmlns:p="http://www.springframework.org/schema/p"
2.步骤二:使用p名称空间的语法
p:属性名=""
p:属性名-ref=""
3.步骤三:测试
<bean id="person" class="com.chenyanbin.spring.demo.Person" p:pname="隔壁老王" p:car2-ref="car2" /> <bean id="car2" class="com.chenyanbin.spring.demo.Car2" />
依赖注入不同类型的属性(基于XML)
简单类型(value)
<bean id="userService" class="com.chenyanbin.spring.service.UserServiceImpl">
<property name="id" value="1"></property>
<property name="name" value="zhangsan"></property>
</bean>
引用类型(ref)
ref就是reference的缩写,是引用的意思
<bean id="userService" class="com.chenyanbin.spring.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean id="userDao" class="com.chenyanbin.spring.dao.UserDaoImpl"></bean>
集合类型(数组)
1.如果是数组或者List集合,注入配置文件的方式是一样的
<bean id="collectionBean" class="com.chenyanbin.demo5.CollectionBean">
<property name="arrs">
<list>
<!--如果集合内是简单类型,使用value子标签,如果是POJO类型,则使用bean标签-->
<value>张三</value>
<value>李四</value>
<value>王五</value>
</list>
</property>
</bean>
2.如果是Set集合,注入的配置文件方式如下:
<property name="sets"> <set> <!--如果集合内是简单类型,使以上是关于Spring源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段
Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段