spring源码学习(一)
由于本人水平有限,本文内容较为简单,仅供个人学习笔记,或者大家参考,如果能够帮助大家,荣幸之至!本文主要分析AnnotationConfigApplicationContext实例化之后,到底干了那些事情。
- 首先通过实例化applicationContext
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class); AnnotationConfigApplicationContext.getBean("beanName");
分析:第一句实例化annotationConfigApplicationContext,初始化了spring的环境,第二句就可以从spring ioc容器中获取bean。
- 接下来查看构造方法
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
- 分析:this()方法会先调用父类的构造函数,再调用当前类的默认构造方法,在父类的构造方法当中,实例化了DefaultListableBeanFactory对象,spring当中的默认工厂, 在当前类的构造函数当中实例化了一个reader和scanner。
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
- 这里的beanFactory很重要,后续实例化beanDefinition、beanName都存储在这里。
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
- reader用来读取被加了注解的beanDenition,以及在当前的构造函数当中,会实例化spring当中非常重要的五个beanPostProcessor(后续分析),以及一个beanFactory,在本文最后会对其分析。
- scanner顾名思义是用来扫描的,只不过是提供外部程序员来调用的,spring内部并没有使用到。
public void register(Class<?>... annotatedClasses) {
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
this.reader.register(annotatedClasses);
}
- 这里的register方法就是调用了我们在构造函数当中初始化的reader,来完成注册beanDenifition。这里注册的类参数是一个数组,通过for循环来处理。注册一个bean之后需要调用refresh()方法,来完成实例化。如果注册了Appconfig类的话,如果不调用refresh()方法,项目会报错,如果注册的是普通的bean,在通过getBean来获取的时候,底层方法会手动调用refresh()当中的方法。接下来我们看register()当中的方法。
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(instanceSupplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
customizer.customize(abd);
} BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
- 分析:首先会判断是否跳过解析,如果元数据为null,或者没有加condition注解则返回false。因为该beanDenifition是初始化new出来的,所以元数据永远不会为空。接下来获取bean的作用域范围,默认是单例,生成beanName,接下来解析类的通用注解,比如说lazy,primary,description等等注解。处理限定符注解,还有自定义注解。beanDefinitionHolder主要是为了传值,在注册beanDenifition时候可以少传一个参数。这里传的这几个参数永远为null,比如@Nullable Supplier<T> instanceSupplier, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers,所以不用解析,如果我们想要解析,只能获取到reader对象,手动调用传参数给他。
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
- map当中存储key:beanName,value:beanDefinition,beanDefinitionName集合当中存储所有的beanDenifitionName。
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
- 源码当中处理类的别名,spring当中如果设置了别名,可以通过id或者别名从spring容器当中获取类的实例。
接下来查看最后一个refresh方法,spring当中的bean生命周期,就是从这里开始的
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh(); ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
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();
}
}
}
分析:ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();获取我们的beanFactory,bean的生命周期就是从这里开始的,prepareBeanFactory顾名思义准备beanfactory,包括设置类加载器、解析器(解析类似与el表达式的页面语句,由spring提供的)、属性编辑器(spring boot当中的yml配置)、这里最重要的是添加了一个BeanPostProcessor,beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));来看这里做了什么事情!
class ApplicationContextAwareProcessor implements BeanPostProcessor
该类继承自BeanPostProcessor,实现了这两个方法
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
缓存预热之时,我们会使用@PostConstruct注解初始化init()方法,在构造函数之后执行,@preDestroy在销毁之后执行。后置处理器spring提供给我们的扩展点,这两个方法会在init方法的前后执行,spring当中的AOP也是这样来完成对IOC的加强的,已经把bean暴漏出来了,在这里返回代理对象即可。接下来看ApplicationContextAwareProcessor实现后置处理器做了那些事情。
@Override
@Nullable
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null; if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
} if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
} return bean;
} private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
if (!bean.getClass().getSimpleName().equals("IndexDao"))
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
分析:这里主要判断是否实现了ApplicationContextAware接口,如果实现了,就把applicationContext注入给他。此时就可以解释为什么实现了applicationcontext接口,重写set方法,就可以获取applicationContext,解决单例模式下获取原型对象了。关于spring当中的其他后置处理器,会在后续文章中更新!
spring源码学习(一)的更多相关文章
- spring源码学习之路---深入AOP(终)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章和各位一起看了一下sp ...
- spring源码学习之路---IOC初探(二)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...
- Spring源码学习
Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...
- Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md
写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...
- Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签
写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...
- Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作
写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...
- Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件
写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...
- 【目录】Spring 源码学习
[目录]Spring 源码学习 jwfy 关注 2018.01.31 19:57* 字数 896 阅读 152评论 0喜欢 9 用来记录自己学习spring源码的一些心得和体会以及相关功能的实现原理, ...
- Spring 源码学习——Aop
Spring 源码学习--Aop 什么是 AOP 以下是百度百科的解释:AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程通过预编译的方式和运行期动态代理实 ...
- Spring 源码学习 04:初始化容器与 DefaultListableBeanFactory
前言 在前一篇文章:创建 IoC 容器的几种方式中,介绍了四种方式,这里以 AnnotationConfigApplicationContext 为例,跟进代码,看看 IoC 的启动流程. 入口 从 ...
随机推荐
- “全栈2019”Java第九十六章:抽象局部内部类详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- jmeter+ant+jenkins+mac 报告优化(三) 使用命令行执行jmeter方式生成多维度的图形化HTML报告
1.在构建中填写如下命令: 2.start.sh文件的内容 cd /Applications/apache-jmeter-3.0/bin/ CURTIME=`date +%Y%m%d%H%M` ./j ...
- css 实现关闭按钮 X
.close::before { content: "\2716";} 然后就显示出来了 这里有个更直接的例子 <!DOCTYPE html> <html lan ...
- Myeclipse设置自动联想功能
///声明,博客园暂无转载功能,这篇博客是转载自贞心真义. 最近初学Java,正在使用MyEclipse来编写新的项目,刚开始打开MyEclipse感觉这个工具既陌生又熟悉,熟悉之处在于编辑器的几大共 ...
- Android脚本打包
最近项目中需要添加应用渠道,我使用的是友盟统计,对于不同渠道需要编译不同版本,对于开发者说编译一次,手动操作还是可以接受的,但是项目发布版本频率较高,而且渠道很多,这就是一个体力活,而且手动打包还比较 ...
- Laravel5.5 引入并使用第三方类库操作
理论上,Laravel5系列都支持,各位可以一试.我这里使用5.5版本. 我这里引入了一个将汉字转化为拼音的类库测试,一起来看看吧! 首先,在laravel的app目录下自定义一个文件夹,我用的名字是 ...
- C#-WebForm-★★★LinQ-数据的条件组合查询并进行分页展示(未加各种限定)★★★
前台代码: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.as ...
- kao shi
1 #include "date.h" #include "utils.h" #include <iostream> using std::cout ...
- F6&F7adjust the volume
Windows Registry Editor Version 5.00[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard La ...
- solr 服务搭建
1. linux 中 安装jdk, tomcat, 2. 下载 solr-4.10.3.tgz.tgz 并解压 解压后文件夹:solr-4.10.3 3. 将 solr-4.10.3/dist 下 ...