1、谈谈你对SpringIOC的理解
IOC,也叫控制反转,是Spring用来解耦的一种设计思想,它的做法就是将对象的控制权由程序员手中反转到Spring手中。具体来说呢就是,在没有IOC之前,对象都是程序员在类中主动去创建,需要哪个创建哪个;有了IOC之后,对象会交给Spring容器创建和管理,如果哪个对象中需要其它对象属性,Spring也会自动完成依赖注入。
总之一句话,IOC可以将对象的创建和对象之间依赖关系的维护交给Spring自动完成。
2、Spring中有哪些依赖注入方式
依赖注入指的是Spring给对象中属性进行赋值的过程,主要包括两种方式:
-
构造器依赖注入:构造器注入是指容器调用一个类的构造器创建对象时,直接传入给属性赋值
-
Setter方法注入:Setter方法注入是指容器在创建对象完成后,通过调用属性的Setter 方法,可以属性赋值
3、你用过哪些Spring注解
我们常用的Spring注解主要分类下面几大类:
1、创建对象:@Component、@Controller、@Service、@Repository
它们都可以标注在自己开发的类上,Spring会使用注解标注的类创建出对象,然后放入容器
2、依赖注入:@Autowired
标注在属性或者属性对应的set方法上,Spring会根据被标注属性的类型自动对属性进行赋值
3、依赖注入:@Qualifier
和@Autowired一块使用,在同一类型的bean有多个的情况下Spring会根据name进行选择注入
4、配置类:@Configuration、@Bean
主要标注在配置类中,用于声明配置类和向Spring容器中放入一些配置有关的对象
5、当然还有一些平时用的不是特别多的
比如:声明注解扫描的@ComponentScan,声明Bean的作用域的@Scope,用于切面编程的@Around,@Pointcut等等
4、SpringBean的作用域有几种
在Spring中作用域是用来对象的存活范围的,它支持5种作用域
-
第一种是单例,配置为单例的对象会跟随Spring容器创建而创建,跟随Spring容器销毁而销毁,在Spring容器中无论获取多少次单例对象,得到的都是同一个,这也是Spring中的对象的默认作用域
-
第二种是多例,配置为多例的对象在每次获取的时候才会创建,而且每次获取到的都不一样
-
还有三种分别是request、session和application,目前已经基本不再使用
其实,在我们平时的开发过程中,对象基本上都是配为单例的,这样可以有效的节省资源,只有单例对象存在线程安全问题时,才考虑调整为多例。
5、Spring中的bean线程安全吗
Spring中的Bean主要分为单例和多例
- 多例对象每次获取都会创建新实例,也就是说线程之间不存在Bean共享问题,也就不存在线程安全问题
- 单例对象是所有线程共享一个实例,因此就可能会存在线程安全问题。但是单例对象又分为无状态和有状态。
- 无状态Bean是指只对对象的成员变量进行查询操作,不会修改成员变量的值,因此不存在线程安全问题
- 有状态Bean需要对Bean中的成员变量进行数据更新操作,因此就可能存在线程安全问题
所以,最终我们得出结论,在Spring中,只有有状态的单例Bean才会存在线程安全问题
处理有状态单例Bean的线程安全问题有以下两种方法:
- 将Bean的作用域由单例改为多例
- 将需要的可变成员变量保存在ThreadLocal中, ThreadLocal本身就具备线程隔离的特性,这就相当于为每个线程提供了一个独立的变量副本,每个线程只需要操作自己的线程副本变量,从而解决线程安全问题。
6、谈谈你对SpringAOP的理解
AOP,又叫面向切面编程,核心思想是将那些与业务无关,却为业务模块所共同调用的逻辑(例如事务处理、日志管理)封装起来,然后再动态插入到业务中的功能
使用AOP可以减少系统的重复代码,降低模块间的耦合度,并有利于扩展和维护,Spring AOP是基于动态代理的,它底层同时支持JDK和CGLIB的代理方式,并且会根据被代理类是否有接口自动选择最合适的代理方式
我们在开发中用到AOP的主要使用场景有:事务管理、日志、性能监视、安全检查
7、AOP的代理有几种方式
AOP思想的实现一般都是基于代理模式,在Java中一般采用JDK动态代理模式和CGLIB动态代理模式
- JDK动态代理模式只能对有接口的类进行代理,而且效率较高
- CGLIB可以对任意的类进行动态代理,但是效率上不如JDK
因此在进行代理时,如果被代理类有接口,就用JDK;如果没有接口,就用CGLIB
使用Spring的AOP,底层会自动按照这个规则进行选择,开发者也无需关心
8、Spring的通知类型有哪些
通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过SpringAOP框架触发的代码段。
Spring切面可以应用五种类型的通知:
- 前置通知:在某切点之前执行的通知
- 返回后通知:在某切点正常完成后执行的通知
- 抛出异常后通知:在某切点抛出异常退出时执行的通知
- 后置通知:在某切点退出的时候执行的通知(不论是正常返回还是异常退出)
- 环绕通知:包围一个切点的通知
9、了解Spring的事务管理吗
嗯,了解的,Spring支持编程式事务和声明式事务
-
第一种是编程式事务指的是在代码中使用try-catch捕获异常,然后配合事务的api来手动处理事务问题
这种方式的缺点是代码耦合,复用性低,优点是可以精确控制要增强的代码(不仅仅限于方法粒度)
-
第二种是声明式事务,声明式事务是AOP思想的一种应用,它的核心思想是将业务方法作为切点,
将事务处理方法作为增强,通过动态代理实现事务的管理,它的优点是降低代码耦合,提供复用
目前在企业中基本上都是采用声明式事务的。
10、Spring事务传播行为有几种
事务传播行为是为了解决业务层方法之间互相调用的事务问题。
当事务方法被另一事务方法调用时,必须指定事务应该如何传播。
例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
Spring支持7个种事务传播行为的:
-
必须事务:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
-
必须新事务:创建一个新的事务,如果当前存在事务,则把当前事务挂起
-
强制事务:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
-
支持事务:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
-
不支持事务:以非事务方式运行,如果当前存在事务,则把当前事务挂起
-
强制无事务:以非事务方式运行,如果当前存在事务,则抛出异常
-
嵌套事务:如果当前存在事务,则创建一个当前事务的嵌套事务来运行;如果当前没有事务,则创建一个事务
嵌套事务是已存在事务的一个子事务,嵌套事务开始执行时,将取得一个保存点,
如果这个嵌套事务失败,将回滚到此保存点
嵌套事务是外部事务的一部分,只有外部事务结束后它才会被提交
11、Spring中的事务是如何实现的
Spring事务底层是基于数据库事务和AOP机制的
首先Spring会为使用了@Transactional注解的Bean代理对象,当调用代理对象的方法时,会先判断该方法上是否加了@Transactional注解。如果加了,那么则利用事务管理器创建一个数据库连接,并且禁止此连接的自动提交事务
然后执行当前方法,方法中会执行sql ,执行完当前方法后,如果没有出现异常就直接提交事务;如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务。
Spring事务的隔离级别对应的就是数据库的隔离级别 ,Spring事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为 需要新开一个事务,那么实际上就是重新建立一个数据库连接,在此新数据库连接上执⾏sql
12、Spring中的设计模式有哪些
- 工厂模式:Spring使用工厂模式通过 BeanFactory和 ApplicationContext创建 bean 对象
- 单例模式: Spring 中的 bean 默认都是单例的
- 代理模式:Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 字节码生成技术
- 模板方法:用来解决代码重复的问题。比如 RestTemplate、jdbcTemplate、 JpaTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式
- 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如 Spring 中 listener 的实现 ApplicationListener。
13、Spring是怎么解决循环依赖的(待整理)
整个IOC容器解决循环依赖,用到的几个重要成员:
singletonObjects:一级缓存,存放完全初始化好的 Bean 的集合,从这个集合中取出来的 Bean 可以立马返回 earlySingletonObjects:二级缓存,存放创建好但没有初始化属性的 Bean 的集合,它用来解决循环依赖 singletonFactories:三级缓存,存放单实例 Bean 工厂的集合 singletonsCurrentlyInCreation:存放正在被创建的 Bean 的集合
IOC 容器解决循环依赖的思路:
-
初始化 Bean 之前,将这个 BeanName 放入三级缓存
-
创建 Bean 将准备创建的 Bean 放入 singletonsCurrentlyInCreation (正在创建的 Bean)
-
createNewInstance 方法执行完后执行 addSingletonFactory,将这个实例化但没有属性赋值的 Bean 放入二级缓存,并从三级缓存中移除
-
属性赋值&自动注入时,引发关联创建
-
关联创建时,检查“正在被创建的 Bean”中是否有即将注入的 Bean。如果有,检查二级缓存中是否有当前创建好但没有赋值初始化的 Bean。如果没有,检查三级缓存中是否有!正在创建中的 Bean。至此一般会有,将这个 Bean 放入二级缓存,并从三级缓存中移除
-
之后 Bean 被成功注入,最后执行 addSingleton,将这个完全创建好的 Bean 放入一级缓存,从二级缓存和三级缓存移除,并记录已经创建了的单实例 Bean
14、SpringBean的生命周期
- Bean 容器找到配置文件中 Spring Bean 的定义。
- Bean 容器利用 Java Reflection API 创建一个 Bean 的实例。
- 如果涉及到一些属性值,利用 set()方法设置一些属性值。
- 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入 Bean 的名字。
- 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader 对象的实例。
- 如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanClassFacotory()方法,传入 ClassLoader 对象的实例。
- 与上面的类似,如果实现了其他*Aware 接口,就调用相应的方法。
- 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行 postProcessBeforeInitialization()方法。
- 如果 Bean 实现了 InitializingBean 接口,执行 afeterPropertiesSet()方法。
- 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
- 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcess 对象,执行 postProcessAfterInitialization()方法。
- 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy()方法。
- 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。
总结以上步骤,核心主干主要就是五部分构成:
- 构造 Bean 对象
- 设置 Bean 属性
- 初始化回调
- Bean 调用
- 销毁 Bean
15、SpringMVC执行流程
MVC 是 Model — View — Controler 的简称,它是一种架构模式,它分离了表现与交互。它被分为三个核心部件:模型、视图、控制器。
具体流程如下所示:
1、用户发送出请求到前端控制器DispatcherServlet。
2、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
3、HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
4、DispatcherServlet调用HandlerAdapter(处理器适配器)。
5、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
6、Controller执行完成返回ModelAndView对象。
7、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
8、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
9、ViewReslover解析后返回具体View(视图)。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应用户。
16、SpringMVC的常用注解有哪些
我们常用的Springmvc注解主要分类下面几大类:
1、用于声明Bean到Springmvc容器:@Controller、@RestController
区别在于后者还可以将返回的集合或对象转换为JSON直接返回
2、设置请求路径:@RequestMapping、@GetMapping、@PostMapping 、@PutMapping、@DeleteMapping
第一个是通用的,可以接收各种类型的请求;后面四个只能直接对应类型的请求
3、接收请求参数:
@RequestBody: 接收请求体中的json数据
@PathViriable:接收请求路径中的参数
@RequestHeader:接收请求头中的参数
@RequestParam:一般用于给参数设置默认值或者完成请求参数和controller方法参数的映射
17、SpringMVC如何处理统一异常
SpringMVC的异常处理底层是通过AOP实现的,它的核心思想是将异常处理的代码和业务逻辑代码分离开来
使用它之后,我们在自己的业务代码中不需要在处理异常,有异常直接就上抛到框架中
框架就会将异常交给自定义的全局异常处理器中统一处理
自定义全局异常处理器,会用到两个注解:
- @RestControllerAdvice 标注在类上,声明被标注的类是一个用于专门处理异常的类
- @ExceptionHandler 标注在异常处理类中的方法上,声明被标注的方法可以处理哪些异常