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过程
- 加载xml文件到内存中
- 解析xml文件中的Bean定义信息
- 将Bean的定义信息封装成一个个的BeanDefinition对象
- 根据BeanDefinition对象,实例化Bean
- 将实例化后的Bean放到IOC容器中
- 业务代码可以直接从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过程理论概述
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方法的流程