博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring高频面试题
阅读量:2455 次
发布时间:2019-05-10

本文共 4510 字,大约阅读时间需要 15 分钟。

1. spring ioc的启动过程:

spring IOC 容器的启动步骤可总结如下:
1 初始化 ApplicationContext
环境属性的初始化和验证,启动时间记录和相关标记设置,应用事件和监听者的初始化。
2 准备好容器中的 BeanDefinition (eager-initializing beans)
对 BeanDefinition 的解析、扫描和注册,BeanDefinition 的扫描和注册大致可以分为 XML 和注解两种,两种方式各自使用的组件有所不同,该步骤的时间也可以在最前面。
3 初始化 BeanFactory
准备好 BeanFactory 以供 ApplicationContext 进行使用,对接下来将要使用到的 Bean 进行实例化,资源进行准备,属性进行设置。
4 注册 BeanPostProcessors
BeanPostProcessors 是进行扩展的关键组件,需要在该步骤中进行注册,可分为两种类型: 一种是框架使用者提供的,用于特定业务功能的,另一种是框架开发者提供的,用于扩展框架功能。
5 调用 BeanDefinitionRegistryPostProcessor
BeanDefinitionRegistryPostProcessor 是一种功能增强,可以在这个步骤添加新的 BeanDefinition 到 BeanFactory 中。
6 调用 BeanFactoryPostProcessor
BeanFactoryPostProcessor 是一种功能增强,可以在这个步骤对已经完成初始化的 BeanFactory 进行属性覆盖,或是修改已经注册到 BeanFactory 的 BeanDefinition。
7 初始化 MessageSource 和 ApplicationEventMulticaster
MessageSource 用于处理国际化资源,ApplicationEventMulticaster 是应用事件广播器,用于分发应用事件给监听者。
8 初始化其他 Bean 和进行其他的的上下文初始化
主要用于扩展
9 注册 ApplicationListene
将 ApplicationListene 注册到 BeanFactory 中,以便后续的事件分发
10 实例化剩余的 Bean 单例
步骤 4 到 9 都对一些特殊的 Bean 进行了实例化,这里需要对所有剩余的单例 Bean 进行实例化
11 启动完成
资源回收,分发"刷新完成"事件

2. spring获取和实例化bean的过程:

Spring是通过递归的方式获取目标bean及其所依赖的bean的;
Spring实例化一个bean的时候,是分两步进行的,首先实例化目标bean,然后为其注入属性。
结合这两点,也就是说,Spring在实例化一个bean的时候,是首先递归的实例化其所依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归的将获取到的bean设置为各个上层bean的属性的。

3. bean实例化的过程

createBeanInstance 调用对象的构造方法 实例化对象
populateBean 填充属性 为多bean的依赖属性进行填充
initializeBean 调用spring的init方法 初始化
即 创建对象(分配空间)-> 属性填充(设置对象属性)-> 初始化
分析可知 对象的循环依赖主要会发生在第一步构造器循环依赖和setter循环依赖
构造器循环依赖问题:
this .singletonsCurrentlylnCreation.add(beanName)将当前正要创建的bean 记录在缓存中
Spring 容器将每一个正在创建的bean 标识符放在一个“当前创建bean 池”中, bean 标识
柏:在创建过程中将一直保持在这个池中,因此如果在创建bean 过程中发现自己已经在“当前
创建bean 池” 里时,将抛出BeanCurrentlylnCreationException 异常表示循环依赖;而对于创建完毕的bean 将从“ 当前创建bean 池”中清除掉。
setter循环依赖
解决方案: 三级缓存
singletonFactories : 进入实例化阶段的单例对象工厂的cache (三级缓存)
earlySingletonObjects :完成实例化但是尚未初始化的,提前暴光的单例对象的Cache (二级缓存)
singletonObjects:完成初始化的单例对象的cache(一级缓存)

一级缓存:用于存放完全初始化好的 bean **/private final Map
singletonObjects = new ConcurrentHashMap
(256);/** 二级缓存:存放原始的 bean 对象(尚未填充属性),用于解决循环依赖 */private final Map
earlySingletonObjects = new HashMap
(16);/** 三级级缓存:存放 bean 工厂对象,用于解决循环依赖 */private final Map
> singletonFactories = new HashMap
>(16);/**bean 的获取过程:先从一级获取,失败再从二级、三级里面获取创建中状态:是指对象已经 new 出来了但是所有的属性均为 null 等待被 init*/

检测循环依赖的过程如下:

A 创建过程中需要 B,于是 A 将自己放到三级缓里面 ,去实例化 B

B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了!
然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A
B 顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)
然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到 B ,然后完成创建,并将自己放到一级缓存里面

4. spring如何实现单例模式

使用单例注册表hashmap结构,若定义为单例模式 则检查注册表 根据beanName 检查为null时才创建bean 然后向注册表注册。

5. bean的创建过程

  1. 转换beanName

    1.1 处理FactoryBean,比如beanName 为 &user,此时会将& 截取掉
    1.2 根据beanName查找真正的Name。比如我们定义了一个bean,id=“user”,name=“student”,alias=“teacher”。那么我们在getBean(name)时,name 传user,student,teacher 任何一个都可以得到我们定义的bean,切转换后的beanName是 user。

  2. 尝试从缓存或者提前暴露的BeanFactory中获取实例

  3. bean的实例化(存在FactoryBean的情况)

    3.1 当前bean非FactoryBean,直接返回
    3.2 当前bean为FactoryBean,则调用被覆盖的getObject()方法获取实例。

  4. 原型类型循环引用的检查(多例模式下的循环引用检查)

  5. 尝试从父类工厂中实例化bean

    5.1 parentBeanFactory 不为空,且parentBeanFactory 包含要查找bean的定义。即通过beanName,能找到BeanDefinition
    5.2 满足条件1,则尝试从parentBeanFactory中加载bean

  6. 转换RootBeanDefinition,并合并父类属性

    6.1 从缓存(容器初始化时解析配置文件得到并存入缓存)中获取GenericBeanDefinition,并将其转换为RootBeanDefinition。spring源码系列-容器之XmlBeanFactory
    6.2 如果parent不为空,则需要将parent的属性也合并进来。

  7. 查找并实例化依赖-depend on

    7.1 查找依赖-dependOn(这里的依赖,指的不是我们常说的依赖注入属性。而是当前bean可能用到特定的bean,而这个特定的bean 要放在当前bean之前加载。)
    7.2 初始化依赖-dependOn

  8. 根据不同的scope做bean的创建

    8.1. 尝试从缓存中加载
    8.2. 如果缓存不存在,则将bean标记为正在创建
    8.3. 调用ObjectFactory.getObject()方法创建bean
    8.4. 移除正在创建状态
    8.5. 删除各种创建时状态,并添加缓存

8.3.1ObjectFactory.getObject()大致包含如下流程:

a1. 处理MethodOverrides
b2. 实例化的前置处理
c3. 根据不同的策略实例化bean,并包装成beanWapper
d4. 提前暴露beanFactory
e5. 属性填充
f6. 调用初始化方法,即各种处理器
g7. 注册销毁相关方法

  1. 类型转换
    到这里bean的创建基本上就结束来,但是有时候会存在,得到的类型和我们要求的类型requireType不一致的情况,这个时候就需要进行类型的转换。
    6. aop的实现方式
    Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

7. spring 实现aop

如果目标类实现了InvocationHandler接口,直接使用jdk动态代理。否则使用CGLIB

主要执行时的invoke步骤:

  1. .以下的几个判断,主要是为了判断method是否为equals、hashCode等Object的方法
  2. 获取当前bean被拦截方法链表chain
  3. 如果为空,则直接调用target的method
  4. 不为空,则逐一调用chain中的每一个拦截方法的proceed,这里的一系列执行的原因以及proceed执行的内容,我 在这里就不详细讲了,大家感兴趣可以自己去研读哈

8. spring aop 应用场景

权限、缓存、日志。

转载地址:http://menhb.baihongyu.com/

你可能感兴趣的文章
Java中的LinkedHashSet
查看>>
fgets 和gets_C编程中的fgets()和gets()
查看>>
angularjs绑定属性_AngularJS隔离范围绑定表达式教程
查看>>
什么是SQL Server DATEPART()方法?
查看>>
scala代码示例_Scala注释示例
查看>>
scala 正则表达式_Scala正则表达式示例
查看>>
java 字符串面试_Java字符串面试问答
查看>>
java访问修饰符_Java访问修饰符
查看>>
AngularJS指令通信教程
查看>>
struts2 拦截器_Struts2令牌拦截器示例
查看>>
大数据简介视频下载_大数据简介
查看>>
java继承的范例_Java范例中的继承
查看>>
pytorch入门_PyTorch入门
查看>>
mapreduce代码示例_MapReduce算法示例
查看>>
Spring4安全
查看>>
jsp servlet示例_Java Servlet Cookies示例
查看>>
java 生成器 设计模式_Java中的生成器设计模式
查看>>
Java BlockingQueue示例
查看>>
在Linux中什么是.bashrc文件?
查看>>
linux vmstat_Linux中vmstat命令指南
查看>>