面试题:Spring

编程入门 行业动态 更新时间:2024-10-07 10:15:41

<a href=https://www.elefans.com/category/jswz/34/1769418.html style=面试题:Spring"/>

面试题:Spring

关于Spring的点:IoC、DI、AOP、Bean(作用域、生命周期)、Spring中的设计模式、事务(隔离级别、传播行为)

一、IoC
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转,哪些方面反转了”:
●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

二、DI
DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
 ●谁依赖于谁:当然是应用程序依赖于IoC容器;
 ●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
 ●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
 ●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。
DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

三、AOP
Spring AOP原理:Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接⼝,那么Spring AOP会使⽤JDK Proxy,去创建代理对象,⽽对于没有实现接⼝的对象,就⽆法使⽤ JDK Proxy 去进⾏代理了,这时候Spring AOP会使⽤Cglib ,这时候Spring AOP会使⽤ Cglib ⽣成⼀个被代理对象的⼦类来作为代理。

JDK Proxy底层原理:
(1)实现InvocationHandler接口
(2)创建代理类(通过java API)
Proxy.newProxyInstance(动态加载代理类,代理类实现接口,使用handler);
(3)调用invoke方法(虚拟机自动调用方法)

Cglib 底层原理:
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

Spring AOP应用:日志记录,性能统计,安全控制,事务处理,异常处理

四、Bean
1、Bean的作用域
singleton : 唯⼀ bean 实例,Spring 中的 bean 默认都是单例的。
prototype : 每次请求都会创建⼀个新的 bean 实例。
request : 每⼀次HTTP请求都会产⽣⼀个新的bean,该bean仅在当前HTTP request内有效。
session : 每⼀次HTTP请求都会产⽣⼀个新的 bean,该bean仅在当前 HTTP session 内有效。

2、Bean的生命周期

  1. Bean 容器找到配置⽂件中 Spring Bean 的定义。
  2. Bean 容器利⽤ Java Reflection API 创建⼀个Bean的实例。
  3. 如果涉及到⼀些属性值 利⽤ set() ⽅法设置⼀些属性值。
  4. 如果 Bean 实现了BeanNameAware 接⼝,调⽤ setBeanName() ⽅法,传⼊Bean的名字。
  5. 如果 Bean 实现了BeanClassLoaderAware 接⼝,调⽤setBeanClassLoader() ⽅法, 传⼊ ClassLoader对象的实例。
  6. 与上⾯的类似,如果实现了其他 *.Aware 接⼝,就调⽤相应的⽅法。
  7. 如果有和加载这个 Bean 的 Spring容器相关的 BeanPostProcessor 对象,执 ⾏ postProcessBeforeInitialization() ⽅法。
  8. 如果Bean实现了 InitializingBean 接⼝,执⾏ afterPropertiesSet() ⽅法。
  9. 如果 Bean在配置⽂件中的定义包含 init-method 属性,执⾏指定的⽅法。
  10. 如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor 对象,执⾏ postProcessAfterInitialization() ⽅法
  11. 当要销毁Bean 的时候,如果 Bean 实现了 DisposableBean 接⼝,执⾏ destroy() ⽅法。
  12. 当要销毁Bean 的时候,如果 Bean 在配置⽂件中的定义包含 destroy-method 属性,执⾏指定的⽅法。

五、Spring中的设计模式
(1)单例模式(Singleton)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory。但没有从构造器级别去控制单例,这是因为spring管理的是是任意的java对象。
核心提示点:Spring下默认的bean均为singleton,可以通过singleton=“true|false” 或者 scope=“?”来指定
(2)代理(Proxy)
为其他对象提供一种代理以控制对这个对象的访问。 从结构上来看和Decorator模式类似,但Proxy是控制,更像是一种对功能的限制,而Decorator是增加职责。
spring的Proxy模式在aop中有体现,比如JdkDynamicAopProxy和Cglib2AopProxy。
(3)工厂方法(Factory Method)
通常由应用程序直接使用new创建新的对象,为了将对象的创建和使用相分离,采用工厂模式,即应用程序将对象的创建及初始化职责交给工厂对象。
一般情况下,应用程序有自己的工厂对象来创建bean.如果将应用程序自己的工厂对象交给Spring管理,那么Spring管理的就不是普通的bean,而是工厂Bean。
(4)适配器(Adapter)
在Spring的Aop中,使用的Advice(通知)来增强被代理类的功能。Spring实现这一AOP功能的原理就使用代理模式(1、JDK动态代理。2、CGLib字节码生成技术代理。)对类进行方法级别的切面增强,即,生成被代理类的代理类, 并在代理类的方法前,设置拦截器,通过执行拦截器重的内容增强了代理方法的功能,实现的面向切面编程。
(5)策略(Strategy)
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
spring中在实例化对象的时候用到Strategy模式。
(6)观察者(Observer)
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
spring中Observer模式常用的地方是listener的实现。如ApplicationListener。

六、事务
1、隔离级别
TransactionDefinition 接⼝中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT: 使⽤后端数据库默认的隔离级别,Mysql 默认采⽤的REPEATABLE_READ隔离级别 Oracle 默认采⽤的 READ_COMMITTED隔离级别.
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • TransactionDefinition.ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻⽌脏读,但是幻读或不可重复读仍有可能发⽣
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:对同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改,可以阻⽌脏读和不可重复读,但幻读仍有可能发⽣。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:最⾼的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰,也就是说,该级别可以防⽌脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会⽤到该级别。

2、传播行为
⽀持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRED:
    如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。
  • TransactionDefinition.PROPAGATION_SUPPORTS:
    如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
  • TransactionDefinition.PROPAGATION_MANDATORY:
    如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

不⽀持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:
    创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
    以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER: 以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。

其他情况:

  • TransactionDefinition.PROPAGATION_NESTED:
    如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

3、循环依赖
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。
Spring的循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的field或则属性是可以延后设置的(但是构造器必须是在获取引用之前)。

那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。
首先我们看源码,三级缓存主要指:

这三级缓存分别指:
singletonFactories : 单例对象工厂的cache
earlySingletonObjects :提前暴光的单例对象的Cache
singletonObjects:单例对象的cache

参考链接:
/

更多推荐

面试题:Spring

本文发布于:2024-02-14 10:41:09,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1763021.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:面试题   Spring

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!