2.1.1 说一说对Spring中IOC的理解
IOC即“控制反转”,不是什么技术,而是一种设计思想。IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制
谁控制谁,控制什么:传统Java程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IOC是有专门一个容器来创建这些对象,即由IOC容器来控制对象的创建
为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转
简单来说IOC控制反转指的就是对象控制权力的转移 , 以前是由程序员自己创建对象自己管理对象 , 使用Spring之后由Spring帮助我们创建对象 , 管理对象, 对象的控制权就转移到Spring框架了 , 所以叫控制反转
2.1.2 Spring中有哪些依赖注入的方式
所谓依赖注入就是在 Spring IOC 容器创建对象的过程中,将所依赖的对象通过配置进行注入。我们可以通过依赖注入的方式来降低对象间的耦合度
依赖注入分为Setter方 法注入(Setter Injection)和构造器注入(Constructor Injection)和 注解注入三种方式
2.1.3 说一说对Spring中AOP的理解
AOP就是我们常说的面向切面编程。使用AOP可以将系统中的一些公共模块抽取出来,减少公共模块和业务代码的耦合。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各 部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
像Spring中的事物控制使用的就是AOP机制实现的 , 同时也可以使用AOP做一些权限控制 , 日志记录 , 接口统一缓存等操作
Spring中的AOP底层主要是通过动态代理机制实现的 , 主要使用的是JDK动态代理和CGLIB动态代理
2.1.4 Spring中AOP的原理了解过嘛
在 Spring 中 AOP 代理使用 JDK 动态代理和 CGLIB 代理来实现,默认如果目标对象是接口,则使用 JDK 动态代理,否则使用 CGLIB 来生成代理类
JDK 动态代理是基于接口的代理 , 产生的代理对象和目标对象实现了相同的接口 , 创建代理对象的方法
Object proxy = Proxy.newProxyInstance(类加载器,目标对象接口, 代理执行器InvocationHandler)
CGLIB动态代理是基于父类的代理 , 产生的代理对象是目标对象的子类 , 创建代理对象的方法
Object proxy = Enhancer.create(目标对象类型 , 代理回调callback)
2.1.5 如何实现一个AOP
实现AOP非常简单 , 主要有两种方式 :
第一种是通过XML配置的方式实现AOP
<aop:config>
<!-- 前置切入点 (在哪里插入)-->
<aop:pointcut expression="execution(public void cust.zaw.aop.Log.addLog())" id="poioncut"/>
<!-- advisor: 相当于 链接切入点 和 切面的线 -->
<aop:advisor advice-ref="logBefore" pointcut-ref="poioncut"/>
</aop:config>
第二种是通过注解的方式实现AOP
//日志切面类
//得加上此注解此切面类才会生效
@Aspect
public class MyLog {
//抽取公共的切入点表达式
//该表达式表明MyService类下的所有方法都加上aop功能
@Pointcut("execution(public int org.com.MyService.*(..))")
public void pointCut(){};
//在目标方法之前切入
//复用上面定义的pointCut()方法上的切入点表达式
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
//获取目标方法名 (单纯取出来而已下面没有用到这个变量)
String methodName = joinPoint.getSignature().getName();
//获取目标方法参数列表 (下面有输出这个)
Object[] args = joinPoint.getArgs();
System.out.println("除法运行...参数列表是: {" + Arrays.asList(args) + "}");
}
//在目标方法之后切入
@After("pointCut()")
public void logEnd(){
System.out.println("除法结束...");
}
//在目标方法正常返回切入
//result是目标方法正常返回的返回值
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(Object result){
System.out.println("除法正常返回...运行结果: { " + result + " }");
}
//在目标方法异常时切入
//exception是目标方法异常的异常对象
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logException(Exception exception){
System.out.println("除法异常...异常信息: {" + exception+ "}");
}
}
- @Aspect : 定义切面类
- @Pointcut : 定义切入点表达式
- @Before : 前置通知
- @After : 最终通知
- @AfterReturning : 后置通知
- @AfterThrowing : 异常通知
2.1.6 Spring支持的几种bean的作用域 Scope
Spring框架支持以下五种bean的作用域:
- singleton : bean在每个Spring ioc 容器中只有一个实例
- prototype:一个bean的定义可以有多个实例
- request:每次http请求都会创建一个bean
- session:在一个HTTP Session中,一个bean定义对应一个实例
- global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例
2.1.7 Spring框架中的单例bean是线程安全的吗
不是,Spring框架中的单例bean不是线程安全的 , spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。
但是我们一般在使用单例Bean的时候, 一般这些Bean中都不会设置共享属性, 属于无状态Bean , 所以也就不会存在线程安全问题 ! 从这个角度讲单例bean也是线程安全的
2.1.8 讲一讲Spring中Bean的生命周期
当我们启动Spring容器后,会先通过refresh
方法,调用BeanFactoryPostProcess
方法,可以在bean初始化前,修改context中的BeanDefinition
, 然后会开始初始化非懒加载的bean
- 通过
InstantiationAwareBeanPostProcessor
在实例化bean之前做前置处理,这样做的目的是可以给应用代码一个返回代理bean的机会(Spring AOP在这里实现) - 通过反射实例化该bean(有多种情况,分为有参数,无参数构造器,以及是否有Autowired注解)
- 填充Bean的field实例(此时可能会有循环依赖问题,涉及到循环初始化)
- 调用初始化方法之前,先调用
BeanPostProcessor#postProcessBeforeInitialization
方法(各种Aware的织入就在此处执行)
- 执行该bean的初始化方法,如
InitializingBean#afterProperties
方法,或者自定义的init-method
方法
- 调用
BeanPostProcessor#postProcessAfterInitialization
执行初始化的后置处理方法 - 如果该bean有相关的销毁方法,则将对应的销毁方法注册进容器中,当销毁bean的时候会进行回调处理
2.1.9 Spring是如何解决循环依赖的
循环依赖是指一个或多个对象之间存在直接或间接的依赖关系,这种依赖关系构成一个环形调用 , 举个例子 : A 依赖B , B依赖C , C依赖A , 这样就形成了循环依赖
Spring解决循环依赖问题, 靠的是三级缓存机制 , Spring中的三级缓存
- 第一级缓存:
singletonObjects
,用于保存实例化、注入、初始化完成的 bean 实例 - 第二级缓存:
earlySingletonObjects
,用于保存实例化完成的 bean 实例 - 第三级缓存:
singletonFactories
,用于保存 bean 创建工厂,以便后面有机会创建代理对象
三级缓存的执行逻辑
- 先从“第一级缓存”找对象,有就返回,没有就找“二级缓存”
- 找“二级缓存”,有就返回,没有就找“三级缓存”
- 找“三级缓存”,找到了,就获取对象,放到“二级缓存”,从“三级缓存”移除
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从一级缓存获取Bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
//从二级缓存获取Bean
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized(this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
//从三级缓存获取Bean工厂
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//通过Bean工厂获取Bean对象
singletonObject = singletonFactory.getObject();
//将Bean保存到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
//从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
当出现循环依赖时,Spring会从singletonObjects和earlySingletonObjects缓存中获取已经创建的对象,如果都没获取到,会调用singletonFactories中的ObjectFactory来创建半成品对象。在半成品对象创建完成之后,将其注入到之前创建的Bean中,完成循环依赖的解决。
但是,Spring解决循环依赖是有一定限制的,首先就是要求互相依赖的Bean必须要是单例的Bean,另外就是依赖注入的方式不能都是构造函数注入的方式。
2.1.10 Spring中在什么情况下事物会失效
Spring中事物失效的场景很多 , 例如 :
- 因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有是被代理对象调⽤时, 那么这个注解才会⽣效 ! 如果使用原始对象事物会失效
- 同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是基于⽗⼦类来实现 的,⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的利⽤代理,也会导致@Transactianal失效
- 如果在业务中对异常进行了捕获处理 , 出现异常后Spring框架无法感知到异常, @Transactional也会失效
- @Transational中默认捕获的是RuntimeException , 如果没有指定捕获的异常类型, 并且程序抛出的是非运行时异常, 事物会失效
2.1.11 说一说@Transational
的原理
- Spring事务底层是基于数据库事务和AOP机制的
- ⾸先对于使⽤了@Transactional注解的Bean,Spring会创建⼀个代理对象作为Bean
- 当调⽤代理对象的⽅法时,会先判断该⽅法上是否加了@Transactional注解
- 如果加了,那么则利⽤事务管理器创建⼀个数据库连接
- 并且修改数据库连接的autocommit属性为false,禁⽌此连接的⾃动提交,这是实现Spring事务⾮ 常重要的⼀步
- 然后执⾏当前⽅法,⽅法中会执⾏sql
- 执⾏完当前⽅法后,如果没有出现异常就直接提交事务
- 如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务
2.1.12 @Transational
属性有哪些
常用的属性有 :
- propagation : 事物传播行为
- isolation : 事物隔离级别, 默认数据库隔离级别
- readOnly : 是否是只读事物
- rollbackFor : 指定哪些异常需要回滚 , 默认RuntimeException
- noRollbackFor : 指定哪些异常不需要回滚
2.1.13 说一下Spring的事务传播行为有哪些
- PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置
- PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务
- PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
- PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行
2.1.14. spring里面的事务, 都加了事务注解, A方法调用B方法,会不会都执行
- 如果A加@Transactional注解,不管是不是在一个类中,不管B加不加注解,AB都是在同一事务中
- 如果A不加@Transactional注解,只有B加@Transactional注解,AB方法为同一类,事务失效;AB不同类,只有B有事务;
2.1.15 @Autowired
和@Resource
有什么区别
@Resource
和@Autowired
都可以标注在字段或属性的setter方法上 , 代表自动注入 , 他们的区别主要有 :
- 提供方不同:@Autowired是由Spring提供;@Resource是由javax提供,需要JDK1.6及以上
- 注入方式不同:@Autowired只按照byType 注入;@Resource默认按byName自动注入,也提供按照byType 注入;
- 使用方式不同:@Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。@Resource有两个中重要的属性:name和type。name属性指定byName,type属性指定byType
2.1.16 Spring中使用的设计模式有哪些
- 单例模式 : Spring中的单例Bean没有使用单例设计模式 , 但是在其他地方使用了单例设计模式 , 例如 :
- org.springframework.transaction.TransactionDefinition#withDefaults
- org.springframework.aop.TruePointcut#INSTANCE
- org.springframework.core.OrderComparator#INSTANCE
- 构建者模式 : 主要用于构建对象, 例如 :
- org.springframework.beans.factory.support.BeanDefinitionBuilder
- org.springframework.http.ResponseEntity.HeadersBuilder
- org.springframework.http.ResponseEntity.BodyBuilder
- 工厂模式 : 隐藏对象创建细节 , 例如: Spring 中的 ApplicationContext 与 BeanFactory 中的 getBean 都可以视为工厂方法
- 适配器模式 : 将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而导致无法在一起工作的两个类能够一起工作 , Spring中典型的实现有几处:
- org.springframework.web.servlet.HandlerAdapter – 因为控制器实现有各种各样,它们的处理方法都不一样,为了统一调用,必须适配为 HandlerAdapter 接口
- org.springframework.beans.factory.support.DisposableBeanAdapter – 因为销毁方法多种多样,因此都要适配为 DisposableBean 来统一调用销毁方法
- org.springframework.web.servlet.handler.HandlerInterceptorAdapter : web拦截器的适配器
- 代理模式 : 通过代理对象, 控制目标对象的访问
- org.springframework.aop.framework.JdkDynamicAopProxy
- org.springframework.aop.framework.ObjenesisCglibAopProxy
- 装饰模式 : 对目标对象功能进行增强 , Spirng中提供的各种Wrapper结尾的类基本上都是装饰模式 , 例如 :
- org.springframework.http.client.support.HttpRequestWrapper
- org.springframework.web.util.ContentCachingRequestWrapper
- 观察者模式 : Spring中的一些监听器 , 基本都是使用的观察者模式 , 例如 ;
- org.springframework.context.ApplicationListener
- org.springframework.context.event.ApplicationEventMulticaster
- org.springframework.context.ApplicationEvent
- 模版方法模式 : Spring中一定了很多以 Template 命名的类 , 都是属于模版方法模式
- 大部分以 Template 命名的类,如 JdbcTemplate,TransactionTemplate
2.1.17 BeanFactory和FactroyBeanl的关系?
他们的区别比较容易理解,从字面意思就能区分开发,
BeanFactory : 是Bean工厂,BeanFactory是Spring中工厂的顶层规范,他是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。它定义了getBean()、containsBean()等管理Bean的通用方法。
FactroyBean : 是工厂Bean , Spring容器中有两种Bean : 普通Bean和工厂Bean。Spring直接使用前者,FactoryBean跟普通B