Spring源码解析(一)

83 阅读5分钟

Spring源码入门

Spring框架的两个重要概念是IOC和AOP

IOC是指控制反转。以前创建对象,需要使用new关键字创建。现在创建和销毁对象的操作可以让Spring框架帮我们来完成,这就是控制反转

AOP是指面向切面编程。用于在不修改原始代码的情况下向现有应用程序添加新功能。这种编程方式将应用程序分成许多独立的部分,称为切面。这些切面可以在应用程序的不同位置进行编写和维护,从而提高了应用程序的可重用性和可维护性

Spring的使用

我们使用Spring框架写一个简单的程序,然后分析Spring是如何发挥作用的

创建接口类

package com.xx.biz;
 
public interface Ink {
    public String getColor();
}
package com.xx.biz;
 
public interface Paper {
    public String getContent();
    public void putInChars(String content);
}

创建接口实现类

package com.xx.impl;
 
import com.xx.biz.Ink;
import lombok.Data;
import java.awt.*;

@Data
public class ColorInk implements Ink {
    private int red;
    private int green;
    private int blue;
 
    @Override
    public String getColor() {
        Color color = new Color(this.red,this.green,this.blue);
        return "#" + Integer.toHexString(color.getRGB()).substring(2);
    }
}
package com.xx.impl;
 
import com.xx.biz.Ink;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.awt.*;

@Data
@NoArgsConstructor
public class GreyInk implements Ink {
    private int red;
    private int green;
    private int blue;
    @Override
    public String getColor() {
        int c = (this.red + this.green + this.blue)/3;
        Color color = new Color(c,c,c);
        return "#" + Integer.toHexString(color.getRGB()).substring(2);
    }
}
package com.xx.impl;
 
import com.xx.biz.Paper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
public class TextPaper implements Paper {
    private int charPerLine;
    private int linePerLine;
    private String content = "";
 
    public TextPaper(int charPerLine, int linePerLine) {
        this.charPerLine = charPerLine;
        this.linePerLine = linePerLine;
    }
    @Override
    public String getContent() {
        String out = "每行字符数" + this.charPerLine + "\n";
        out += "每行页数" + this.linePerLine + "\n";
        out += this.content;
        return out;
    }
 
    @Override
    public void putInChars(String content) {
        this.content = content;
    }
}

编写Printer类

package com.xx;
 
import com.xx.biz.Ink;
import com.xx.biz.Paper;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
public class Printer {
    private Ink ink;
    private Paper paper;
    public Printer(Ink ink,Paper paper){
        this.ink = ink;
        this.paper = paper;
    }
    public void print(String str){
        System.out.println("使用" + ink.getColor() + "颜色打印");
        paper.putInChars(str);
        System.out.println(paper.getContent());
    }
}

创建xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!-- spring-config.xml -->
<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">
 
    <bean id="ColorInk" class="com.xx.impl.ColorInk">
        <property name="red" value="10" />
        <property name="green" value="20" />
        <property name="blue" value="30" />
    </bean>
 
    <bean id="greyInk" class="com.xx.impl.GreyInk">
        <property name="red" value="20" />
        <property name="green" value="30" />
        <property name="blue" value="40" />
    </bean>
 
    <bean id="textPaper" class="com.xx.impl.TextPaper">
        <constructor-arg value="10" />
        <constructor-arg value="8" />
    </bean>
 
    <bean id="printer" class="com.xx.Printer">
        <constructor-arg ref="greyInk" />
        <constructor-arg ref="textPaper" />
    </bean>
 
</beans>

编写启动类

import com.xx.Printer;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class PrinterTest {
 
    @Test
    public void testPrint() {
        // 加载xml文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 创建并获取Bean
        Printer printer = (Printer) context.getBean("printer");
        printer.print("哈哈哈哈哈哈哈哈哈!");
    }
}

Spring创建Bean过程

image.png

  1. 加载xml文件到内存中
  2. 解析xml文件中的Bean定义信息
  3. 将Bean的定义信息封装成一个个的BeanDefinition对象
  4. 根据BeanDefinition对象,实例化Bean
  5. 将实例化后的Bean放到IOC容器中
  6. 业务代码可以直接从IOC容器中获取Bean

在xml文件中,Bean定义信息中可以有多个属性

<bean id="exampleBean" class="com.example.MyClass"
      scope="singleton" lazy-init="true" autowire="byName"
      init-method="init" destroy-method="cleanup"
      factory-method="createInstance" factory-bean="myFactoryBean"
      primary="true" depends-on="otherBean" abstract="false"
      autowire-candidate="true">
</bean>
  • scope:指定bean的作用域,默认是singleton,表示单例
  • lazy-init:是否懒加载
  • autowire:指定bean的自动装配模式
  • init-method:指定Bean在实例化之后,需要执行的初始化方法
  • destroy-method:指定销毁方法的名称,该方法将在容器销毁bean时调用
  • factory-method:指定创建Bean的静态工厂方法
  • factory-bean:指定一个现有的工厂bean用于创建此bean实例
  • primary:指定在自动装配时,若有多个候选者,此bean是否作为首选项
  • depends-on:指定当前bean所依赖的一个或多个bean的名称,这些bean将在当前bean实例化之前实例化
  • abstract:指定bean是否为抽象类。抽象bean本身不会被实例化,只能用作父类
  • autowire-candidate:指定此bean是否可以作为其他bean自动装配的候选者

这些属性都会被封装到BeanDefinition对象中,方便操作

实例化后的Bean会放到IOC容器中,使用的数据结构是Map。其实,不只是Bean,还有在Bean实例化过程中,生成的一些重要对象,例如BeanDefinition、BeanFactory等,这些也会放到Map中存储起来,方便获取。

创建Bean过程理论概述

image.png

Bean创建的详细过程及扩展点

Bean的定义信息可以写在xml文件中,也可以写在其他格式的文件中。Spring考虑到这一点,所以定义了不同的类可以解析不同格式的文件内容,这些类实现了同一个接口,就是BeanDefinitionReader接口

解析Bean的定义信息生成BeanDefinition对象后,要进行Bean的实例化。在实例化之前,Spring提供了一些扩展点,程序员在扩展点编写的程序会在实例化之前执行。例如继承BeanFactoryPostProcessor类,可以获取BeanDefinition对象,并且修改对象中的属性

Bean的实例化过程包括在堆区创建空间,以及对属性赋值。Spring提供了一系列Aware接口,业务类实现这些接口就获取到Spring容器中的其他组件对象,通过这些对象可以做扩展操作。例如实现ApplicationContextAware接口,可以获取到ApplicationContext对象,这是Spring容器上下文对象,通过此对象可以获取容器中的所有对象

在Bean初始化前后,还可以进行扩展。只要实现BeanPostProcessor接口,就可以在Bean初始化前后执行一些操作。在Bean初始化前会执行BeanPostProcessor的PostProcessorBeforeInitialization方法,在初始化后会执行PostProcessorAfterInitialization方法。实现BeanPostProcessor接口,实现这两个方法,在Bean初始化过程中进行一些扩展操作

重要的接口

  • BeanFactory:BeanFactory作为IOC容器的顶层接口,定义了IOC容器的基本行为,包括:Bean实例获取、判断Bean是否存在、Bean别名获取等行为
  • BeanDefinitionReader:BeanDefinitionReader的作用是读取Spring配置文件中的内容,将之解析为BeanDefinition并注册到BeanDefinitionRegistry工厂中
  • BeanDefinition:支持对Bean的定义信息进行修改查询等功能
  • BeanFactoryPostProcessor:通常用于修改Bean的定义,Bean的属性值
  • Aware:Aware接口是Spring提供的一组标记接口,用于在Bean装配的过程中获取Spring容器中提供的一些核心组件或运行时上下文等信息
  • BeanPostProcessor:用于在Bean初始化前后执行一些操作
  • Environment:获取环境中的配置参数
  • FactoryBean:提供的getObject方法用于创建Bean。FactoryBean创建的Bean不会走Spring的创建Bean流程,而是程序员实现的getObject方法的流程