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类:重载的构造方法依次调用,进入下面代码

 

AbstractApplicationContextrefresh方法:初始化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集合,注入的配置文件方式如下: