最近开始读Spring源码,读着读着发现里面还是有很多很好玩的东西在里面的,里面涉及到了大量的设计模式以及各种PostProcessor注入的过程,很好玩,也很复杂,本文就是记录一下我学习过程中的主干流程。
在开始我们源码解读之前,我们先大致的归纳一下解析的全过程。
1. 首先就是每个spring的jar包都会提供各自的配置文件,包括注解,标签,处理类。
2. 然后就是根据我们的配置信息去解读我们自己的配置文件spring.xml,主要分为自定义标签和默认标签(<bean> 就是默认标签)
3. 最后就是解读这些信息,封装成 BeanDefinition对象,进行注册。
4. 最后介绍一下此阶段注册的几个PostProcessor接口,这部分内容是为我们实例化bean和DI注入做准备的,下一章我们会重点分析。
在我们逐步分解实例化beanDefinition之前,我先贴出时大体流程图,后面解读的过程会参照这张图进行讲解。
进入主题:
1. 每一个Spring的jar包都会配置好各种各样的处理类,这些类都提供了各种各样的处理器功能。比如说,spring想要管理maybatis,那么在spring管理mabatis的jar包中肯定会定义一些处理类,如下图:
而本篇我们是将spring基于注解方式实例化BeanDefinition的,那么我们都会基于扫描的方式进行
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:jack="http://www.xiangxueedu.com/schema/mytags"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.xiangxueedu.com/schema/mytags
http://www.xiangxueedu.com/schema/mytags.xsd"
default-lazy-init="false">
<context:component-scan base-package="com.xiangxue.jack" />
</beans>
我们知道,扫描是基于 http://www.springframework.org/schema/context进行的,那么我们就打开spring的context相关 jar包,找到对应的处理类。
点进去以后,我们可以看到这个处理类只有一个方法,就是注册基于context标签下的各个标签的处理类,有点拗口,看一下代码就明白了
2. 上面步骤是一个基本的了解过程,下面我们就开始自己逐步debug分解过程,首先就是生成一个applicationContent的操作;
3. 进入ClassPathXMLApplicationContext的refresh()方法,这个方式是spring的核心
4. 我们关注一下obtainFreshBeanFactory()方法,就在refresh()内部调用。这个方法做了很多的事情,下方第一张图片有详细的comments进行解释
总结一下,其实就是把spring.xml文件生成一个document对象,然后进行逐步解析。
5. 解析document对象的时候,我们会判断是默认标签,还是自定义标签。比如在spring.xml中配置<bean id="" class=""></bean>, 这就是默认标签。 而<context:component-scan base-package="com.xiangxue.jack" />就是自定义标签。 本文针对的是基于注解的方式分析,所以关注的是自定义标签。
6. 我们首先会根据component-scan 标签,在spring..xml 中找到对应的URI
7. 基于上一步获取到的URI,拿到对应的NameSapceHandler处理器,这个处理器就是我们第1步说的,在spring.handers文件中配置的信息,也就是 ContextNamespaceHandler。这一步我们需要进行详细的分析一下,进入到这个resolve方法中看一下:
感兴趣的话可以逐个点进去看一下具体。此处我需要再次强调一下初始化init方法,注册component-scan的流程。点进去看一下,我们发现
我们确认了,它就是初始化各个标签的扫描类的,并且会把这些扫描类放到缓存中,缓存是一个Map, 在它的父类NameSpaceHandle人Support中
8.最后,我们会返回现在已经准备好的context的处理类。进入主流程
9. 上一步,我们拿到了context的处理类,即ContextNamespaceHandler。接下来会调用它的parase方法,进行解析操作。 这个方法是重点方法,我们需要点进去看
a. 首先会根据<context:component-scan base-package="com.xiangxue.jack" />配置,拿到base-package配置的目录。
b. 创建注解扫描器 ClassPathBeanDefinitionScanner,来扫描base-package配置的路径。 扫描到对应的class文件,生成beanDefinition进行注册。这一步是核心
b-1: 扫描到有注解的类并封装成BeanDefinition对象
b-1-1: 进入findCandidateComponents方法:它就是递归的方法,根据配置的路径找到所有的class文件,不管是有注解的,还是没有注解的。只要是这个路径下的class文件,统统找出来
b-1-2: 然后逐个遍历,判断当前的class文件是否有符合要求的注解。如果是符合要求的class, 就生成BeanDefinition对象进行收集
b-1-3: 这里有必要看一下这个判断方法 isCandidateComponent。点进去
我们发现,只要这个类有与注解Component相关的信息,就是符合条件的class对象,就会被搜集起来。
最后,我们解释一下,什么是和@Component相关的呢? 其实,@Controller, @Service, @Repository都是与@Component相关的. 如何判断呢?只要找到对应的注解,打开发现接口上方有@Component相关信息即可。
b-1-4: 最后返回到所有搜集起来的符合条件的BeanDefinition的set集合
b2: 拿到这些有注解的,符合条件的beanDefinition,那么接下来就是处理这些BeanDefinition的事情了。
b2-1: 判断beanDefinition是否直接懒加载@Lazy @DependOn @Description @Primary等注解
其实,就是在BeanDefinition中,给对应的属性设置个值而已。当我们实例化对象的时候,我们在根据BeanDefinition中设置的这些值,做不同的逻辑判断而已。下一章章实例化的时候,我会分析一下这些注解
b2-2: 最后就是注册这些BeanDefinition了
b2-3: 进去看一下最终是如何注册的。
最后,我们发现,所谓的注册BeanDefinition,其实就是把所有的name都放在一个list集合中,把BeanDefinition按照 name -->BeanDefinition放在一个Map中。这个垃圾,搞了半天,就搞了2个集合,这就完了。
上面就是基于注解的方式,实例化BeanDefinition的全部过程,概括就是根据自定义标签,拿到URI,再根据URI拿到对应的context的处理类,而这个处理类会实例化context下的各个子标签的各个处理器,再根据字标签拿到属于自己的子处理器。 执行context处理类的resolve方法和parse方法。resolve方法就是负责初始化子标签的处理器的。而parse就是根据子标签处理器找满足条件class文件对应的beanDefinition,最后进行注册。就是放在2个集合中。最后打完收工,回家睡觉。
最后补充一下:我们在parse的最后一行,有个registerComponents方法。它是负责注册一些PostProcessor接口的。
那么为啥要注册这些接口呢? 因为在我们实例化对象的时候,我们会使用很多注解进行依赖注入,比如说@Autowired @Resource等等。而这些注解,都是有这些PostProceseor接口提供的。具体如何提供的,我们在下一章会针对@Autowired @Resource这两个注解进行重点分析。
此处, 我们只需要知道注册了一些PostProceseor即可,那具体注册了哪些PostProceseor呢?它们右具体负责哪些事呢?
每个PostTProcessor 作用,我们都可以点进去查看。如何进行查看呢?
下面我列出一些常用的注解支持类:
AutowiredAnnotationBeanPostProcessor 支持@Autowired @Value
CommonAnnotationBeanPostProcessor 支持 @PostConstruct @PreDestroy @Resource
ConfigurationClassPostProcessor 支持@Configuration, @Bean
我们都知道@PostConstruct相当于init-method,@PreDestroy相当于destory-method,其实,它们在beanDefinition中都是设置到相同的属性中的,这证明了它们就是一个东西
最后,分享一个甜点 BeanDefinitionRegistryPostProcessor
在第9步的b2-3中,我们谈到了注册BeanDefinition的过程
其实,如果一些对象,它们没有注解,也没有配置在spring.xml中,它们只是一个普通的java文件,或者是第三方jar提供的文件, 我们是否可以也交给spring进行管理呢?答案是可以的。
假设一个普通的java文件
package com.xiangxue.jack.postProcessor;
public class Dao2 {
private String name;
private String id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "name :" + name + " id :" + id;
}
}
实现了BeanDefinitionRegistryPostProcessor的实现类,这个类需要交给spring管理
package com.xiangxue.jack.postProcessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.stereotype.Component;
@Component
public class MyBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//生成bean
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(Dao2.class);
//给Dao2属性值赋值,这样实例化完成以后就会有值了.MutablePropertyValues有很多方法,
//也就意味着我们即使配置的类有错误,只要实现这个接口,我们依旧可以在类实例化之前,通过
//对beanDefinition进行修改,从而达到修改类的目的
MutablePropertyValues pValues = beanDefinition.getPropertyValues();
pValues.addPropertyValue("name", "yyds");
pValues.addPropertyValue("id", "测试001");
//我们给beanDefinition起了个 名字叫 dao2, 然后注册到spring容器中
registry.registerBeanDefinition("dao2", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
测试类:
package com.xiangxue.jack;
import com.xiangxue.jack.bean.MyTestBean2;
import com.xiangxue.jack.postProcessor.Dao2;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
//@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring.xml"})
public class MyTest {
@Autowired
private ApplicationContext applicationContext;
@Test
public void myBean() {
applicationContext = new ClassPathXmlApplicationContext("spring.xml");
MyTestBean2 service = (MyTestBean2) applicationContext.getBean("myTestBean2");
service.system();
}
@Test
public void myBean2() {
applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Dao2 dao2 = (Dao2) applicationContext.getBean("dao2");
System.out.println(dao2.toString());
}
}
测试结果:
这个甜点的原理,我会在下一章开头就进行分析