Spring面试题

概述

ioc 控制反转

控制反转,原本对象由使用者来进行控制,现在把整个对象交给spring来进行管理。

容器:存储对象,使用map结构进行存储,放置在一级缓存中,整个Bean的生命周期都是交由容器来管理

DI 依赖注入

依赖注入 某个类声明了另一个类的属性,称为依赖关系,Spring通过配置,使得声明的属性被赋值。使用spring框架为类所需要依赖的属性进行赋值的过程,称为依赖注入。

AOP

面相切面编程,将那些与业务无关,却为业务模块所共同调用的逻辑或者责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,有利于未来的可操作性和可维护性。

Spring AOP 名词

切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。

连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

通知(Advice):在AOP术语中,切面的工作被称为通知。

切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。

引入(Introduction):引入允许我们向现有类添加新方法或属性。

目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。

织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入:

编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。

类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。

运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。

spring AOP支持如下两种实现方式
  • JDK动态代理:这是Java提供的动态代理技术,可以在运行时创建接口的代理实例。Spring AOP默认采用这种方式,在接口的代理实例中织入代码。
  • CGLib动态代理:采用底层的字节码技术,在运行时创建子类代理的实例。当目标对象不存在接口时,Spring AOP就会采用这种方式,在子类实例中织入代码。
spring框架中使用了那些设计模式及应用场景
  1. 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例。
  2. 单例模式:Bean默认是单例模式
  3. 代理模式:aop功能用到了jdk的动态代理和cglib字节码生成技术
  4. 模板方法:用来解决代码重复的问题,RestTemplate,JMSTemplate
  5. 观察者模式:定义对象键一对多的依赖关系,当一个对象的状态发生改变,所有依赖于它的对象都会得到通知被动更新。spring Listener的实现 ApplicationListener

原理

spring bean的生命周期

BeanFactor实例化一个Bean,对该Bean进行属性赋值,然后判断该Bean是否实现了Aware接口,

实现了BeanNameAware接口,就调用实现类的setBeanName(String)方法,传递Spring配置文件中的id值。

如果实现了BeanFactoryAware接口,调用实现类的setBeanFactory(BeanFactory),传递Spring BeanFactory自身(对象中包含其他对象的引用时,会实现)

如果实现了ApplicationContextAware接口,会调用实现类的setApplicationContext方法,传入Spring的上下文(对象中有使用到Application.yml这些配置文件的值,或者本身是 @Component 组件)

判断完这些实现接口后,会进行Bean的初始化,

在初始化之前会看这个Bean是否关联了BeanPostProcessor接口,如果关联了,会调用postProcessBeforeInitialization方法。

然后进行Bean的初始化,

初始化后调用postProcessAfterInitialization方法。

此时,Bean就创建完成了,可以进行使用了(Bean是单例的Singleton)

当Bean不在被需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法。

如果Bean在spring配置中配置了destroy-method属性,会自动调用配置的销毁方法。

什么是循环依赖,怎么解决

概念

循环依赖是指两个对象A B,A对象引用了B,B对象引用了A

产生原因具体描述

在创建A对象,进行属性赋值时,如果发现BeanFactory中不存在B对象实例,就会去创建B对象,创建B对象进行赋值时,发现BeanFactory中不存在A对象实例,又需要去创建A对象。

解决

Spring目前是通过三级缓存和提前暴露解决的

在实例化A对象,将A提前暴露,以Bean工厂的形式存储到三级缓存。 进行属性赋值时,先去缓存中查,一二三级缓存中不存在B对象实例,

实例化B,将B提前暴露,以Bean工厂的形式存储到三级缓存。

在对B进行赋值时,会发现需要A对象,会去一二三级缓存中找,一二级缓存没发现A对象,但在三级缓存中发现了提前暴露的A对象,从三级缓存中获取到半成品A对象,将该对象放进二级缓存中,并删除三级缓存中的A对象。

将对象A注入到对象B中,对象B完成了属性填充,执行初始化方法,放到一级缓存中。删除三级缓存中的对象B。

对象A得到对象B,进行注入,A完成属性填充和初始化,放到一级缓存中,并删除二级缓存中的A对象。

二级缓存能不能解决循环依赖? 一级缓存呢

不能,一级缓存和二级缓存存储的是不同类型的对象,如果只有一级缓存的话,那么成品对象和半成品对象,而半成品状态的对象是不能直接暴露给其他对象做引用的,所以此时无法进行判断。

二级缓存可以解决某些情况下的循环依赖问题,但是有限制条件,不能出现代理对象

三级缓存各自存放内容

一级缓存放成品对象

二级缓存放半成品对象

三级缓存放Bean工厂

三个缓存对象的查找顺序

先找一级,找不到找二级,再找不到找三级缓存

spring框架中的单例Bean是线程安全的吗

不是,大部分时候spring bean是无状态的,某种程度上说bean是安全的,但如果bean有状态的话,需要我们自己去保证bean的作用域。

有状态:有数据存储功能

无状态:不会保存数据

BeanFactory和ApplicationContext的区别
  • BeanFactory:是基础类型的IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延 迟初始化策略。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的IoC容器选择。
  • ApplicationContext:它是在BeanFactory的基础上构建的,是相对比较高级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等。ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于BeanFactory来说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容 器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场景中,ApplicationContext类型的容器是比较合适的选择。

ApplicationContext是BeanFactory的子接口

BeanFactory是spring最底层的接口,能读取Bean配置文档,管理Bean的生命周期,维护Bean之间的依赖关系

ApplicationContext作为BeanFactory的子类,除了具有BeanFactory的功能外,还实现了一些其他的功能

​ 继承了MessageSource,支持国际化

​ 统一资源文件访问方式。

​ 提供在监听器中注册Bean的实践。

​ 同时加载多个配置文件。

​ 载入了多个上下文。

BeanFactory采用的是延迟加载来注入Bean的,只有在Bean被调用时,才会对Bean进行初始化。

ApplicationContext是在容器启动时,一次性创建所有Bean

BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建

BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPOSTProcessor的使用,但BeanFactory需要手动注册,ApplicationContext是自动注册

spring支持的bean作用域

五种作用域:

  1. singleton:单例,每个spring ioc容器中只有一个实例
  2. prototype:一个bean的定义可以有多个实例
  3. request:每次http请求都会创建一个bean
  4. session:每一个http session中,一个bean只有一个实例
  5. global-session:每一个全局的session中,一个bean对应一个实例。
Bean的自动装配

在Spring框架xml配置中共有5种自动装配:

no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。

byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。

byType:通过参数的数据类型进行自动装配。

constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。

autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用byType的方式自动装配

使用@Autowired注解自动装配的过程是怎样的?

使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-confifig />。

在启动spring IOC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IOC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;

如果查询的结果不止一个,那么@Autowired会根据名称来查找;

如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

事务

spring的事务传播机制
  1. required:如果当前没有事务,就创建一个事务,如果当前存在事务,就加入事务。
  2. supports:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
  3. mandatory:支持当前事务,如果当前存在事务,就加入该事务;如果当前不存在事务就抛出异常。
  4. requires_new:创建新事务,无论当前存不存在事务,都创建新事务。
  5. not_suppported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  6. never:以非事务方式执行,如果当前存在事务,则抛出异常。
  7. nested:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则按required属性执行。
spring事务的实现方式原理是什么
  1. spring事务底层是基于数据库事务和aop机制的

  2. 首先对于使用了@Transactional注解的Bean,Spring会创建一个代理对象作为Bean

  3. 当调用代理对象的方法时,会先判断该方法是否加了@Transactional注解

  4. 如果加了,则用事务管理器创建一个数据库连接 ,并关闭该连接的自动提交。

  5. 开始执行方法中的sql

  6. 如果出现了异常,且是@Transactional注解相匹配的异常, 则调用连接对象进行回滚。

  7. 没出现异常,提交事务

  8. 清除相关的事务信息

spring事务的隔离级别

spring事务有五种隔离级别,默认使用数据库的设置,其他四种隔离级别与数据库一致。

  1. default:默认,使用数据库的隔离级别
  2. read_uncommitted:未提交读,最低的隔离级别,事务未提交前,就可以被其他事务读取。会出现幻读、脏读、不可重复读。
  3. read_committed:提交读,一个事务提交后才能被其他事务读取到。会造成幻读,不可重复读。
  4. repeatable_read:可重复读,保证多次读取同一数据时,返回第一次读取时的快照,其值都和事务开始时候的内容一致,禁止读取到别的事务未提交的数据。会造成幻读。
  5. serizlizable:序列化,隔离级别最高,能防止脏读,不可重复读,幻读。

脏读:表示一个事务能读取另一个事务还未提交的数据。

不可重复读:是指在一个事务内,多次读同一数据,却得到不同的结果。

幻读:指同一个事务内多次查询返回的结果集不一样,

spring事务什么时候会失效
  1. 访问权限不是public
  2. 方法使用final修饰
  3. 未被spring管理
  4. 错误的传播特性
  5. 捕获了异常
  6. 异常不匹配
  7. 方法内部调用

注解

@Autowired和@Resource之间的区别

@Autowired和@Resource可用于:构造函数、成员变量、Setter方法

@Autowired和@Resource之间的区别在于

@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值