(1条消息) Spring常见面试题总结(超详细回答)_张维鹏的博客-优快云博客_spring面试题
-
IOC控制反转:
控制是指将所有的类都托管给Spring创建和管理
反转是指通过依赖注入的方式,给对象的属性赋值,包括基本属性和引用属性
-
AOP面向切面编程:
本质是动态代理模式(类似于装饰者模式)
通过横切的方式在原有的业务代码之上添加公共功能,而不改变原有业务代码
扩展:代理模式和装饰者模式
代理模式:
代理模式包含代理对象,被代理对象
静态代理:
-
代理对象Proxy和被代理对象RealSubject都继承了Subject接口。客户端调用Proxy的方法,而Proxy则把具体操作委托给RealSubject执行。
-
在Client中,首先创建了一个realSubject对象,然后创建了一个代理对象proxy并把realSubject对象通过构造器传入进去。最后调用proxy的方法,实际还是调用realSubject的方法。
-
代理对象和被代理对象需要实现相同的接口
动态代理:
-
通过动态代理,创建一个实现了InvocationHandler接口的DynamicProxy类,通过这个类可以在运行期为各种对象创建对应的代理。
-
DynamicProxy类实现了InvocationHandler接口,这个接口中有一个invoke方法,被代理的对象的任何方法都是在invoke中调用。
-
在Client中,先创建一个realSubject,然后把它传入DynamicProxy的构造函数中。但是在这个DynamicProxy还不是我们需要的代理,毕竟它没有实现Subject接口。
在通过Proxy.newProxyInstance创建一个Subject对象,也就是最终的代理对象。
装饰者模式:
-
装饰者对被装饰者进行了功能的扩展,但是又不修改被装饰者的相应代码;
只为每个功能编写一个装饰类,在运行时组合不同的对象即可实现所需的功能组合。
-
代理模式和在装饰者模式都有很多的应用,具有一定的相似性,都是通过一个新的对象封装原有的对象。
二者的差异在于代理模式是为了实现对象的控制,可能被代理的对象难以直接获得或者是不想暴露给客户端;
而装饰者模式是继承的一种替代方案,在避免创建过多子类的情况下为被装饰者提供更多的功能。
1.Spring是什么?
Spring是一个轻量级的IOC和AOP容器框架,目的是为了简化企业级应用程序的开发,使得开发者只需要关注业务需求。主要包括以下七个模块:
-
Spring Core:核心类库,提供IOC和DI服务
-
Spring AOP
-
Spring WebMVC 提供面向web应用的MVC实现
-
Spring ORM 对象关系映射
-
Spring DAO 对JDBC抽象封装、
-
Spring Context 提供框架式的Bean访问方式,以及企业级功能
-
Spring Web 提供基本的面向Web的综合特性
2.Spring的优点?
- 属于低侵入式设计(业务的类不需要集成框架的类)
- IOC实现了对象由Spring创建和管理,对象之间的依赖关系交给Spring处理;对象之间的引用不采用在程序中硬编码,降低了组件的耦合性
- 提供了AOP技术,支持将一些通用任务(如日志、事务)进行集中式管理,从而提供更好的复用
- spring对于在主流的应用框架提供了集成支持(如mybatis等)
3.对IOC的理解?
-
IOC控制反转,控制指的是对象由Spring容器进行创建和管理,反转指的是对象之间的依赖关系也交给Spring处理,降低了对象之间的耦合性。
DI依赖注入和控制反转是同一个概念不同角度的描述,是指在运行时 通过IOC容器 动态注入对象所需要的外部依赖,给对象的属性赋值。
-
最直观的表达就是:以前创建和引用对象都是在程序中硬编码的;IOC让创建对象不用再去new了,对象由Spring创建和管理;
-
IOC的依赖注入方式:构造器注入(只能有参构造),set方法注入(需要有无参构造);c空间和p空间中注入;注解注入
4.AOP理解
OOP面向对象,适合纵向开发,但是不适合横向开发,会导致大量代码的重复,而不利于各个模块的复用。
AOP面向切面,将那些与业务无关的公共行为(日志、事务)抽取封装成一个可复用的模块,这个模块被命名为Aspect。减少系统中的重复代码,降低模块间的耦合度。
AOP的实现关键在于代理模式,代理模式分为静态代理和动态代理;
-
代理模式中有代理对象和被代理对象,都实现了一个抽象接口,在Client中创建被代理对象,将其传入到代理对象构造方法中。
-
静态代理称为编译时增强,在编译时生成代理类,并将切面织入到Java字节码中,运行时就是增强之后的AOP对象
-
SpringAOP使用的是动态代理。通过动态代理,创建一个实现了InvocationHandler接口的DynamicProxy类,通过这个类可以在运行期为各种对象创建对应的代理。
==DynamicProxy类实现了InvocationHandler接口,这个接口中有一个invoke方法,被代理的对象的任何方法都是在invoke中调用。==动态地将横切逻辑和业务编织在一起;
-
静态代理和动态代理区别在于生成代理对象的时机不同
IOC让相互协作的组件保持松散的耦合,AOP允许将公共通用功能剥离出来封装称为一个单独的可复用的模块。
5.AOP里面的几个名词的概念
-
切面(Aspect):被抽取出来的公共模块,切入类,可能会用来横切多个对象;Aspect切面由多个Pointcut切点和Advice通知组成;
-
切点(Pointcut):公共类中的方法
-
通知(Advice):指在连接点上执行的动作,增加的逻辑,Before(在连接点之前执行的通知)、After、Around;AfterReturning(连接点正常完成后通知)、AfterThrowing(连接点异常退出时执行的通知)
-
目标对象(Target):包含连接点的对象,被通知的对象;SpringAOP是通过动态代理实现的,所以这个对象永远是一个代理对象。
-
连接点(Joinpoint):目标对象中一个方法(在这个方法前后执行增强)
-
织入:通过动态代理,在目标对象的方法(即joinpoint)中执行增强逻辑(Advice)的过程。
【6.Spring容器的启动流程】
(1)初始化Spring容器,注册内置的BeanPostProcessor的BeanDefinition到容器中
(2)将配置类的BeanDefinition注册到容器中
(3)调用refresh()方法刷新容器
7.BeanFactory和ApplicationContext有什么区别?
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。
(1)BeanFactory是Spring里面最底层的接口,是IOC的核心,定义了IOC的基本功能,包括了各种Bean的定义、加载、实例化、依赖注入和生命周期管理。
ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能。
(2)BeanFactory采用延迟加载的形式注入Bean,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。
ApplicationContext是在容器启动时,一次性创建了所有的Bean。可以在容器启动时,发现Spring中存在的配置错误,这样有利于检查所依赖属性的是否注入。
(3)BeanFactory和ApplicationContext都支持BeanPostProcessor和BeanFactoryPostProcessor的使用;但是BeanFactory需要手动注册,ApplicationContext自动注册。
(4)BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
【9.SpringBean的生命周期?】
简单来说,SpringBean的生命周期只有四个阶段:实例化Instantiation== >属性赋值Populate== >初始化Initialization == >销毁Destruction
【10.Spring中bean的作用域】
(1)singleton:默认作用域,单例bean,每个容器中只有一个bean实例
(2)prototype:为每个bean请求创建一个实例
(3)request:为每个request请求创建一个实例,在请求完成后,bean会失效并被GC回收
(4)session:为每个session会话创建一个实例
(5)global-session:所有会话共享一个实例
11.Spring框架中Bean是线程安全的吗?如果线程不安全,那么如何处理?
Spring容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体情况需要结合Bean的作用域来看:
(1)对于prototype作用域的Bean,每次都创建一个新对象,因此线程之间不存在Bean的共享,所以不存在线程安全问题
(2)对于Singleton作用域的Bean,所有的线程共享一个单例实例的Bean,因此存在线程安全问题。但是如果单例Bean是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Controller类、Service类、Dao等,这些Bean大多是无状态的,只关注方法本身。
-
有状态的Bean:就是有实例变量的对象,可以保存数据,是非线程安全的。
-
无状态的Bean:就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。
对于有状态的Bean,需要自行保证线程安全,最简单的解决办法就是将Bean的作用域由singleton改为prototype。
- 采用ThreadLocal解决线程安全问题,为每个线程提供一个独立的变量副本,不同线程只操作自己线程的变量副本
- 线程同步,加锁阻塞
12.Spring注入bean的几种方式
- 构造器注入(有参构造注入)
- set方法注入(需要有无参构造方法)
- c空间注入,p空间注入
【13.Spring如何解决循环依赖问题?】
循环依赖问题在Spring中主要有三种情况:
(1)通过构造方法进行依赖注入时,产生的循环依赖问题
(2)通过setter方法进行依赖注入时,且在原型模式下,产生的循环依赖问题
(3)通过setter方法进行依赖注入时,且在单例模式下,产生的循环依赖问题
在Spring中,只有第(3)中方式的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。
- 第一种构造方法注入的情况下,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
- 第二种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。
Spring在单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过二级缓存和三级缓存来解决的,其中三级缓存是主要功臣。解决的核心原理就是:在对象实例化之后,依赖注入之前,Spring提前暴露的Bean实例的引用在第三级缓存中进行存储。
14.Spring的自动装配
在Spring中,使用autowire来配置自动装配。对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋给各个对象。