(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):面向切面编程
(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抽象类提供的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)。
3.DI(Dependency Injection):依赖注入
DI是IOC的实现手段。依赖注入分两种情况:
(1)属性注入
(2)构造器注入
(二)Spring如何使用
1.IOC容器中加载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对象创建的完整过程:
①实例化
在堆内存开辟一块内存空间,此时对象的属性值都是默认值。
②初始化
给对象属性设置值。
Ⅰ.填充属性
Ⅱ.设置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.测试:
d.也可以自定义类实现EnvironmentAware接口,通过该自定义对象可以获取该对象所在环境的的属性信息:
public class MyAware implements ApplicationContextAware, EnvironmentAware {
...
@Override
public void setEnvironment(Environment environment) {
System.out.println("from MyAware: "+environment.getActiveProfiles());
}
}
e.测试结果如下:
Ⅲ.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容器的入口
②后置处理器增强器(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());
}
}
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());
}
}
Ⅱ.BeanPostProcessor:用来增强bean信息
在容器创建过程中需要动态改变bean的信息可以通过BeanPostProcessor接口来实现。
(6)使用时从容器中获取bean对象
(三)Sping Bean
1.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中分别提供了获取环境信息的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());
}
}
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。
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。
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。
Ⅳ.initializeBean(beanName, exposedObject, mbd):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子类。
12.finishRefresh():完成容器刷新
完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人。
四、Spring启动流程细节
(一)prepareRefresh():创建容器前准备工作
Environment接口是用来管理系统环境和属性相关信息的,在Spring中StandardEnvironment类间接实现了Environment接口,它没有无参构造器,但是在其抽象父类AbstractEnvironment中无参构造器中调用了它自己空实现的customizePropertySources方法,StandardEnvironment类重写了父类的那个方法来实现对自定义属性信息的管理。
而在SpringBoot中,StandardServletEnvironment类又继承了Spring的StandardEnvironment类,在其基础上做了增强,增加了另外三个属性值:servletContextInitParams、servletConfigInitParams、jndiProperties,其中名称中包含InitParams关键字的属性是用来处理web.xml这个项目配置文件中init-param标签信息的。
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配置文件名中的占位符替换处理。
(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属性值将会为空。
5.prepareRefresh()之准备空集合applicationListeners
监听器集合属性在Spring框架中为空,但是在SpringBoot框架中是专门用来放各种监听器的,所以监听器集合也是Spring提供给其他框架的一个扩展点。
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)方法。
Ⅰ.案例:修改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"));
}
}
Ⅱ.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文件进行解析。
Ⅲ.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对象中。