spring的bean创建流程源码解析

文章目录

本文基于 Spring 5.0.2.RELEASE

spring 是 Java Web 开发中,非常常用的框架。它的基础作用是为我们生成对象。这里涉及到 2 个概念。IOC 和 DI。

IOC 和 DI

IOC:Inversion of Control,控制反转。强调的是原来在程序中创建Bean的权利反转给第三方,这里的第三方,其实就是 spring 框架。

DI:依赖注入。比如 UserService类,依赖 UserDao,但是 UserDao 是由 BeanFactory 注入给 UserService 的。

BeanFactory

1、BeanFactory是Spring的早期接口,称为Spring的Bean工厂.它提供了一系列的方法,用于获取 Bean

BeanFactory 的默认实现类是 DefaultListableBeanFactory,DefaultListableBeanFactory主要是管理 BeanDefinition 对象,并根据BeanDefinition 对象创建 Bean 对象。

在DefaultListableBeanFactory中,实现了BeanDefinitionRegistry接口,这个接口定义了关于 BeanDefinition 的注册、移除、查询等一系列的操作。

DefaultListableBeanFactory还继承了DefaultSingletonBeanRegistry,这个类中,实现了三级缓存,创建单例 Bean 的操作。????BeanDefinition对象,怎么传给DefaultSingletonBeanRegistry对象的????

DefaultListableBeanFactory总结起来,就是可以增删改查BeanDefinition定义对象,并通过父类方法,生成 Bean 对象。但是它并没有把 xml 文件,或者注解,解析成BeanDefinition对象的功能。这个功能是在ApplicationContext实现的。

ApplicationContext

实现的接口

1、BeanFactory接口

ApplicationContext 继承了很多接口,每增加一个接口,都增加了对应的功能。

但是,ApplicationContext 中,并不是继承了 DefaultListableBeanFactory,而是实现了 BeanFactory接口,并引入了 DefaultListableBeanFactory 作为属性,由 DefaultListableBeanFactory 实现 BeanFactory 相关的功能。(组合优于继承)

注意看,AnnotationConfigApplicationContext中包含 beanFactory 属性,在它的父类 AbstractApplicationContext中,实现了 getBean 等 方法,都是调用 beanFactory 来实现的。

java 复制代码
@Override
public Object getBean(String name) throws BeansException {
  assertBeanFactoryActive();
  return getBeanFactory().getBean(name);
}

ApplicationContext 相比 BeanFactory,还多继承了四个接口,功能如下:

2、MessageSource 国际化接口
java 复制代码
public static void main(String[] args) {
  ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
  System.out.println(context.getMessage("hi", null, Locale.CHINA));
  System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
}

在 resources 目录下创建三个配置文件,注意设置 idea 的 properties 编码格式是 utf-8

messages.properties 空内容

messages_en.properties 填写内容 hi=hello

messages_zh.properties 填写内容 hi=你好

运行代码,控制台打印如下,说明国际化生效

复制代码
你好
hello
3、ResourcePatternResolver,资源解析接口

可以用于读取配置文件

java 复制代码
public static void main(String[] args) throws IOException {
  ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
  //读取类类路径下文件
  Resource[] resources1 = context.getResources("classpath:application.properties");
  //读取jar 包中的类路径文件,注意带个 * 号
  Resource[] resources2 = context.getResources("classpath*:META-INF/spring.factories");
  //读取文件目录下的文件
  Resource[] resources3 = context.getResources("file:/tmp/test.log");
}
4、EnvironmentCapable接口,用于获取环境变量,配置信息
java 复制代码
  public static void main(String[] args) throws IOException {
    ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
    //读取application.properties 中的内容
    System.out.println(context.getEnvironment().getProperty("server.port"));
    //读取系统环境变量
    System.out.println(context.getEnvironment().getProperty("JAVA_HOME"));
  }
5、ApplicationEventPublisher 事件发布接口
java 复制代码
// 自定义事件
public class UserLoginEvent extends ApplicationEvent {
  public UserLoginEvent(Object source) {
    super(source);
  }
}
//发布事件
  public static void main(String[] args) throws IOException {
    ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
    context.publishEvent(new UserLoginEvent("username"));
  }
//接收事件
@Component
public class UserMessageService {

  @EventListener
  public void sendMessage(UserLoginEvent event){
    Object source = event.getSource();
    System.out.println("欢迎用户:"+source);
  }
}

当调用publishEvent发布事件时,会调用到 sendMessage 方法。(观察者模式)

总结:

1、BeanFactory 类,用于生成 Bean。

2、ApplicationContext,继承了 BeanFactory 接口,引入了 DefaultListableBeanFactory对象,体现了组合优于继承的原则。

3、ApplicationContext,实现了其他接口,增加了国际化,事件发布,资源解析,读取配置等功能。

Application 的继承体系

常用的 ApplicationContext 有 3 个,分别是

AnnotationConfigApplicationContext,用于加载注解配置类的ApplicationContext

FileSystemXmlApplicationContext,用于加载磁盘路径下的xml配置的ApplicationContext

ClassPathXmlApplicationContext,用于加载类路径下的xml配置的ApplicationContext

如果 spring 中加入了 Web 相关的组件,那么使用的 ApplicationContext 是

XmlWebApplicationContext,用于web环境下,加载类路径下的xml配置的ApplicationContext

AnnotationConfigWebApplicationContext,用于web环境下,加载磁盘路径下的xml配置的ApplicationContext

Bean 的创建执行流程

以解析 xml 文件为例,使用ClassPathXmlApplicationContext,流程如下:

1、加载xml配置文件,解析获取配置中的每个的信息,封装成一个个的BeanDefinition对象;

2、将BeanDefinition存储在一个名为beanDefinitionMap的Map<String,BeanDefinition>中;

3、ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;

4、创建好的Bean实例对象,被存储到一个名为singletonObjects的Map<String,Object>中;

5、当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回。

Spring 给开发者留下的扩展点

主要有两个地方,

1 是对 beanDefinitionMap 进行添加,修改,只要向 beanDefinitionMap 中添加了 BeanDefinition,就会被 Spring 生成 Bean。

2 是当 Bean 创建完成后,可以对 Bean 对象进行修改,比如 AOP,注解,都是生成代理 Bean,替换原有的 Bean。

流程如下:

结合代码描述

代码入口:

java 复制代码
ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:application-single.xml");

UserService userService = context.getBean("userServiceBeanId",UserService.class);

System.out.println(userService.getName());

进入ClassPathXmlApplicationContext

java 复制代码
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		//1、返回一个classloader
		//2、返回一个解析器
		super(parent);  // ===> 1
		// 1、获取环境(系统环境、jvm环境)
		// 2、设置Placeholder占位符解析器,${xxx}.xml解析为完整文件名
		// 2、将xml的路径解析完存储到数组
		setConfigLocations(configLocations); //  ===>
		//默认为true
		if (refresh) {
			//核心方法,入口
			refresh();  // ===>  magic happens here!
		}
	}

进入 Refresh 方法

java 复制代码
	@Override
	public void refresh() throws BeansException, IllegalStateException {
		// synchronized块锁(monitorenter --monitorexit)
		// 不然 refresh() 还没结束,又来个启动或销毁容器的操作
		//	 startupShutdownMonitor就是个空对象,锁
		synchronized (this.startupShutdownMonitor) {
			//1、【准备刷新】,设置了几个变量,也是准备工作
			prepareRefresh();   //  ===>
			// 2、【获得新的bean工厂】关键步骤,重点!
			//2.1、关闭旧的 BeanFactory
			//2.2、创建新的 BeanFactory(DefaluListbaleBeanFactory)
			//2.3、解析xml/加载 Bean 定义、注册 Bean定义到beanFactory(不初始化)
			//2.4、返回全新的工厂
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			//3、【bean工厂前置操作 】为BeanFactory配置容器特性
			// 例如类加载器、表达式解析器、注册默认环境bean、后置管理器
			prepareBeanFactory(beanFactory);// ===>
			try {
				// 4、【bean工厂后置操作】此处为空方法,如果子类需要,自己去实现
				postProcessBeanFactory(beanFactory);  // ===> 空的!

				//5、【调用bean工厂后置处理器】,开始调用我们自己实现的接口
				//调用顺序一:先执行BeanDefinitionRegistryPostProcessor接口的方法,它也继承了BeanFactoryPostProcessor
				//调用顺序二:后执行BeanFactoryPostProcessor接口的方法,用于修改 beanDefinition
				invokeBeanFactoryPostProcessors(beanFactory);  // ===>  重头戏

				//6、【注册bean后置处理器】只是注册,但是还不会调用
				//逻辑:找出所有实现BeanPostProcessor接口的类,分类、排序、注册
				registerBeanPostProcessors(beanFactory);  // ===>  关键点
				// Initialize message source for this context.
				//7、【初始化消息源】国际化问题i18n,参照https://nacos.io/
				initMessageSource(); // ===> 就是往factory加了个single bean
				// Initialize event multicaster for this context.
				//8、【初始化事件广播器】初始化自定义的事件监听多路广播器
				// 如果需要发布事件,就调它的multicastEvent方法
				// 把事件广播给listeners,其实就是起一个线程来处理,把Event扔给listener处理
				// (可以通过 SimpleApplicationEventMulticaster的代码来验证)
				initApplicationEventMulticaster(); // ===> 同样,加了个bean
				// 9、【刷新】这是个protected空方法,交给具体的子类来实现
				//  可以在这里初始化一些特殊的 Bean
				// (在初始化 singleton beans 之前)
				onRefresh();  // ===> 空的!一般没人管它
				//10、【注册监听器】,监听器需要实现 ApplicationListener 接口
				// 也就是扫描这些实现了接口的类,给他放进广播器的列表中
				// 其实就是个观察者模式,广播器接到事件的调用时,去循环listeners列表,
				// 挨个调它们的onApplicationEvent方法,把event扔给它们。
				registerListeners();  // ===> 观察者模式
				//11、 【结束bean工厂初始化操作】
				//1、初始化所有的 singleton beans,反射生成对象/填充
				//2、 调用Bean的前置处理器和后置处理器
				// 关键点:getBean方法里完成
				finishBeanFactoryInitialization(beanFactory);  // ===>  关键点!
				// 12、结束refresh操作
				// 发布事件与清除上下文环境
				finishRefresh();
			} catch (BeansException ex) {

			} finally {
			}
		}
	}

Bean 的创建流程总结:

面试中说的三级缓存是什么?

三级缓存是为了解决循环依赖,也是为了存储单例对象

Spring提供了三级缓存存储 完整Bean实例 和 半成品Bean实例 ,用于解决循环引用问题

在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:

java 复制代码
public class DefaultSingletonBeanRegistry ... { 
  //1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"
  Map<String, Object> singletonObjects = new ConcurrentHashMap(256); 
  //2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存" 
  Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16); 
  //3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存" 
  Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}

三级缓存怎么解决循环依赖问题?核心就是先把半成品的 Bean 放入一个缓存中

复制代码
假设 Bean1 和 Bean2 循环依赖,那么创建过程如下:
1、Bean1 实例化对象,但尚未初始化,将 Bean1 存储到三级缓存;
2、Bean1 属性注入,需要 Bean2,从缓存中获取,没有 Bean2;
3、Bean2 实例化对象,但尚未初始化,将 Bean2 存储到到三级缓存;
4、Bean2 属性注入,需要 Bean1,从三级缓存获取 Bean1,Bean1 从三级缓存移入二级缓存;
5、Bean2 执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
6、Bean1 注入 Bean2;
7、Bean1 执行其他生命周期过程,最终成为一个完整 Bean,存储到一级缓存,删除二三级缓存。

示例如下:

java 复制代码
public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean(ConfigurationClassPostProcessor.class);
    context.registerBean(Config.class);
    context.refresh();
    context.close();
}

@Configuration
static class Config {
    @Bean // 解析 @Aspect、产生代理
    public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
        return new AnnotationAwareAspectJAutoProxyCreator();
    }

    @Bean // 解析 @Autowired
    public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
        return new AutowiredAnnotationBeanPostProcessor();
    }

    @Bean // 解析 @PostConstruct
    public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
        return new CommonAnnotationBeanPostProcessor();
    }

    @Bean
    public Advisor advisor(MethodInterceptor advice) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");
        return new DefaultPointcutAdvisor(pointcut, advice);
    }

    @Bean
    public MethodInterceptor advice() {
        return (MethodInvocation invocation) -> {
            System.out.println("before...");
            return invocation.proceed();
        };
    }

    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }
}

static class Bean1 {
    public Bean1() {
        System.out.println("Bean1()");
    }
    @Autowired public void setBean2(Bean2 bean2) {
        System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
    }
    @PostConstruct public void init() {
        System.out.println("Bean1 init()");
    }
}

static class Bean2 {
    public Bean2() {
        System.out.println("Bean2()");
    }
    @Autowired public void setBean1(Bean1 bean1) {
        System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
    }
    @PostConstruct public void init() {
        System.out.println("Bean2 init()");
    }
}

执行 main 方法后,打印如下:

复制代码
Bean1()
Bean2()
Bean2 setBean1(bean1) class is: class com.spring.test.A17_1$Bean1
Bean2 init()
Bean1 setBean2(bean2) class is: class com.spring.test.A17_1$Bean2
Bean1 init()

为什么是三级缓存而不是二级缓存?

如果只是为了解决循环依赖问题,那么二级缓存就够了。为什么要设计三级缓存?为了解决循环依赖时,某个 Bean 需要被 AOP 增强,生成代理对象。

在Bean 的创建流程总结中,注意看图,Bean 的初始化方法执行完成后,才执行 BeanPostProcess的后处理方法,如果这个 Bean 被 AOP 增强了,会生成代理对象。

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法

java 复制代码
// 方法一:实例化! Bean的构造函数在这里被调用!
instanceWrapper = createBeanInstance(beanName, mbd, args); 
//方法二:注入相关的属性!【关键点】
populateBean(beanName, mbd, instanceWrapper);
//方法三:初始化方法,这里会调用 BeanPostProcess
exposedObject = initializeBean(beanName, exposedObject, mbd);

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition) 方法

java 复制代码
//调用Bean的前置处理器!我们自己实现的bean的processer在这里!
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

// 1、调用初始化方法, 处理 bean 中定义的 init-method,
//2、如果 bean 实现了 InitializingBean 接口,调用 afterPropertiesSet() 方法
invokeInitMethods(beanName, wrappedBean, mbd); 

// 调用Bean的后置管理器!用于生成代理aop也在这里偷偷做了手脚......注意aop调试时,看这句后wrappedBean的变化!
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); 

通过上述源码,可以看到,代理对象 生成,是在初始化方法执行后,才生成代理对象。

这个功能,在循环依赖场景下,就会存在问题。

刚才的 Bean1 和 Bean2 循环依赖时,打印如下:

复制代码
1、Bean1()
2、Bean2()
3、Bean2 setBean1(bean1) class is: class com.spring.test.A17_1$Bean1
4、Bean2 init()
5、Bean1 setBean2(bean2) class is: class com.spring.test.A17_1$Bean2
6、Bean1 init()

如果是在初始化后,才对 Bean1 进行增强生成代理对象,那么是在第 6 步完成后,才生成代理对象。这样Bean2 setBean1(bean1) 注入的就是原始对象,不是代理对象。不符合预期。

所以 Spring 打了一个补丁,提供一个三级缓存,三级缓存中存储的不是未完成的对象,而是 Bean 的创建工厂ObjectFactory。当需要Bean1 对象时,执行的是 ObjectFactory 的 getObject 方法,这个方法中,也会进行是否 AOP 的判断,如果是,就生成代理对象。

源码如下:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

java 复制代码
// () -> getEarlyBeanReference(beanName, mbd, bean) 是 ObjectFactory 匿名实现类 的简写
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

getEarlyBeanReference 方法

java 复制代码
	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		//是否存在实现InstantiationAwareBeanPostProcessors接口的类
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			//循环所有Bean后置处理器
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					//重点:开始创建AOP代理,
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		//总结
		//1、如果不调用后置,返回的bean和三级缓存一样
		//2、如果调用AOP后置,返回的就是代理对象
		//3、这就是三级缓存设计的巧妙之处!!!!Map<String, ObjectFactory<?>>
		// 结论:虽然二级缓存能解决循环依赖,但是使用不了aop了,也就是扩展点没有了
		return exposedObject;
	}

示例如下:修改 Bean1 代码增加一个 foo 方法,让 Bean1 被 AOP 增强

java 复制代码
    static class Bean1 {
        public void foo() {

        }
        public Bean1() {
            System.out.println("Bean1()");
        }
        @Autowired public void setBean2(Bean2 bean2) {
            System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
        }
        @PostConstruct public void init() {
            System.out.println("Bean1 init()");
        }
    }

再次执行,观察打印效果:

复制代码
Bean1()
Bean2()
Bean2 setBean1(bean1) class is: class com.spring.test.A17_1$Bean1$$EnhancerBySpringCGLIB$$28831f11
Bean2 init()
Bean1 setBean2(bean2) class is: class com.spring.test.A17_1$Bean2
Bean1 init()

注意看,Bean1 对象,很早就变成了代理对象,说明是三级缓存生效了。注意:虽然很早就变成代理对象了,但是初始化方法还没执行。

总结下:

AOP 代理对象生成的时机?

1、如果存在循环依赖,是在三级缓存中生成代理对象

2、如果不存在循环依赖,是在 Bean 初始化方法执行后生成。

为什么需要三级缓存?

为了打补丁,让循环依赖的 AOP 的 Bean,能正常注入到其他 Bean 中。

1,2,3 三级缓存的作用是什么?

一级缓存存储已经完全创建好的单例对象

二级缓存存储未完成的,还被其他 Bean 引用的对象

三级缓存,用于存储未完成的对象工厂,且没被其他对象引用过。

相关推荐
xuxie1339 分钟前
SpringBoot文件下载(多文件以zip形式,单文件格式不变)
java·spring boot·后端
重生成为编程大王1 小时前
Java中的多态有什么用?
java·后端
666和7771 小时前
Struts2 工作总结
java·数据库
中草药z1 小时前
【Stream API】高效简化集合处理
java·前端·javascript·stream·parallelstream·并行流
野犬寒鸦1 小时前
力扣hot100:搜索二维矩阵 II(常见误区与高效解法详解)(240)
java·数据结构·算法·leetcode·面试
zru_96021 小时前
centos 系统如何安装open jdk 8
java·linux·centos
码熔burning2 小时前
Spring Security 深度学习(六): RESTful API 安全与 JWT
安全·spring·restful·springsecurity
LiRuiJie2 小时前
深入剖析Spring Boot / Spring 应用中可自定义的扩展点
java·spring boot·spring