Spring源码-中级

(Version:Spring5)

一、方法论

(一)基础知识

1.Java设计模式

2.数据结构+算法

3.反射

(1)反射的实现方式

反射的实现方式

4.多线程

5.JVM

(二)注意事项

1.不要太专注细节

2.看注释(接口、类、方法)

3.见名知意

4.大胆猜测、小心验证

5.画图(时序图,结构图,总结图)

6.坚持(most important)

二、Spring概述

(一)Spring是什么?

Spring框架是一个生态,在其基础上构建了SpringBoot框架脚手架,然后SpringCloud服务治理框架中包含每个组件又是在SpringBoot基础上构建起来的。
Spring的扩展性很好,源码里面提供了很多空实现的接口,比如:OnRefresh()方法、afterPropertiesSet()方法等。SpringMVC框架、Mybatis框架、SpringBoot脚手架框架就是对这些空实现接口做了实现才形成新的框架。
IOC和AOP是Spring的两大核心。

1.IOC(Inversion Of Control):控制反转

IOC是一种创建对象的思想,其本质是一个专门用来存放Spring对象的容器。

2.AOP(Aspect Oriented Programming):面向切面编程

AbstractAutoProxyCreator抽象类通过实现BeanPostProcessor接口来实现AOP
AbstractAutoProxyCreator抽象类中的postProcessAfterInitialization方法就是继承自BeanPostProcessor接口中的方法

(1)AOP实现流程源码
①BeanPostProcessor接口:bean加载过程中对bean进行增强
②AbstractAutoProxyCreator抽象类:实现BeanPostProcessor接口

postProcessAfterInitialization方法->wrapIfNecessary方法->createProxy方法->getProxy方法->getProxy(classLoader)方法->getProxy(@Nullable ClassLoader classLoader)方法。

③ProxyFactory类

getProxy(@Nullable ClassLoader classLoader)->getProxy(classLoader)。

④AopProxy接口

getProxy(@Nullable ClassLoader classLoader)。

⑤CglibAopProxy/JdkDynamicAopProxy类:实现AopProxy接口

继承getProxy(@Nullable ClassLoader classLoader)方法分别实现两种不同的AOP方式。

(2)接口扩展
①AOP

AOP是AbstactAutoProxyCreator抽象类通过实现BeanPostProcessor接口并重写其postProcessAfterInitialization方法实现的。
方法步骤:
postProcessAfterInitialization方法
->wrapIfNecessary方法
->createProxy方法
->getProxy方法
->getProxy(classLoader)方法
->getProxy(@Nullable ClassLoader classLoader)方法。
Spring源码中用到了两种实现AOP的方法,CglibAopProxy和JdkDynamicAopProxy,在源码中进行了判断。

②SpringBoot中集成的Tomcat的启动

AbstractApplicationContext抽象类和Spring容器的顶层接口之间的实现关系

AbstractApplicationContext抽象类提供的refresh()方法里面有一个空实现方法onRefresh(),这是Spring提供的专门用来扩展实现的,SpringBoot框架中集成的Tomcat容器的启动就是通过重写onRefresh()方法来实现的。

Ⅰ.启动类:

run(SpringbootDataApplication.class, args)。

Ⅱ.SpringApplication类

run(new Class<?>[] { primarySource }, args)
->.run(args)
->refreshContext(context)
->refresh(context)
->.refresh()

Ⅲ.AbstractApplicationContext抽象类

->refresh()
->onRefresh();

Ⅳ.ServletWebServerApplicationContext类

->onRefresh()
->createWebServer();
->.onStartup(servletContext)。
也可以通过createWebServer()方法中的getWebServerFactory()方法获取Tomcat服务器web服务器工厂。此时Tomcat和factory都有了,下一步就可以创建很多个Tomcat了

3.DI(Dependency Injection):依赖注入

DI是IOC的实现手段。依赖注入分两种情况:

(1)属性注入
(2)构造器注入

(二)Spring如何使用

1.IOC容器中加载Bean的流程

依赖通过属性值或构造器注入完成后,通过ClassPathXmlApplicationContext容器对象就可以获取到依赖信息
bean加载流程

从xml配置文件中加载bean的每个步骤中又包含了很多细节。bean对象实例化后放到容器中,其实就是用数据结构将对象信息存储起来。在使用时通过依赖注入的方式从容器中取出来,其取出对象的过程就是根据Map对象的key去获取其value值。

(1)加载xml
(2)解析xml

XML文件解析方法包含三种模型:

①DOM(文档对象模型):Spring源码中使用该方法解析xml

将整个XML文档加载到内存中,形成树状结构,适用于小型文档。

②SAX(简单API for XML)

逐行读取XML文档,适用于大型文档,不需要将整个文档加载到内存中。

③StAX(流API for XML)

双向解析模型,以类似流的方式读取和写入XML数据,适用于大型文档。
Java中的XML处理包括解析XML、生成XML和操作XML。

(3)封装BeanDefinition

通过BeanDefinition接口管理定义的Bean的信息。

(4)创建bean对象

创建对象分两个大过程:
创建对象过程分为实例化和初始化过程
bean对象创建的完整过程:
bean对象创建的完整过程

①实例化

在堆内存开辟一块内存空间,此时对象的属性值都是默认值。

②初始化

给对象属性设置值。

Ⅰ.填充属性
Ⅱ.设置Aware接口属性

Spring容器创建bean对象时,在具体操作过程中需要Spring容器的其他对象时,可以实现Aware接口并给当前对象设置容器对象属性,然后当前实例对象就可以获取它所在的容器对象,进而通过容器对象获取其他对象。
a.定义ApplicationContextAware接口的实现类:

public class MyAware implements ApplicationContextAware {
   
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
   
        this.applicationContext=applicationContext;
    }

    public ApplicationContext getApplicationContext() {
   
        return applicationContext;
    }

    @Override
    public String toString() {
   
        return super.toString();
    }
}

b.myDemo.xml文件配置:

<?xml version="1.0" encoding="UTF-8"?>
<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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <bean id="myAware" class="com.mashibing.myDemo.Aware.MyAware"></bean>
</beans>

c.测试:
自定义的实现类需要实现ApplicationContextAware接口,否则实例对象获取不到容器对象而是null值
d.也可以自定义类实现EnvironmentAware接口,通过该自定义对象可以获取该对象所在环境的的属性信息:

public class MyAware implements ApplicationContextAware, EnvironmentAware {
   
...
@Override
    public void setEnvironment(Environment environment) {
   
        System.out.println("from MyAware: "+environment.getActiveProfiles());
    }
}

e.测试结果如下:
自定义类实现EnvironmentAware接口,通过该自定义对象可以获取该对象所在环境的的属性信息

Ⅲ.BeanPostProcessor:postProcessBeforeInitialization

初始化方法调用前要进行的处理逻辑。

@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   
	return bean;
}
Ⅳ.执行初始化方法(init-method)

案例:
BookDao类中的init方法:

public void init(){
   
        System.out.println("BookDao类中的init方法");
        System.out.println(this.jdbcTemplate);
    }

tx.xml文件中通过init-method属性执行初始化方法:

<bean id="bookDao" class="com.mashibing.tx.xml.dao.BookDao" init-method="init">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>

执行初始化方法结果

构造器方法执行完成后才执行init方法。

Ⅴ.BeanPostProcessor:postProcessAfterInitialization

在初始化方法指定后要进行的处理逻辑。

@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   
	return bean;
}
③完整对象

使用时直接从Spring容器中获得对象:context.getBean()。

(5)实例化后的bean放置容器

IOC如何处理?

①BeanFactory

BeanFactory是整个IOC容器的顶层接口,也是IOC容器的入口
BeanFactory是这个IOC容器的顶层接口

②后置处理器增强器(PostProcessor)
Ⅰ.BeanFactoryPostProcessor:用来增强BeanDefinition信息

在容器创建过程中如果想随时修改beanDefinition就可以通过BeanFactoryPostProcessor接口来实现。
a.自定义BeanDefinition的处理器增强器

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
   
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
   
        BeanDefinition definition = beanFactory.getBeanDefinition("bookDao");
        definition.setDescription("设置BeanDefinition: tony add description to BeanDefinition");
        System.out.println("\ndescription from BeanDefinition:\n"+definition.getDescription());
    }
}

自定义BeanDefinition的处理器增强器时可以设置的属性值有很多,也可以获取其已有的属性值
b.tx.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<bean id="bookDao" class="com.mashibing.tx.xml.dao.BookDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean class="com.mashibing.MyBeanFactoryPostProcessor"></bean>
</beans>

c.测试

public class MyBeanFactoryPostProcessorTest {
   
    public static void main(String[] args) {
   
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("tx.xml");
        BookDao bean = context.getBean(BookDao.class);
        System.out.println("\n"+bean.toString());
    }
}

测试结果:BeanDefinition属性信息设置成功后可以正常读取出来

Ⅱ.BeanPostProcessor:用来增强bean信息

在容器创建过程中需要动态改变bean的信息可以通过BeanPostProcessor接口来实现。

(6)使用时从容器中获取bean对象

(三)Sping Bean

1.Bean的生命周期

Spring Bean的生命周期

Ⅰ. 创建前准备阶段

这个阶段的主要目的是Bean在开始加载之前,需要从上下文和相关配置中查找Bean有关的扩展实现,比如像init-method容器初始化Bean时调用的方法、destroy-method容器销毁Bean时调用的方法、以及BeanPostProcessor这类的Bean加载过程中的前置和后置处理。这些类或者配置其实是Spring提供给开发者,用来实现Bean加载过程中的扩展机制,在很多和Spring集成的中间件中比较常见,比如Dubbo。

Ⅱ. 创建实例阶段

这个阶段主要是通过反射来创建Bean的实例对象,并且扫描和解析Bean声明的一些属性。

Ⅲ. 依赖注入阶段

如果被实例化的Bean存在依赖其他Bean对象的情况,则需要对这些依赖Bean进行对象注入,比如常见@Autowired注解注入、setter注入等配置形式。同时,在这个阶段会触发一些扩展的调用,比如常见的扩展类:BeanPostProcessor接口(用来实现Bean初始化前后的扩展回调)、InitializingBean(它里面提供了空实现的afterPropertiesSet()方法,这个方法在BeanFactory设置了提供的所有bean属性后调用)、BeanFactoryAware接口(它里面提供了空实现方法setBeanFactory())等等。

Ⅳ. 容器缓存阶段

容器缓存阶段主要是把Bean保存到容器以及Spring的缓存中,到了这个阶段,Bean就可以被开发者正常使用了。这个阶段涉及到的操作,常见的比如init-method这个属性配置的方法会在这个阶段调用、以及BeanPostProcessor接口提供的方法postProcessAfterInitialization(Object bean, String beanName)也会在这个阶段触发。

Ⅴ.实例销毁阶段

当Spring应用上下文关闭时,该上下文中的所有Bean都会被销毁。如果存在Bean实现了DisposableBean接口、或者配置了distory-method属性,会在这个阶段被调用。

2.作用范围:

(1)singleton(默认)
(2)prototype
(3)request
(4)session

3.对象类型

(1)普通对象:自定义需要的对象
(2)容器(内置)对象:Spring需要的对象

(四)观察者模式:监听器、监听事件、多播器(广播器)、事件源

综上可知,Spring Bean在初始化的五个不同阶段做了不同的事情,那么一个阶段是如何直到其他阶段的状态的,这就是通过Java的观察者设计模式来实现的。

(五)接口梳理

1.BeanFactory:Spring容器的根接口

2.Aware:Spring创建Bean时涉及到容器的其他对象可以用该接口获得

3.BeanDefinition:用来封装从配置文件中解析出来的Bean信息

4.BeanDefinitionReader:解析Bean的配置文件

5.BeanFactoryPostProcessor:容器创建Bean时用该接口对BeanDefinition进行增强

6.BeanPostProcessor:容器创建Bean时用该接口对Bean信息进行增强

该接口提供了postProcessBeforeInitialization前置处理和postProcessAfterInitialization后置处理两个方法。这两个方法分别在Bean对象初始化过程中执行init-mothed这个方法之前和之后执行。
Spring AOP就是通过实现BeanPostProcessor接口并重写其postProcessAfterInitialization后置处理方法来实现的。

7.Environment

实现类StandardEnvironment间接实现了Environment接口

实现类StandardEnvironment中分别提供了获取环境信息的getSystemEnvironment()方法和获取属性信息的getSystemProperties()方法。当需要给自定义Bean设置系统相关的环境和属性信息时就可以通过实现Environment接口来实现。

8.FactoryBean:创建对象的接口,提供了三个方法

(1)getObject():获取对象
(2)getObjectType():获取对象类型
(3)isSingleton():判断对象是否是单例
(4)FactoryBean和BeanFactory:都是用来创建对象的接口

当使用FactoryBean来创建对象时只需要调用getObject()方法获取对象即可,整个创建对象的过程是由用户自己来控制的。
当使用BeanFactory来创建对象时必须要遵循完整的对象创建过程,这个过程是由Spring控制的。

三、调试Spring源码整体流程

(一)AbstractApplicationContext抽象类

1.prepareRefresh():容器刷新前的准备工作

(1)设置容器的启动时间为当前系统时间;
(2)设置容器的活跃状态为true;
(3)设置容器的关闭状态为false;
(4)获取Environment对象,并加载当前系统的属性值到Environment对象中;
(5)准备监听器和事件的集合对象,默认为空的集合;

处理逻辑:挨个遍历时间并且和监听器进行匹配,匹配上了就处理,匹配不上就不处理。

2.obtainFreshBeanFactory():创建容器对象或Spring的Bean工厂

初始化BeanFactory,并进行XML文件读取,并将得到的BeanFactory记录在当前实体的属性中。

(1)加载xml配置文件的属性值到当前工厂中,最重要的就是BeanDefinition;
(2)创建容器对象DefaultListableBeanFactory:对bean进行相关处理

继承体系

(3)HierarchicalBeanFactory:getParentBeanFactory()

继承BeanFactory,在BeanFactory功能基础上增加对parentFactory的支持。

(4)ListableBeanFactory:根据各种条件获取bean的配置清单
(5)ConfigurableBeanFactory:提供配置Factory的各种方法;
(6)refreshBeanFactory():obtainFreshBeanFactory()中重要方法

初始化BeanFactory,并进行XML文件读取,并将得到的BeanFactory记录在当前实体的属性中。
调试:

<bean id="person" class="com.mashibing.Person">
        <property name="id" value="66"></property>
        <property name="name" value="tony"></property>
</bean>
public class MyApplicationContextTest {
   
    public static void main(String[] args) {
   
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("myApplicationContext.xml");
        Person person = (Person) context.getBean("person");
        System.out.println("id="+person.getId()+",name="+person.getName());
    }
}

refreshBeanFactory()方法中通过loadBeanDefinitions(beanFactory)方法将xml文件中bean信息解析出来封装到BeanDefinition,然后将BeanDefinition再设置到BeanFactory中

beanDefinitionMap是一个ConcurrentHashMap,以bean的id为key、以bean对应的BeanDefinition为value;而beanDefinitionNames是一个ArrayList,它里面存储很多bean的id。
测试结果

3.prepareBeanFactory(beanFactory):beanFactory的准备工作,对各种属性进行填充

(1)设置beanFactory的classloader为当前context的classloader;
(2)设置beanfactory的表达式语言处理器;
(3)为beanFactory增加一个默认的propertyEditor,这个主要是对bean的属性等设置管理的一个工具类;
(4)添加beanPostProcessor,ApplicationContextAwareProcessor此类用来完成某些Aware对象的注入;
(5)设置要忽略自动装配的接口,因为这些接口的实现是由容器通过set方法进行注入的,所以在使用@Autowired注解进行注入的时候需要将这些接口进行忽略。
(6)设置几个自动装配的特殊规则,当在进行ioc初始化的时候如果有多个实现,那么就使用指定的对象进行注入;
(7)给BeanFactory注册BeanPostProcessor;
(8)增加对AspectJ的支持:
在java中织入分为三种方式:编译器织入、类加载器织入、运行期织入。
编译器织入是指在java编译期,采用特殊的编译器,将切面织入到java类中。
类加载器织入则指通过特殊的类加载器,在类字节码class文件加载到JVM内存时,织入切面。
运行期织入则是采用cglib和jdk进行切面的织入。
aspectj提供了两种织入方式,第一种是通过特殊编译器,在编译期,将aspectj语言编写的切面类织入到java类中,第二种是类加载器织入,就是load time weaving,后面再详细补充。
(9)注册默认的系统环境bean到一级缓存中。

4.postProcessBeanFactory(beanFactory):模版方法用于做扩展实现

子类覆盖方法做额外的处理,此处我们自己一般不做任何扩展工作,但是可以查看web中的代码,是有具体实现的。

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   
}

5.invokeBeanFactoryPostProcessors(beanFactory):调用各种beanFactory处理器

invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()):
获取到当前应用程序上下文的beanFactoryPostProcessors变量的值,并且实例化调用执行所有已经注册的beanFactoryPostProcessor;
默认情况下,通过getBeanFactoryPostProcessors()来获取已经注册的BFPP。
该阶段主要处理任务是红框内的各种BeanFactoryPostProcessor的调用

6.registerBeanPostProcessors(beanFactory)

注册可以拦截bean创建过程的处理器,这里只是注册功能,真正调用的是getBean方法。
该方法会实例化并且注册所有的beanPostProcessor。

7.initMessageSource():i18n国际化语言处理

为上下文初始化message源,即不同语言的消息体,国际化处理。在看SpringMVC源码的时候通过国际化的代码重点研究。

8.initApplicationEventMulticaster():初始化事件监听多路广播器

9.onRefresh():给子类做扩展的空实现方法

10.registerListeners()

在所有注册的bean中查找listener bean,注册到消息广播器中。

11.finishBeanFactoryInitialization(beanFactory):完成初始化

初始化剩下的(非懒加载的)单实例。

(1)new ClassPathXmlApplicationContext(“XXX.xml”):构建容器
(2)ClassPathXmlApplicationContext:

this
->refresh()

(3)AbstractApplicationContext:

->refresh()
->finishBeanFactoryInitialization(beanFactory):通过该方法初始化剩下的单实例
->beanFactory.preInstantiateSingletons():一系列判断之后进行单实例对象的初始化

(4)ConfigurableListableBeanFactory

->preInstantiateSingletons():接口中提供预实例化单例的空方法

(5)DefaultListableBeanFactory

->preInstantiateSingletons():该抽象类实现了接口中的方法
->getBean(beanName):通过该方法获取bean实例

(6)AbstractBeanFactory
①getBean(String name)
②doGetBean(name, null, null, false)

此方法是实际获取bean的方法,也是触发依赖注入的方法。

Ⅰ.getSingleton(beanName)

该方法第一次执行时的返回值是null,因为缓存中还不存在。当实例化和初始化完成后会返回完整bean。
当实现了FactoryBean接口需要获取具体对象的时候就会执行此方法
getObjectForBeanInstance(sharedInstance, name, beanName, null)(AbstractBeanFactory类):
从 beanInstannce 中获取公开的Bean对象,主要处理beanInstance是FactoryBean对象的情况,如果不是FactoryBean会直接返回beanInstance实例。
->getObjectFromFactoryBean(factory, beanName, !synthetic)(AbstractBeanFactory类):
从BeanFactory对象中获取管理的对象.如果不是synthetic会对其对象进行该工厂的后置处理。从FactoryBean中获取一个对象。
->doGetObjectFromFactoryBean(factory, beanName)(FactoryBeanRegistrySupport类):
获取factory管理的对象实例。
->object = factory.getObject():获取factory管理的对象实例赋值给object。FactoryBean会创建两个对象,一个是FactoryBean对象、另一个是调用getObject()方法时会返回的自定义对象。然后一直正常返回,直到beanFactory.preInstantiateSingletons()也正常返回,此时**实例化(实例化+初始化)**完成。

③createBean(beanName, mbd, args)

为给定的合并后BeanDefinition(和参数)创建一个bean实例。之所以存在合并是因为存在GenericBeanDefinition(自定义的Bean)和RootBeanDefinition(系统本来存在的Bean)。

(7)AbstractAutowireCapableBeanFactory
①doCreateBean(beanName, mbdToUse, args):实际创建bean的调用。

从该方法中开始进行bean的实例化和初始化。实例化时采用反射的方式实现,虽然反射在调用次数很多的情况下性能会下滑,但是其灵活性很高;而new的方式来实例化对象耦合度很高,所以在框架中通过反射进行实例化是不错的选择。

Ⅰ. doCreateBean方法中获取bean对象之后有主动缓存单例对象的来解决循环依赖问题的逻辑

通过构造器注入的bean没办法解决循环依赖问题,因为它不能提前暴漏bean;而通过settter方式注入的bean可以通过提前暴漏的方式解决循环依赖问题。提前暴漏是指将已经完成实例化还未完成初始化的bean暴漏给外界使用,此时其属性值还是默认空值,但是不影响使用,后续再把属性给填充进去。

Ⅱ.addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))

进入DefaultSingletonBeanRegistry类的addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)方法:添加给定的单例对象工厂来构建指定的单例对象。

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
   
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    // 使用singletonObjects进行加锁,保证线程安全
    synchronized (this.singletonObjects) {
   
        // 如果单例对象的高速缓存【bean名称-bean实例】没有beanName的对象
        if (!this.singletonObjects.containsKey(beanName)) {
   
            // 将beanName,singletonFactory放到单例工厂的缓存【bean名称 - ObjectFactory】
            this.singletonFactories.put(beanName, singletonFactory);
            // 从早期单例对象的高速缓存【bean名称-bean实例】 移除beanName的相关缓存对象
            this.earlySingletonObjects.remove(beanName);
            // 将beanName添加已注册的单例集中
            this.registeredSingletons.add(beanName);
        }
    }
}
Ⅲ.populateBean(beanName, mbd, instanceWrapper)

该方法完成对bean的属性填充,将各个属性值注入,其中,可能存在依赖于其他bean的属性,则会递归初始化依赖的bean。
在执行populateBean方法之前bean的属性值都是null
在执行populateBean方法之后bean的属性值已经填充进去

Ⅳ.initializeBean(beanName, exposedObject, mbd):Bean初始化

Bean初始化流程

上一步骤已经完成bean的属性填充,接下来需要执行初始化逻辑。
invokeAwareMethods(beanName, bean):设置Aware接口属性。它是Aware接口处理器,会调用BeanNameAware、BeanClassLoaderAware、beanFactoryAware等接口。
->applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName):BeanPostProcessor后置处理器的前置处理,后置处理器,顾名思义就是事后增强功能。
->processor.postProcessBeforeInitialization(result, beanName):

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {
   
		//初始化返回结果为existingBean
		Object result = existingBean;
		//遍历 该工厂创建的bean的BeanPostProcessors列表
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
   
			// postProcessBeforeInitialization:在任何Bean初始化回调之前执行(如初始化Bean的afterPropertiesSet或自定义的init方法)
			// 将此BeanPostProcessor应用到给定的新Bean实例。Bean已经填充了属性值。返回的Bean实例可能是原始Bean的包装器。
			// 默认实现按原样返回给定的Bean
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			// 如果 current为null
			if (current == null) {
   
				//直接返回result,中断其后续的BeanPostProcessor处理
				return result;
			}
			//让result引用processor的返回结果,使其经过所有BeanPostProcess对象的后置处理的层层包装
			result = current;
		}
		//返回经过所有BeanPostProcess对象的后置处理的层层包装后的result
		return result;
	}

->invokeInitMethods(beanName, wrappedBean, mbd):执行init-method方法
调用初始化方法,先调用bean的InitializingBean接口方法,后调用bean的自定义初始化方法。
->applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName):BeanPostProcessor后置处理器的后置处理。将BeanPostProcessors应用到给定的现有Bean实例,调用它们的postProcessAfterInitialization方法。返回的Bean实例可能是原始Bean包装器。

Ⅴ.registerDisposableBeanIfNecessary(beanName, bean, mbd):

bean初始化完成后还需要注册bean对象,方便后续在容器销毁的时候销毁对象。

②createBeanInstance(beanName, mbd, args)

根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化。

③instantiateBean(beanName, mbd)

使用默认无参构造函数创建对象,如果没有无参构造且存在多个有参构造且没有@AutoWired注解构造,会报错。

④getInstantiationStrategy():获取Bean实例化的生成策略。
⑤instantiate(mbd, beanName, this)

如果发现是有方法重写的,就需要用cglib来动态代理,如果没有就直接获取默认构造方法实例化。bd对象定义中,是否包含MethodOverride列表,spring中有两个标签参数会产生MethodOverrides,分别是lookup-method,replaced-method。

Ⅰ.BeanUtils.instantiateClass(constructorToUse)

没有MethodOverrides对象,可以直接实例化,通过反射生成具体的实例化对象。
->BeanUtils.instantiateClass(constructorToUse)(BeanUtils类)
->ctor.newInstance(argsWithDefaultValues):通过反射利用构造器进行实例化。

Ⅱ.instantiateWithMethodInjection(bd, beanName, owner)

有方法重写时默认使用cglib来动态代理,必须生成cglib子类
beanInstance实例化完成,其属性值为默认空值进一步说明实例化只是在内存开辟存储空间不给属性赋值。属性填充是在初始化阶段完成

12.finishRefresh():完成容器刷新

完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人。

四、Spring启动流程细节

(一)prepareRefresh():创建容器前准备工作

Environment接口继承体系

Environment接口是用来管理系统环境和属性相关信息的,在Spring中StandardEnvironment类间接实现了Environment接口,它没有无参构造器,但是在其抽象父类AbstractEnvironment中无参构造器中调用了它自己空实现的customizePropertySources方法,StandardEnvironment类重写了父类的那个方法来实现对自定义属性信息的管理。
而在SpringBoot中,StandardServletEnvironment类又继承了Spring的StandardEnvironment类,在其基础上做了增强,增加了另外三个属性值:servletContextInitParams、servletConfigInitParams、jndiProperties,其中名称中包含InitParams关键字的属性是用来处理web.xml这个项目配置文件中init-param标签信息的。
AbstractEnvironment抽象类会加载出来包括电脑主机名在内的54个系统属性和51个环境值

1.调试案例:

测试代码:

public class MySpringSourceCodeDebug {
   
    public static void main(String[] args) {
   
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-${username}.xml");
        Person person = (Person) context.getBean("person");
        System.out.println("id="+person.getId()+",name="+person.getName());
    }
}

xml配置文件:spring-${username}.xml。

<bean id="person" class="com.mashibing.Person">
     <property name="id" value="88"></property>
     <property name="name" value="Tony"></property>
</bean>

2.属性值设置

(1)setConfigLocations(configLocations):设置应用程序上下文的配置路径
(2)resolvePath(locations[i]).trim():解析给定路径
(3)resolveRequiredPlaceholders(path):占位符处理
(4)createPlaceholderHelper(false):(AbstractPropertyResolver抽象类中)

xml配置文件名中的占位符替换处理。
createPlaceholderHelper(false)方法做占位符替换

(5)doResolvePlaceholders(text, this.strictHelper):占位符处理
(6)replacePlaceholders(text, this::getPropertyAsRawString):替换占位符
(7)parseStringValue(value, placeholderResolver, null):占位符中包含的值的处理
(8)resolvePlaceholder(placeholder):(PropertyPlaceholderHelper类)
(9)getProperty(key, String.class, false):获取电脑主机名
(10)propertySource.getProperty(key):第二次循环走到此处获取主机名

获取电脑主机名

(11)convertValueIfNecessary(value, targetValueType):主机名替换到配置文件中
(12)replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal)

当前方法执行完成后主机名替换完毕。完成替换后的配置文件名会存放到configLocations变量中
以上步骤都正常返回后可以从变量值中查看到setConfigLocations(configLocations)方法中xml配置文件中的占位符已经替换成主机名:
主机名替换完成

3.prepareRefresh()之initPropertySources()方法:属性扩展

AbstractApplicationContext抽象类中空实现方法initPropertySources()是留给子类覆盖或初始化属性资源,ClassPathXmlApplicationContext是抽象类的一个子类。扩展initPropertySources()可以获取用户主机名。

(1)扩展类:
public class MyInitPropertySources extends ClassPathXmlApplicationContext {
   
    public MyInitPropertySources(String... configLocations) throws BeansException {
   
        super(configLocations);
    }
    
    @Override
    protected void initPropertySources() {
   
        Object username = getEnvironment().getSystemEnvironment().get("USERNAME");
        System.out.println("扩展initPropertySources方法获取username: "+username);
        getEnvironment().setRequiredProperties("username");
    }
}
(2)测试类:
public class MyInitPropertySourcesTest extends ClassPathXmlApplicationContext {
   
    public static void main(String[] args) {
   
        ApplicationContext context = new MyInitPropertySources("myDemo.xml");
        System.out.println(context.getBean("myAware"));
    }
}
(3)xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <bean id="myAware" class="com.mashibing.myDemo.aware.MyAware"></bean>
    <bean id="myFacotryBean" class="com.mashibing.myDemo.factoryBean.MyFacotryBean"></bean>
</beans>

4.prepareRefresh()之validateRequiredProperties()方法:属性验证

创建并获取环境对象,验证需要的属性文件是否都已经放入环境中。如果没有对initPropertySources()进行扩展并通过getEnvironment().setRequiredProperties(“username”)方式给环境设置需要的属性username,那么requiredProperties属性值将会为空。
属性扩展时已经做了需要的属性设置,所以requiredProperties属性值不为空

5.prepareRefresh()之准备空集合applicationListeners

监听器集合属性在Spring框架中为空,但是在SpringBoot框架中是专门用来放各种监听器的,所以监听器集合也是Spring提供给其他框架的一个扩展点。
Spring中applicationListeners属性值为空
SpringBoot框架中applicationListeners属性值为空,用来存放监听器

6.总结:

prepareRefresh()方法中准备刷新容器前的准备工作一共做了五件事:

(1)设置容器的启动时间为当前系统时间毫秒值;
(2)设置容器的活跃状态标志位为true;
(3)设置容器的关闭状态标志位为false;
(4)获取Environment对象,并加载当前系统值到该对象;
(5)准备监听器和事件集合对象,默认是空集合。

(二)obtainFreshBeanFactory():创建容器

创建出来的容器对象是DefaultListableBeanFactory。其过程中会加载xml配置文件的属性值到当前工厂中,当前工厂指的就是容器对象。最重要的就是BeanDefinition,它用来封装从xml配置文件中加载出来的bean属性信息。

1.refreshBeanFactory():(AbstractApplicationContext类中)

初始化BeanFactory,并进行XML文件读取,并将得到的BeanFactory记录在当前实体的属性中。

(1)refreshBeanFactory():(AbstractRefreshableApplicationContext类中)
①如果存在beanFactory,则销毁beanFactory
②如果不存在beanFactory则创建:createBeanFactory()
③beanFactory.setSerializationId(getId())

为了序列化指定id,可以从id反序列化到beanFactory对象。

④customizeBeanFactory(beanFactory)

定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖。这两个属性值默认是null值,如果要修改这两个属性的默认值,需要继承抽象类AbstractRefreshableApplicationContext或其实现类并重写customizeBeanFactory(beanFactory)方法。
抽象类AbstractRefreshableApplicationContext继承体系关系图
allowBeanDefinitionOverriding和allowCircularReferences的属性值默认是null。可以通过实现

Ⅰ.案例:修改allowBeanDefinitionOverriding和allowCircularReferences的属性值

xml配置文件:

<bean id="myAware" class="com.mashibing.myDemo.aware.MyAware"></bean>

重写代码:

public class MyInitPropertySources extends ClassPathXmlApplicationContext {
   
    ......
    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
   
        super.setAllowBeanDefinitionOverriding(false);
        super.setAllowCircularReferences(false);
        super.customizeBeanFactory(beanFactory);
    }
}

测试代码:

public class MyInitPropertySourcesTest extends ClassPathXmlApplicationContext {
   
    public static void main(String[] args) {
   
        ApplicationContext context = new MyInitPropertySources("myDemo.xml");
        System.out.println(context.getBean("myAware"));
    }
}

属性值修改成功,断点可以进入if循环体

Ⅱ.allowBeanDefinitionOverriding属性值的作用

在xml文件的bean标签里面可以通过子标签或者来指定让方法返回指定类型,前提是要设置allowBeanDefinitionOverriding的属性值为true,否则不生效。

⑤loadBeanDefinitions(beanFactory):(AbstractXmlApplicationContext类中)

初始化documentReader,并进行XML文件读取及解析,默认命名空间的解析,自定义标签的解析。

Ⅰ.设计模式之适配器模式

通过XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory)创建一个xml的beanDefinitionReader,并通过回调设置到beanFactory中。
XmlBeanDefinitionReader是一个xml文件读取器,专门用来解析xml类型的配置文件。

Ⅱ.给reader对象设置环境对象、资源加载器、类和资源解析器
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

new ResourceEntityResolver(this):设置资源解析器。
->super(resourceLoader.getClassLoader()):调用实体类解析器。
->new PluggableSchemaResolver(classLoader):加载xml文件中约束文件的解析器,然后对xml文件进行解析。
this.schemaResolver属性值为null
this.schemaResolver属性已经有值
修改toString()方法。该方法在程序运行时断点是进不去的
修改toString()方法之后执行完那一行代码后schemaMappings值仍然为空

Ⅲ.initBeanDefinitionReader(beanDefinitionReader)

初始化beanDefinitionReader对象,此处设置配置文件是否要进行验证。此处使用了适配器设计模式,把传入的beanDefinitionReader对象变成另一个对象。适配器模式的功能就是用来做接口适配,主要解决的是接口不兼容的问题,它把传入的对象封装成另一个对象。

Ⅳ.loadBeanDefinitions(beanDefinitionReader):开始并完成beanDefinition的加载
(2)Spring的xml配置文件加载流程

设计模式之适配器模式:BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)),BufferedReader是字符流而inputStream是字节流,通过InputStreamReader将字节流转换成字符流,然后BufferedReader才能操作。

①new ClassPathXmlApplicationContext(“spring-${username}.xml”):容器入口
②this(new String[] {configLocation}, true, null)

创建一个新的ClassPathXmlApplicationContext,从xml文件中加载给定的bean定义信息,并且刷新上下文。

Ⅰ.super(parent):准备资源加载器
new PathMatchingResourcePatternResolver(this)

调用父类构造。在抽象类AbstractApplicationContext的无参构造中通过getResourcePatternResolver()方法创建了一个路径匹配资源模式解析器PathMatchingResourcePatternResolver用来解析xml配置文件,简单来说就是初始化了一个资源加载器方便后续加载xml文件。

Ⅱ.setConfigLocations(configLocations):找到资源文件

该步骤处理逻辑过程中会将refresh变量的值设置为true以方便后面代码进入refresh()容器刷新逻辑。
该步骤完成后会将xml配置文件路径记录在字符串数组变量configLocations里面。

Ⅲ.refresh():容器刷新前准备工作

prepareRefresh():准备刷新容器。
->obtainFreshBeanFactory():创建容器。
->refreshBeanFactory():加载xml配置文件属性并封装成BeanDefinition对象,并将对象设置为BeanFactory对象的属性。
->loadBeanDefinitions(beanFactory):XML文件相关信息读取及解析。
->loadBeanDefinitions(beanDefinitionReader):开始beanDefinition的加载。
->reader.loadBeanDefinitions(configLocations):从存放xml配置文件路径的字符串数组中取出每个路径,然后根据每个路径获得xml文件的资源对象数组。
->loadBeanDefinitions(new EncodedResource(resource)):根据资源对象数组中的每个资源对象获取其对应的字节输入流。
->doLoadBeanDefinitions(inputSource, encodedResource.getResource()):根据资源对象的字节输入流解析出对应的Document对象,在具体解析过程中需要根据xml文件的约束条件进行不同格式的解析。
->doLoadDocument(inputSource, resource):将xml文件的标签和属性值解析成Document对象,得到的Document对象中包含从xml文件解析出来的各个节点信息。
->registerBeanDefinitions(doc, resource):将文档的节点信息封装成一个个的BeanDefinition对象。
->createBeanDefinitionDocumentReader():定义文档读取器BeanDefinitionDocumentReader。
->registerBeanDefinitions(doc, createReaderContext(resource)):通过文档读取器对文档进行解析。
->doRegisterBeanDefinitions(doc.getDocumentElement()):对取出的文档对象进行解析。
->parseBeanDefinitions(root, this.delegate):执行文档解析。
->parseDefaultElement(ele, delegate):根据元素类型决定解析默认标签还是自定义标签,该方法是解析默认标签。
->processBeanDefinition(ele, delegate):解析bean标签。当然解析不同标签会进去不同的if代码块,如果嵌套多层标签,则会迭代doRegisterBeanDefinitions(ele)方法进行解析。
->registerBeanDefinition(bdHolder, getReaderContext().getRegistry()):向Spring IOC容器注册解析得到的beandefinition对象信息。
->beanDefinitionMap.put(beanName, beanDefinition):注册beanDefinition。以beanName为key,以beanDefinition为value存入ConcurrentHashMap中。
->beanDefinitionNames.add(beanName):记录beanName。将beanName也就是xml文件中bean对应的id存入ArrayList中。
beanDefinitionMap和beanDefinitionNames是IOC容器(DefaultListableBeanFactory)的重要属性。至此,从xml中解析出来的bean的id以及根据bean信息封装的BeanDefinition对象信息已经存入IOC容器中

2.getBeanFactory()

返回当前实体的beanFactory属性。

五、Spring自定义xml标签

(一)代码+测试

1.属性实体类

public class User {
   
    private String username;
    private String email;
    private String password;
    public String getUsername() {
   
        return username;
    }
    public void setUsername(String username) {
   
        this.username = username;
    }
    public String getEmail() {
   
        return email;
    }
    public void setEmail(String email) {
   
        this.email = email;
    }
    public String getPassword() {
   
        return password;
    }
    public void setPassword(String password) {
   
        this.password = password;
    }
}

2.实体类解析器

public class UserBeanDefinitionParser  extends AbstractSingleBeanDefinitionParser {
   
    // 返回属性值所对应的对象
    @Override
    protected Class<?> getBeanClass(Element element) {
   
        return User.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
   
        // 获取标签具体的属性值
       String userName = element.getAttribute("userName");
       String email = element.getAttribute("email");
       String password = element.getAttribute("password");

       if(StringUtils.hasText(userName)){
   
           builder.addPropertyValue("username",userName);
       }
       if (StringUtils.hasText(email)){
   
           builder.addPropertyValue("email",email);
       }
       if (StringUtils.hasText(password)){
   
           builder.addPropertyValue("password",password);
       }
    }
}

3.命名空间处理器

public class UserNamespaceHandler extends NamespaceHandlerSupport {
   
    @Override
    public void init() {
   
        registerBeanDefinitionParser("user",new UserBeanDefinitionParser());
    }
}

4.测试类:

public class CustomTagTest {
   
    public static void main(String[] args) {
   
        ApplicationContext context = new ClassPathXmlApplicationContext("customTag.xml");
        User user=(User)context.getBean("msb");
        System.out.println("username:"+user.getUsername()+"  "+"email:"+user.getEmail());
    }
}

5.资源文件

资源文件

(1)spring.handlers
http\://www.mashibing.com/schema/user=com.mashibing.myDemo.customTag.xmlConfig.UserNamespaceHandler
(2)spring.schemas
http\://www.mashibing.com/schema/user.xsd=META-INF/user.xsd
(3)user.xsd
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.mashibing.com/schema/user"
        xmlns:tns="http://www.mashibing.com/schema/user"
        elementFormDefault="qualified">
    <element name="user">
        <complexType>
            <attribute name ="id" type = "string"/>
            <attribute name ="userName" type = "string"/>
            <attribute name ="email" type = "string"/>
            <attribute name ="password" type="string"/>
        </complexType>
    </element>
</schema>
(4)customTag.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:msb="http://www.mashibing.com/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.mashibing.com/schema/user http://www.mashibing.com/schema/user.xsd">
    <msb:user id="msb" userName = "tony" email = "tony@163.com" password="111111"></msb:user>
</beans>

6.实现步骤

(1)定义一个属性实体类,用来封装属性;
(2)定义实体类的解析器。

继承AbstractSingleBeanDefinitionParser抽象类,重写getBeanClass方法用来返回实体对象的字节码对象;
重写doParse方法用来获取具体标签的属性值,然后存入BeanDefinitionBuilder对象中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值