前言

spring作为一个容器,可以管理对象的生命周期、对象与对象之间的依赖关系。可以通过配置文件,来定义对象,以及设置其与其他对象的依赖关系。

main测试类

 public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Bean bean = applicationContext.getBean(Bean.class);
System.out.println(bean);
}

ClassPathXmlApplicationContext类

  • application建立以后,可以通过refresh()进行重建,这样会将原来的application销毁,然后重新执行初始化

  • 构造方法

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
    throws BeansException { super(parent);
    //设置配置文件
    setConfigLocations(configLocations);
    if (refresh) {
    //核心方法
    refresh();
    }
    }

AbstractApplicationContext类

refresh方法

	@Override
public void refresh() throws BeansException, IllegalStateException {
// 同步,防止你在初始化的过程中被打断
synchronized (this.startupShutdownMonitor) {
// 准备工作
prepareRefresh(); // 配置文件被拆分成Bean定义,注册在BeanFactory中。
// 也就是Map<beanName, beanDefinition>
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// 到这一步,所有的Bean加载和注册都已经完成,但是还没开始初始化
// 如果实现了 BeanFactoryPostProcessor 接口,可以在初始化的时候做点事情
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
// 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
// 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
// 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
// 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
// 初始化当前 ApplicationContext 的 MessageSource, 国际化等
initMessageSource(); // Initialize event multicaster for this context.
// 初始化当前 ApplicationContext 的事件广播器
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
// 从方法名就可以知道,典型的模板方法(钩子方法),
// 具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
onRefresh(); // Check for listener beans and register them.
// 注册监听,监听器需要实现 ApplicationListener 接口
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
// 初始化所有的singletons , non-lazy除外
// 主要方法
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();
}
}
}

obtainFreshBeanFactory()方法

1、初始化 BeanFactory-》DefaultListableBeanFactory.class

DefaultListableBeanFactory beanFactory = createBeanFactory();

2、定义工厂的属性: 是否允许 Bean 覆盖、是否允许循环引用

3、加载 Bean 到 BeanFactory 中 -》 loadBeanDefinitions()方法

​ 3.1 读取xml配置文件,检查,解析

preProcessXml(root); //钩子
parseBeanDefinitions(root, this.delegate); //主要代码:将信息放在Factory的相关map中
postProcessXml(root); //钩子

​ 3.2 将xml配置文件的信息放到工厂的map里。

​ parseBeanDefinitions() => DefaultListableBeanFactory.class的registerBeanDefinition()方法

	this.beanDefinitionMap.put(beanName, beanDefinition);
// beanName的集合,和Map的key(beanName)对应
this.beanDefinitionNames.add(beanName);

具体不细讲,简述一下过程,就是factory有几个map,这里将xml里读出来的对象以Map<beanName, beanDefinition>的形式存储。

以及一个list保存了所有的beanName。

beanDefinition只是一个类的描述而已,和我们需要的对象还差了一大截。

4、返回携带各种信息的factory

finishBeanFactoryInitialization()方法

根据factory初始化所有的singletons , non-lazy除外

1、初始化特殊类,不展开啦。

2、缓存。还是DefaultListableBeanFactory.class。把之前我们上面讲到的带beanName的List转为数组保存到frozenBeanDefinitionNames里。

public void freezeConfiguration() {
this.configurationFrozen = true;
this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames);
}

3、初始化。

​ 3.1 拿到带beanName的List,遍历循环

List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

​ 3.2 特殊类FactoryBean等特殊处理,不展开了。

​ 3.3 进入对象实例化。

AbstractBeanFactory类的doGetBean()方法

AbstractAutowireCapableBeanFactory类的doCreateBean()

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException { ...
if (instanceWrapper == null) {
// 这里实例化 Bean,
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
...
}

AbstractAutowireCapableBeanFactory类的createBeanInstance()

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// Make sure bean class is actually resolved at this point.
// 确保已经加载了此 class
Class<?> beanClass = resolveBeanClass(mbd, beanName); // 校验一下这个类的访问权限
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
} if (mbd.getFactoryMethodName() != null) {
// 采用工厂方法实例化,注意,不是 FactoryBean
return instantiateUsingFactoryMethod(beanName, mbd, args);
} // Shortcut when re-creating the same bean...
// 如果不是第一次创建,比如第二次创建 prototype bean。
// 这种情况下,我们可以从第一次创建知道,采用无参构造函数,还是构造函数依赖注入 来完成实例化
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
// 构造函数依赖注入
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 无参构造函数
return instantiateBean(beanName, mbd);
}
} // Candidate constructors for autowiring?
// 判断是否采用有参构造函数
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
// 构造函数依赖注入
return autowireConstructor(beanName, mbd, ctors, args);
} // No special handling: simply use no-arg constructor.
// 调用无参构造函数
return instantiateBean(beanName, mbd);
}

AbstractAutowireCapableBeanFactory类的instantiateClass()

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
return getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
}, getAccessControlContext());
}
else {
// 实例化
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
// 包装一下,返回
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
return ctor.newInstance(args); //构造函数初始化对象
}
...
}

最后

感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

面试官:就问个Spring容器初始化和Bean对象的创建,你讲一小时了的更多相关文章

  1. Spring 中初始化一个Bean对象时依赖其他Bean对象空指针异常

    1. Bean依赖关系 一个配置类的Bean,一个实例Bean: 实例Bean初始化时需要依赖配置类的Bean: 1.1 配置类Bean @ConfigurationProperties(prefix ...

  2. Spring管理Filter和Servlet(在servlet中注入spring容器中的bean)

    在使用spring容器的web应用中,业务对象间的依赖关系都可以用context.xml文件来配置,并且由spring容器来负责依赖对象 的创建.如果要在servlet中使用spring容器管理业务对 ...

  3. 【String注解驱动开发】面试官让我说说:如何使用FactoryBean向Spring容器中注册bean?

    写在前面 在前面的文章中,我们知道可以通过多种方式向Spring容器中注册bean.可以使用@Configuration结合@Bean向Spring容器中注册bean:可以按照条件向Spring容器中 ...

  4. 厉害!这份阿里面试官 甩出的Spring源码笔记,GitHub上已经爆火

    前言 时至今日,Spring 在 Java 生态系统与就业市场上,面试出镜率之高,投产规模之广,无出其右.随着技术的发展,Spring 从往日的 IoC 框架,已发展成 Cloud Native 基础 ...

  5. JVM工作原理和特点(一些二逼的逼神面试官会问的问题)

    作为一种阅读的方式了解下jvm的工作原理 ps:(一些二逼的逼神面试官会问的问题) JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完毕,通过以下4步来完毕JVM环境. ...

  6. spring容器初始化执行某个方法

    在做web项目开发中,尤其是企业级应用开发的时候,往往会在工程启动的时候做许多的前置检查. 比如检查是否使用了我们组禁止使用的Mysql的group_concat函数,如果使用了项目就不能启动,并指出 ...

  7. 当spring 容器初始化完成后执行某个方法

    在做web项目开发中,尤其是企业级应用开发的时候,往往会在工程启动的时候做许多的前置检查. 比如检查是否使用了我们组禁止使用的Mysql的group_concat函数,如果使用了项目就不能启动,并指出 ...

  8. 当spring 容器初始化完成后执行某个方法 防止onApplicationEvent方法被执行两次

    在做web项目开发中,尤其是企业级应用开发的时候,往往会在工程启动的时候做许多的前置检查. 比如检查是否使用了我们组禁止使用的Mysql的group_concat函数,如果使用了项目就不能启动,并指出 ...

  9. spring容器初始化bean和销毁bean之前进行一些操作的定义方法

    关于在spring  容器初始化 bean 和销毁前所做的操作定义方式有三种:        第一种,通过在xml中定义init-method和destory-method方法        第二种, ...

随机推荐

  1. unittest学习

    unittest的四大特点 TestCase:测试用例.所有的用例都是直接继承与UnitTest.TestCase类. TestFixture:测试固件.setUp和tearDown分别作为前置条件和 ...

  2. 助力全球抗疫:3D突发公共卫生事件管理平台

    前言 秋冬降临,北半球气温转凉.欧洲多个国家单日新增病例持续创新高,美国更是成为全球疫情最严重的国家.国内山东青岛.新疆喀什等地也相继发现多例病情.全球第二波疫情已经开始,国内疫情牵动人心,全球抗疫仍 ...

  3. 从原生web组件到框架组件源码(三)

    快乐的时光都是这么短暂,转眼间,web原生组件的知识点已经学完了,这个虽然暂时不一定有用,但是随着时间的积累,一步一个脚印的积累,你会有相应的收获,希望能变得更强,比如两年前我也会想有现成的东西不用, ...

  4. 【轻松学编程】如何快速学会一门高级编程语言,以python为例

    python文章目录 关注公众号"轻松学编程"了解更多. 写在前面:如何快速(比如在一个月内)学会一门高级编程语言? 现在想学一门编程语言并不难,网上有很多资料,包括书籍.博客.视 ...

  5. [Luogu P1829] [国家集训队]Crash的数字表格 / JZPTAB (莫比乌斯反演)

    题面 传送门:洛咕 Solution 调到自闭,我好菜啊 为了方便讨论,以下式子\(m>=n\) 为了方便书写,以下式子中的除号均为向下取整 我们来颓柿子吧qwq 显然,题目让我们求: \(\l ...

  6. JS中使用for-each遍历数组

    1 let array = [1, 3, 6, 8, 9, 0, 5]; 2 /* 3 index是数组索引 4 value代表数组的值 5 arr是指整个数组 6 */ 7 array.forEac ...

  7. Spring Boot API 统一返回格式封装

    今天给大家带来的是Spring Boot API 统一返回格式封装,我们在做项目的时候API 接口返回是需要统一格式的,只有这样前端的同学才可对接口返回的数据做统一处理,也可以使前后端分离 模式的开发 ...

  8. Python调用Java(基于Ubuntu 18.04)

    最近实习,需要使用Python编程,其中牵涉到一些算法的编写.由于不熟悉Python,又懒得从头学,而且要写的算法自己之前又用Java实现过,就想着能不能用Python调用Java.经过查找资料,方法 ...

  9. LDA与PCA

    参考: https://www.cnblogs.com/pinard/p/6244265.html https://blog.csdn.net/qq_25680531/article/details/ ...

  10. vim编辑器的常用命令

    按ESC键跳到命令模式,然后::w - 保存文件,不退出 vim.:w file -将修改另外保存到 file 中,不退出 vim.:w! -强制保存,不退出 vim .:wq -保存文件,退出 vi ...