prepareBeanFactory方法源码跟踪
看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead
代码过宽,可以shift + 鼠标滚轮 左右滑动查看
AbstractApplicationContext类refresh()方法中的第三个调用方法prepareBeanFactory()的跟踪。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
// Prepare the bean factory for use in this context.
// 准备在上下文中bean factory的使用
prepareBeanFactory(beanFactory);
···
}
断点进入跟踪。
此方法的实现在AbstractApplicationContext类中。
prepareBeanFactory(零)
/**
* Configure the factory's standard context characteristics,
* such as the context's ClassLoader and post-processors.
*
* 配置工厂的标准上下文特征,比如上下文的ClassLoader和post-processors
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
//告知内部的bean工厂,使用上下文的类加载器
beanFactory.setBeanClassLoader(getClassLoader());
//设置bean表达式解析器,
//StandardBeanExpressionResolver内部expressionParser属性默认SpelExpressionParser类型
//spel = spring el表达式
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
//将ResourceEditorRegistrar实例添加到工厂的propertyEditorRegistrars属性中,
//propertyEditorRegistrars是一个LinkedHashSet,里面的元素将会应用到工厂bean中
//ResourceEditorRegistrar持有上下文和environment的引用
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
// 使用上下文回调配置bean 工厂
//在工厂的beanPostProcessor属性中添加处理器,beanPostProcessor是一个ArrayList
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
//在工厂的ignoredDependencyInterfaces属性中添加Aware系列接口,
//ignoredDependencyInterfaces是一个HashSet
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
// 在普通的工厂中,BeanFactory接口并没有按照resolvable类型进行注册
// MessageSource被注册成一个Bean(并被自动注入)
//BeanFactory.class为key,beanFactory为value放入到了beanFactory的resolvableDependencies属性中
//resolvableDependencies是一个ConcurrentHashMap,映射依赖类型和对应的被注入的value
//这样的话BeanFactory/ApplicationContext虽然没有以bean的方式被定义在工厂中,
//但是也能够支持自动注入,因为他处于resolvableDependencies属性中
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
//再将上下文的一些接口与上下文本身做映射,一一放入到resolvableDependencies中
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// 检测LoadTimeWeaver,如果有就准备织入
//1.跟踪进入,浅看下containsBean方法
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
//如果有LoadTimeWeaver,加入bean后处理器
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
// 为匹配类型设置一个临时的ClassLoader
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
// 注册默认的environment beans
// 判断目前这个bean工厂中是否包含指定name的bean,忽略父工厂
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
//虽然XmlWebApplicationContext中持有默认实现的StandardServletEnvironment
//但是没有注册到beanFactory中,通过getEnvironment方法拿到持有的引用
//2.注册environment单例
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
//注册systemProperties单例
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
//注册systemEnvironment单例
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
1.containsBean
跟踪标记为1的方法
此方法的实现在AbstractBeanFactory类中
//1.跟踪看下containsBean方法
beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)
/**
* Does this bean factory contain a bean definition or externally registered singleton
* instance with the given name?
* <p>If the given name is an alias, it will be translated back to the corresponding
* canonical bean name.
* <p>If this factory is hierarchical, will ask any parent factory if the bean cannot
* be found in this factory instance.
* <p>If a bean definition or singleton instance matching the given name is found,
* this method will return {@code true} whether the named bean definition is concrete
* or abstract, lazy or eager, in scope or not. Therefore, note that a {@code true}
* return value from this method does not necessarily indicate that {@link #getBean}
* will be able to obtain an instance for the same name.
*
* bean工厂是否包含一个给定name的bean definition,或者外部被注册的单例bean?
* 如果name是一个别名,会被转换成对应的beanName
* 如果工厂是有层级的,那么当工厂实例中找不到这个bean时,就会去父工厂中查找
* 如果找到匹配name的bean definition或者单例,那么这个方法会返回true
* 不管这个bean definition是具体的还是抽象的,提前加载还是懒加载,是否在范围中。
* 因此,注意从这个方法中返回的true值并不代表从getBean方法中能够获取一个同名称的实例
*/
@Override
public boolean containsBean(String name) {
//1.1对name进行必要的转换
String beanName = transformedBeanName(name);
//singletonObjects或者beanDefinitionMap中已注册beanName则进入条件
//说明该beanName有对应的bean definition,或者单例bean
if (containsSingleton(beanName) || containsBeanDefinition(beanName)) {
//name开头不为&返回true,如果带了&但是是FactoryBean也返回true
//要注意下FactoryBean和BeanFactory的区别,可以看下文参考链接
return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(name));
}
// Not found -> check parent.
// 如果没有找到对应beanName的bean或者bean definition,那么从父工厂查找
BeanFactory parentBeanFactory = getParentBeanFactory();
return (parentBeanFactory != null && parentBeanFactory.containsBean(originalBeanName(name)));
}
1.1 transformedBeanName
跟踪标记为1.1的方法
此方法的实现在AbstractBeanFactory类中
//1.1对name进行必要的转换
String beanName = transformedBeanName(name);
/**
* Return the bean name, stripping out the factory dereference prefix if necessary,
* and resolving aliases to canonical names.
*
* 返回bean name,剥离factory dereference 前缀,并将别名解析为bean name
*/
protected String transformedBeanName(String name) {
//总的来说,如果name代表factory,那么name前就带有&前缀,去掉此前缀
//如果这个name是beanName,则直接返回,如果name是alias,在aliasMap中查找对应的beanName,再返回
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
//先看下transformedBeanName方法的实现
//进入BeanFactoryUtils类中
/**
* Return the actual bean name, stripping out the factory dereference
* prefix (if any, also stripping repeated factory prefixes if found).
*
* 返回真实的beanName,剥离工厂前缀(如果有的话,也剥离重复的工厂前缀)
*/
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
String beanName = name;
//FACTORY_BEAN_PREFIX常量为:&
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
return beanName;
}
//再进入canonicalName方法查看
//此方法在SimpleAliasRegistry中实现,被默认bean工厂间接继承
/**
* Determine the raw name, resolving aliases to canonical names.
*
* 确定原生的name,将别名解析为BeanName
*/
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
// 处理别名
String resolvedName;
do {
//拿到canonicalName对应的实际名称
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
//只有当canonicalName在aliasMap中对应的value为null时,才跳出循环
//这时候说明canonicalName已经不作为其他任何BeanName的别名,排除了间接引用
//canonicalName就为真正的beanName
while (resolvedName != null);
return canonicalName;
}
2.registerSingleton
跟踪标记为2的方法
此方法的实现在DefaultListableBeanFactory类中
//2.注册environment单例
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
//2.1调用父类方法,注册单例
super.registerSingleton(beanName, singletonObject);
//AbstractBeanFactory类中有个集合属性alreadyCreated
//里面保存在至少被创建过一次的beanName
//如果这个集合中存在beanName,那么说明已经进入了bean创建阶段
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
// 无法再修改启动时集合元素(为了稳定迭代)
synchronized (this.beanDefinitionMap) {
//beanName不在beanDefinitionMap中,说明是手动注册
if (!this.beanDefinitionMap.containsKey(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames.size() + 1);
updatedSingletons.addAll(this.manualSingletonNames);
updatedSingletons.add(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
// 仍然处于启动注册阶段
if (!this.beanDefinitionMap.containsKey(beanName)) {
//属于手动注册情况
//environment属于手动注册单例
this.manualSingletonNames.add(beanName);
}
}
//进入这个方法查看
clearByTypeCache();
}
/**
* Remove any assumptions about by-type mappings.
*
* 删除按照类型映射有关的任何假设
*/
private void clearByTypeCache() {
//allBeanNamesByType是单例和非单例beanName的映射,key是依赖类型
this.allBeanNamesByType.clear();
//仅单例beanName的映射,key是依赖类型
this.singletonBeanNamesByType.clear();
}
2.1 registerSingleton
跟踪标记为2.1的方法
此方法的实现在DefaultSingletonBeanRegistry类中
//2.1调用父类方法,注册单例
super.registerSingleton(beanName, singletonObject);
/**
* Register the given existing object as singleton in the bean registry,
* under the given bean name.
* <p>The given instance is supposed to be fully initialized; the registry
* will not perform any initialization callbacks (in particular, it won't
* call InitializingBean's {@code afterPropertiesSet} method).
* The given instance will not receive any destruction callbacks
* (like DisposableBean's {@code destroy} method) either.
* <p>When running within a full BeanFactory: <b>Register a bean definition
* instead of an existing instance if your bean is supposed to receive
* initialization and/or destruction callbacks.</b>
* <p>Typically invoked during registry configuration, but can also be used
* for runtime registration of singletons. As a consequence, a registry
* implementation should synchronize singleton access; it will have to do
* this anyway if it supports a BeanFactory's lazy initialization of singletons.
*
* 在给定的bean name下,将存在的对象作为单例注册在工厂中
* 给定的实例应该是完全初始化;工厂不执行任何初始化回调(特别是,他不会调用InitializingBean的
* afterPropertiesSet方法)
* 给定的实例也不接收任何销毁回调(像DisposableBean的destroy方法)
* 当在完整的BeanFactory运行时:
* 如果你的bean需要接收初始化或者销毁的回调,注册一个bean definition替代一个存在的实例
* 通常此方法在工厂配置时被调用,也能在运行时单例注册时被调用。
* 作为结果,工厂的实现应该同步单例的访问;如果支持BeanFactory的单例的延迟初始化就不得不这样做
*/
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
//不能注册两次
if (oldObject != null) {
throw new IllegalStateException("Could not register object [" + singletonObject +
"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
}
//进入这个方法
addSingleton(beanName, singletonObject);
}
}
/**
* Add the given singleton object to the singleton cache of this factory.
* <p>To be called for eager registration of singletons.
*
* 添加给定单例对象到工厂的单例缓存中
* 用来被提早注册的单例调用
*/
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//singletonObjects是一个ConcurrentHashMap
//用来缓存单例对象
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
//singletonFactories是一个HashMap
//里面缓存着单例工厂
this.singletonFactories.remove(beanName);
//早期单例对象
//earlySingletonObjects是一个HashMap
this.earlySingletonObjects.remove(beanName);
//registeredSingletons是一个LinkedHashSet
//被注册单例的集合,以注册的顺序包含着bean name
this.registeredSingletons.add(beanName);
}
}
这样,整个prepareBeanFactory方法也就跟踪完毕了。
接下来跟踪postProcessBeanFactory方法:
https://www.jianshu.com/p/c05aea93b939
参考
BeanFactory和FactoryBean的区别:https://www.cnblogs.com/aspirant/p/9082858.html
总结
- 为工厂设置类的加载器、表达式解析器、属性编辑器注册器等
- 为工厂添加后处理器、要忽略的依赖接口
- 在工厂中注册可解析的依赖
- 在工厂中提前注册一些单例Bean
prepareBeanFactory方法源码跟踪的更多相关文章
- loadBeanDefinitions方法源码跟踪(一)
看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 AbstractBeanDe ...
- obtainFreshBeanFactory方法源码跟踪
看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 AbstractApplic ...
- prepareRefresh方法源码跟踪
看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 AbstractApplic ...
- postProcessBeanFactory方法源码跟踪
看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 AbstractApplic ...
- loadBeanDefinitions方法源码跟踪(三)
因为字数超过了限制,所以分成了三篇,承接上篇: https://www.jianshu.com/p/46e27afd7d96 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 4.parseCus ...
- loadBeanDefinitions方法源码跟踪(二)
因为字数超过了限制,所以分成了三篇,承接上篇: https://www.jianshu.com/p/a0cfaedf3fc5 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 3.parseDef ...
- erlang下lists模块sort(排序)方法源码解析(一)
排序算法一直是各种语言最简单也是最复杂的算法,例如十大经典排序算法(动图演示)里面讲的那样 第一次看lists的sort方法的时候,蒙了,几百行的代码,我心想要这么复杂么(因为C语言的冒泡排序我记得不 ...
- Java源码跟踪阅读技巧
转:https://www.jianshu.com/p/ab865109070c 本文基于Eclipse IDE 1.Quick Type Hierarchy 快速查看类继承体系. 快捷键:Ctrl ...
- Thread.interrupt()源码跟踪
1 JDK源码跟踪 // java.lang.Thread public void interrupt() { if (this != Thread.currentThread()) checkAcc ...
随机推荐
- URL和 URI 的区别
URL:统一资源定位符 URI:统一资源标识符 URL 是 URI 的一个子集: 来源知乎 1.统一资源标识符 URI 就是在某一规则下能把一个资源独一无二的表示出来. 拿人做例子,假设这个世界上多有 ...
- 八 Spring的IOC的XML和注解的区别及其整合开发
xml和注解的区别 xml和注解整合开发 注解:取消扫描配置开启注解配置 扫描:<context:component-scan base-package="" /> ...
- 从零构建以太坊(Ethereum)智能合约到项目实战——学习笔记10
P57 .1-Solidity Types - 玩转 Solidity 数组 (Arrays) 学习目标 1.掌握Arrays的可变不可变的创建 2.深度理解可变数组和不可变数组之间的区别 3.二维数 ...
- 汇编语言从入门到精通-5微机CPU的指令系统1
微机CPU的指令系统 5.1 汇编语言指令格式 为了介绍指令系统中指令的功能,先要清楚汇编语言是如何书写指令的,这就象在学习高级语言程序设计时,要清楚高级语言语句的语义.语法及其相关规定一样. 5.1 ...
- python爬虫(七) mozillacookiejar
MozillaCookiejar 保存百度得Cookiejar信息: from urllib import request from urllib import parse from http.coo ...
- java 依赖注入
https://blog.csdn.net/coderder/article/details/51897721 前言 在软件工程领域,依赖注入(Dependency Injection)是用于实现控制 ...
- 最近公共祖先(LCA)问题
目录 最近公共祖先 1.向上标记法 2.树上倍增法 3.Tarjan算法 最近公共祖先 定义:给定一颗有根树,若结点 z 既是 x 的祖先,也是 y 的祖先,则称 z 是 x,y 的公共祖先.在 x, ...
- SpringBoot之数据访问和事务-专题三
SpringBoot之数据访问和事务-专题三 四.数据访问 4.1.springboot整合使用JdbcTemplate 4.1.1 pom文件引入 <parent> <groupI ...
- vscode调试开发C/C++程序
https://www.cnblogs.com/TAMING/p/8560253.html
- phpcms安装与使用
安装完wamp(或phpstudy)之后,将phpcms的安装文件复制入C:\wamp\www(或C:\phpStudy\PHPTutorial\WWW)文件夹下: 打开phpcms_v9_UTF8\ ...