从源码学Spring+debug模式
Posted 早上真起不来!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从源码学Spring+debug模式相关的知识,希望对你有一定的参考价值。
文章目录
BeanDefinition对象
spring是一个容器,里面放了一个个bean对象,spring帮我们管理这些对象,那spring是怎么知道我们需要管理那些对象呢?
-
xml 和 注解(@Component) 而这些又被称为bean的定义信息,当我们写好这些配置之后要交给我们的容器,这些配置转成一个叫BeanDefinition对象(BD对象),然后通过BD对象进行创建对象操作
-
但是xml是怎样解析的呢? 通过io流读文件,读取到一堆字符串,然后通过一些工具(比如sax,dom4j)转成我们的document对象
再将我们document中的数据给到BD对象中
- 那注解又是怎样获取的呢?通过获取某个上面的注解信息,再获取对应的class,就可以把class中的属性等方法BD对象中
当我们定义好一系列配置文件的时候 可以把这些具体的属性值(value)放到BD对象中,这个时候spring就知道要创建哪些基本的对象了
那又是怎么创建对象的呢?
反射还是new?
几乎所有的框架都是反射,但是反射效率性能低为什么还用反射呢?
反射它是足够灵活的,当创建10w个对象的时候反射才可能出现性能问题
但是我们BD对象通过反射就直接创建对象了,那spring就变的太简单了
spring有一套完整的生态,最根本最核心是因为 扩展性,spring给我们留了很多的口子
BeanFactory
那BeanFactory怎么理解呢?
官方翻译:访问 Spring bean 容器的根接口,也就是相当于入口
但是我们使用的时候好像和这个BeanFactory没什么关系,我们一般这么用:context.getBean(Peoson.class)
但是面试的时候一般会问到:BeanFactory和ApplicationContext是什么关系?
查看父类
ApplicationContext继承了BeanFactory,提供了一系列更加完善的功能
而且BeanFactory只提供了一些getxx方法
虽然我们用的是ApplicationContetx但是继承的还是BeanFactory
所以BeanFactory可以理解为入口或者容器对象
我们可以把BeanFactory的实现子类当做容器
那我们的BeanFactory可以获取到我们的BD对象吗?
BeanFactory接口中没有get他的方法,但是BeanFactory是一个顶级接口 我们可以去他的实现类中找
这就是spring源码很乱的原因之一,它存在了一堆的接口或者一堆的抽象类
那我们可以通过Beanfactory来获取我们的BD
下面这种情况在加载到BD中的时候是否把value替换了?并没有而是在PostProcessor中实现的
PostProcessor
先来看看
PostProcessor 被称为后置处理器或者增强器(更具体),就是用来扩展一些功能的,先看看他常见的实现
-
BeanFactoryPostProcessor
-
BeanPostProcessor
BeanFactoryPostProcessor
解析 bean 定义属性值中的占位符的属性资源配置器的抽象基类,这是什么意思呢?
也就是我们的
在我们当前的BeanFactoryPostProcessor对象里面可以完成对BD中的值完成替换
BD对象(保留原始值) 通过 BeanFactoryPostProcessor 得到最终的BD对象
最终的BD对象
debug分析:
DefaultListableBeanFactory 我们所有的bean对象都在这里存放,相当于一个大容器
debug的时候 有一个重点方法,refresh
里面有13个方法
public void refresh() throws BeansException, IllegalStateException
synchronized (this.startupShutdownMonitor)
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
/**
* 前戏 做容器刷新前的准备工作
* 1、设置容器的启动时间
* 2、设置活跃状态为true
* 3、设置关闭状态为false
* 4、获取Environment对象,并加载当前系统对象的属性到Environment对象中
* 5、准备监听器和事件的集合对象,默认为空的集合
*/
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
/**
* 创建容器对象:DefaultListableBeanFactory
* 加载xml配置文件的属性值到当期工厂中,最重要的是BeanDefinition 也就是BD对象
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
/**
* BeanFactory的各种准备工作,对各种属性进行填充
*/
prepareBeanFactory(beanFactory);
try
// Allows post-processing of the bean factory in context subclasses.
// 子类覆盖方法做额外的处理,此处我们自己一般不做任何扩展工作,但是可以查看web中的代码,是有具体实现的
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
// 调用各种beanFactory处理器,这个就是替换占位符的方法
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注册bean的处理器,这里只是注册功能,真正调用再getBean
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
// 为上下文初始化源,及不同语言的消息体,国化处理,再springmvc的时候会有国际化。
initMessageSource();
// Initialize event multicaster for this context.
// 初始化事件监听多路广播起
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 留给子类来初始化其他的bean
onRefresh();
// Check for listener beans and register them.
// 在所有注册的bean中查找listener bean,注册到广播器中
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 初始化剩下的单实例(非懒加载的)
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent
finishRefresh();
catch (BeansException ex)
if (logger.isWarnEnabled())
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
finally
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
obtainFreshBeanFactory
中的refreshBeanFactory
方法
@Override
protected final void refreshBeanFactory() throws BeansException
// 如果存在beanFactory,则摧毁beanFactory
if (hasBeanFactory())
destroyBeans();
closeBeanFactory();
try
// 创建DefaultListableBeanFactory对象
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 为了序列化指定id,设置从id反序列化到beanFactory对象
beanFactory.setSerializationId(getId());
// 定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖
customizeBeanFactory(beanFactory);
// 初始化documentReader,并进行XML文件读取及解析,默认命名空间的解析,自定义标签的解析
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
catch (IOException ex)
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
- 进到createBeanFacotury,新建一个DefaultListableBeanFactory 对象
- 初始化的beanFactory
- 加载后的beanFactory
- 发现我们的占位符还是没有替换
当我们继续debug走到reflash的invokeBeanFactoryPostProcessors方法的时候就是替换的时候,invoke(调用)增强处理器
那如果我们自己去替换一个bean,也就是处理BD可以吗?
- 只要去实现beanFactoryProcessor接口即可,拿到我们的BD,就可以set值了
- 但是spring能识别到我们自己写的吗?不能!我们需要注册到容器中可以用bean 或者@Component
- 当然beanFactoryProcessor可以定义多个,来处理BD
创建Bean对象
当我们通过BeanFactoryPostProcessor拿到我们的完整的BD后就要开始创建对象了
那创建对象又是怎样一个环节呢?
属性赋值
spring除了我们自己写的配置对象,他还跟我们提供了许多自己的东西,比如BeanFactory、ApplicationContext
我们把对象交给容器,当然属性的set操作(就是属性赋值)也交给容器,
那么什么时候调?该调用什么方法?
所以定义统一的规范接口,然后实现功能
Aware
那如果我们自己set BeanFactory、ApplicationContext,该怎么做呢?
那么属性赋值到底是赋什么值?
准确来说,属性赋值分为:
- 用户自定义属性赋值
- 容器对象属性赋值 BeanFactory、ApplicationContext
注意,@Autowired可以代替Aware,所以基本不用aware,但是如果是xml写的就得用aware,所以说spring生态完善就在这里,他尽可能的把所有情况给到,
所以Aware为什么为空,就是起到一个标识作用
那我们赋值完后是否获取一个完整的bean对象?
当我们完成属性赋值完后,正常情况下是直接调用使用即可,但是spring在赋值完后 完成一系列增强实现,都属于bean的扩展实现
bean的扩展实现
BeanPostProcessor
就是我们的BeanPostProcessor
说明其中还有一个初始化过程,这个初始化和之前的不相同
说到这个BeanPostProcessor其中还有一个特别重要的东西AOP,
AOP
当我们的有了一个bean对象之后我们想生成他的代理对象,把当前bean对象的引用传递给BeanPostProcessor,在xxafter做逻辑处理,让他来生成我们具体的代理商
走到我们的wrapIFNecessary方法
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey)
// 如果已经处理直接返回
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName))
return bean;
// 这里advisedBeans缓存了已经进行了代理的bean,如果换存中存在则直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey)))
return bean;
// 这里isInfrastructureClass 用于判断当前的bean是否是spring自带的bean
// 自带的bean是不需要进行代理的,shouldSkip则判断当前bean是否应该被略过
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName))
// 对当前bean进行缓存
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
// Create proxy if we have advice.
// 获取当前bean的Advices和Advisors
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 对当前bean的代理状态进行缓存
if (specificInterceptors != DO_NOT_PROXY)
// 对当前bean的代理状态进行缓存
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 根据获取的Advices和Advisors为当前bean生成代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// 缓存生成的代理bean的类型,并且返回生成的代理bean
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
再往下走
所以再来看看这句话 AOP也是IOC核心创建流程中的个扩展点
再接着往下就到了初始化步骤,其实叫初始化不严谨,应该叫执行初始化调用方法
执行初始化调用方法
那什么叫执行初始化调用方法?
他会判断是否实现了InitializingBean接口,是 则调用afterPropertiesSet,否则调用里面有一个invokeInitMethod(最基本的初始化方法)方法,
那为什么会有这个呢?
因为spring帮我们做了一切事情,spring给我们留了一个口子,让我们也可以自己干预
拿到完整bean对象
而销毁对象怎么销毁呢?
销毁对象是在关闭容器的时候,而我们没有关闭过容器,所以不用我们自己做
bean的生命周期
再看看整个bean的生命周期
这里的createBeanInstance和populateBean后面说
@Autowaired
而这里面有一个很重的注解 @PostContruct,主要就是执行初始化方法的
那我们加上这个注解之后,被使用的那个对象是否要经历完整的bean生命周期?
肯定要!
@PostContruct是怎么实现的呢?
先来看看常用的
@Autowaired
@Value
那是怎么调用的呢?
先思考一个问题,spring是先有的xml还是注解,这个当然是xml,那么当spring发明初期只有xml的时候,spring已经有了一套完整生态体系,也就是有了一个完整的bean生命周期流程
当开发了注解,就要完成注解的功能,那么是推翻xml重来 还是扩展实现?
一定是扩展实现,但是在bean的生命周期里面哪里给我们留了扩展的点?
BeanProcessor
那么就可以知道
@PostContruct
小结
IOC的核心实现原理
spring的扩展实现
- BeanFactoryPostProcessor:针对BD
- BeanPostProcessor:针对Bean对象
bean的整个生命周期
spring aware接口的意义
起到一个标识作用,定义统一的规范接口,然后实现功能,属性赋值
BeanFactory和FactoryBean的关系
共同点:都是用来创建对象(BD)的
不同的是FactoryBean中包含 了三个方法
- isSingleton:判断是否是单例对象
- getObjectType:获取对象类型
- getObject:用户可以按照自己任意的方式来创建对象(可以使用new、动态代理、反射)
BeanFactory是一个标准化流程,流水线
FactoryBean是私人订制
怎么实现呢?
public class Student implements FactoryBean<Student>
public Student getObject() throws Exception
// new方式创建对象
Student student = new Student();
// 代理方式创建对象
Object o = Proxy.newProxyInstance(Student.class.getClassLoader(), Student.class.getInterfaces(),
new InvocationHandler()
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
// 一些处理逻辑
return null;
);
return (Student) o;
//return student;
public Class<?> getObjectType()
return Student.class;
public boolean isSingleton()
return true;
当我们在生成每一个具体对象的时候,他有一个标准的bean对象的一个配置流程
那怎么识别是FactoryBean的呢?什么时候识别的呢?
前面说了有一个大容器DefaultListableBeanFactory
在上面refresh方法中的finishBeanFactoryInitialization(初始化剩下的单实例(非懒加载的)),中的preInstantiateSingletons可以找出识别FactoryBean的位置
public void preInstantiateSingletons() throws BeansException
if (logger.isTraceEnabled())
logger.trace("Pre-instantiating singletons in " + this);
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
// 将所有的BeanDefinition的名字创建一个集合
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
// 触发所有非延迟加载单例bean的初始化,遍历集合的对象
for (String beanName : beanNames)
// 获取BD对象 合并父类BeanDefinition
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 条件判断,抽象、单例、非懒加载
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit())
// 获取到BD 第一个判断的就是是否是FactoryBean,是就会走处理逻辑,里面会调用getObject方法,把对象创建出来
if (isFactoryBean(beanName))
// 根据&+beanName来获取具体的对象
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
// 进行类型转换
if (bean instanceof FactoryBean)
FactoryBean<?> factory = (FactoryBean<?>) bean;
// 判断这个beanFactory是否希望立即初始化
boolean isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
if (isEagerInit)
// 如果beanName不是FactoryBean,只是普通的bean,则通过beanName获取bean实例
getBean(beanName);
else
getBean(beanName);
// .....
以上是关于从源码学Spring+debug模式的主要内容,如果未能解决你的问题,请参考以下文章
Unity HTFramework框架(四十)Debug的性能监控
Eclipse开发环境debug模式调试断点从jar跳到源码
初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段