【八股文】Spring篇

目录

1. 什么是IOC和DI

1. IOC 控制翻转

1. 定义

将创建对象的方式交给Spring来管理,应用程序需要的时候直接向Spring拿,而不是自己采用new的方式创建

2. 例子

对象a 依赖了对象 b,当对象 a 需要使用 对象 b的时候必须自己去创建。但是当系统引入了 IOC 容器后, 对象a 和对象 b 之前就失去了直接的联系。这个时候,当对象 a 需要使用 对象 b的时候, 我们可以指定 IOC 容器去创建一个对象b注入到对象 a 中"。 对象 a 获得依赖对象 b 的过程,由主动行为变为了被动行为,控制权反转了,这就是控制反转名字的由来。

2. DI 依赖注入

动态地向对象中注入它所需要的其他对象,原理是通过反射
注入的方式,setter方法、构造器注入、注解自动注入(@Autowired、@Resource、@Value)

2. 谈谈对Spring的AOP的理解

1. 要素

1. 切面@Aspect

@Aspect切面,切面类的注解,代表是一个切面类

2. 切入点@PointCut

@PointCut切入点,指定哪些注解(一般是自定义注解用@interface)会进入这个切面

3. 连接点JoinPoint

JoinPoint,连接点,程序进入切面的点

4. Advice通知

1. 前置通知@Before

@Before在方法执行前需要的切面操作

2. 后置通知@After

@After在方法执行完成后需要的切面操作

3. 环绕通知@Around(常用)

@Around两者都可以有,在方法调用前后完成自定义行为

2. 作用(解耦)

解耦、提高代码的复用率、开发者只需要注重纵向业务

3. 原理(代理)

1. 静态代理

AspectJ AOP使用的代理,编译时会生成AOP代理类,合并到Java字节码中(相当于帮我们改代码了)

2. 动态代理

Spring AOP使用的代理,如果被代理的实现了接口,就会采用的是jdk,其他情况采用的是cglib

1. jdk动态代理(Proxy类、InvocationHandler接口)

核心是Proxy类InvocationHandler接口

步骤如下:

  1. Proxy类来动态创建目标类的代理类
  2. 生成的代理类调用真实对象的方法时,InvocationHandler调用invoke()反射来调用目标方法,将切面和业务逻辑合并在一起
1. Proxy的作用

创建代理类,通过代理类调用目标方法

2. InvocationHandler的作用

反射调用invoke方法,将代码整合到一起

2. cglib动态代理

cglib是一个优秀的代码生成类库,采用继承的方式,生成一个被代理对象的子类来作为代理

3. 问:AspectJ AOP和Spring AOP的区别

Spring AOP 基于代理(Proxying),而 AspectJ AOP基于字节码操作(Bytecode Manipulation)。

4. 问:AOP在Bean的生命周期

1. @EnableAspectJAutoProxy

@EnableAspectJAutoProxy注解实际上就是向容器中注册了一个AnnotationAwareAspectJAutoProxyCreator的BeanDefinition,这个类本身就是一个后置处理器,AOP代理就是由它在这一步完成的

2. postProcessBeforeInstantiation方法执行

执行AnnotationAwareAspectJAutoProxyCreatorpostProcessBeforeInstantiation方法,实际上就是父类AbstractAutoProxyCreatorpostProcessBeforeInstantiation被执行

注意:Instantiation是实例化;Initialization是初始化

3. postProcessAfterInitialization方法执行(AOP代理创建时机)

实际上也是执行父类AbstractAutoProxyCreator中的方法

4. wrapIfNecessary方法执行

对需要进行代理的Bean完成AOP代理的创建

在这里插入图片描述
图片来源:https://blog.csdn.net/qq_41907991/article/details/107141101

3. Spring、SpringMvc常用注解

1. Spring

1. 声明bean注解(4个)

@Controller、@Component、@Service、@Repository

2. 自动注入(@Autowired、@Resource)

@Autowired、@Resource、@Qualifier、@Value

1. 题外话 @Autowired、@Resource的区别

@Autowired按照类型(ByType)注入,存在多个的时候用@Qualifier制定具体的名称
@Resource按照名称(ByName)注入

3. 配置相关(@Configuration、@Bean)

@Configuration、@Bean、@ComponentScan(扫描Bean)

4. AOP相关

@Aspect、@PointCut、@Before、@After、@Around

5. 异步相关(@EnableAync、@Async)

@EnableAync、@Async

2. SpringMvc

1. 请求相关

@RequestMapping:映射请求url
@RequestParam:获取请求路径后面的参数值
@RequestBody:获取请求体上的参数值
@ResponseBody:接口返回以json的形式

2. 异常处理相关(@ControllerAdvice、@ExceptionHandler)

@ControllerAdvice:定义一个异常类
@ExceptionHandler:用于全局处理控制器里的异常

4. 说说SpringMvc的执行流程?(非常重要)

DispatcherServlet、HandlerMapping、Handler、HandlerAdapter、视图解析器

5. 讲一讲Bean的生命周期(非常重要)

四个步骤+各种Aware
在这里插入图片描述

1. 源码如下

// 忽略了无关代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (instanceWrapper == null) {
       // 1. 实例化阶段!
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
       // 2. 属性赋值阶段!
      populateBean(beanName, mbd, instanceWrapper);
       // 3. 初始化阶段!
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }  
}

2. 步骤

主要流程:实例化、属性赋值、初始化、销毁

1. 实例化(构造器)

通过构造器实例化,选择最佳构造方法

注意:实例化结束后对外暴露的是ObjectFactory(用于获取Bean的工厂类)而不是实例化对象本身,可以提供getObject() 来获取Bean实例。

2. 属性赋值(setter方法)

使用setter方法对属性进行赋值操作

3. 初始化

4.销毁(调用destroy)

实现DisposableBean接口调用destroy方法

3. 扩展点

1. 影响多个Bean(2个BeanPostProcessor)

1. InstantiationAwareBeanPostProcessor(Aware相关的Bean加工器)

初始化Aware相关的Bean

1. 作用范围(实例化阶段前后)

实例化阶段前后

2. BeanPostProcessor(后置处理器)
1. 作用范围(初始化阶段前后)

作用于初始化阶段的前后,初始化前调用postProcessBeforeInitialization,初始化之后调用postProcessAfterInitialization

2. 题外话:Aop代理被创建的是什么时候?

BeanPostProcessor调用postProcessAfterInitialization方法创建的

2. 题外话:BeanFactoryPostProcessor和BeanPostProcessor的区别
1. 相同点(都是扩展点)

两者都是对Spring创建Bean提供的扩展点

2. 不同点(BeanFactoryPostProcessor执行早一点)

BeanFactoryPostProcessor早点,只有1方法,在Bean实例化之前执行的
BeanPostProcessor晚点,有2个方法,分别是在Bean初始化前后执行的

2. 影响单个Bean(各种Aware)

1. 调用各种Aware
1. Aware的调用时机(初始化之前)

所有的Aware方法都是在初始化阶段之前调用的

1. xxxAware的作用

帮助我们拿到Spring容器的xxx资源

2. 有哪些Aware(Bean名称、类加载器、工厂类)
  1. BeanNameAware:获取BeanName
  2. BeanClassLoaderAware:获取ClassLoader对象的实例
  3. BeanFactoryAware:获取BeanFactory对象的实例

参考:https://www.jianshu.com/p/1dec08d290c1.

6. Spring是如何解决循环依赖的(难)

在这里插入图片描述

1. 解决方案(setter注入)

用setter方法注入(非构造方法注入),且对于单例模式下的Bean

注意:如果是构造器注入引起的循环依赖,直接启动不了,无解

2. 关键词

Bean的生命周期(实例化属性赋值分开)、ObjectFactory、三级缓存

2. Spring解决循环依赖的过程

1. 现象

ClassA引用ClassB,ClassB中又引用了ClassA。

2. 解决步骤

  1. A的实例化,Spring选择合适的构造器实例化A,并把A的ObjectFactory放入到三级缓存

  2. A的属性赋值,赋值的过程中,发现成员变量B为被实例化,接着会去执行B的生命周期

  3. B的实例化,Spring选择合适的构造器实例化B,并将B的ObjectFactory放入到三级缓存

  4. B的属性赋值,赋值的过程中,发现成员变量A能够在三级缓存中找到(半成品),B属性赋值成功,删除二级、三级缓存中的Bean,保存到一级缓存中

  5. 回到A的属性赋值,A属性赋值成功(完成品),删除二级、三级缓存中的Bean,保存到一级缓存中

1. 问:这里第4步骤,属性B是如何找到A的半成品对象的
  1. 从三级缓存中取出Bean对应的ObjectFactory实例,调用其getObject方法,来获取早期曝光Bean,最终会调用getEarlyBeanReference方法

  2. 拿到半成品的bean实例(属性未填充),从三级缓存移除,放到二级缓存earlySingletonObjects

    注意:此时B注入的是一个半成品的实例A对象,不过随着B初始化完成后,A会继续进行后续的初始化操作,最终B会注入的是一个完整的A实例,因为在内存中它们是同一个对象

1. getEarlyBeanReference的作用

2种情况:

  1. 如果bean被AOP切面代理则返回的是beanProxy对象
  2. 如果未被代理则返回的是原bean实例
2. 问:为什么要用到二级缓存?(AOP代理的Bean)

好像只需要用到三级缓存就可以了,为什么要用到二级缓存?

1. bean被AOP进行了切面代理的场景

getEarlyBeanReference方法返回的是代理对象,还是被CGLIB代理的,每次执行都会产生一个新的代理对象

这就会有问题了,因为A是单例的,每次执行singleFactory.getObject()方法又会产生新的代理对象,假设这里只有一级和三级缓存的话,我每次从三级缓存中拿到singleFactory对象(也可以认为是ObjectFactory),执行getObject()方法又会产生新的代理对象,这是不行的,因为A是单例的,所有这里我们要借助二级缓存来解决这个问题,将执行了**singleFactory.getObject()**产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍singletonFactory.getObject()方法再产生一个新的代理对象,保证始终只有一个代理对象

2. 二级缓存的作用

在Bean被AOP代理的情况下,保证始终只有一个代理对象

3. 总结

如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以需要借助另外一个缓存来保存产生的代理对象

参考一下优秀的文章:Spring 为何需要三级缓存解决循环依赖,而不是二级缓存

7. Spring中Bean的一级缓存、二级缓存、三级缓存

1. 一级缓存(初始化完成的Bean)singletonObjects

存在ConcurrentHashMap中,用来存放已经初始化完成的Bean

2. 二级缓存(有问题的Bean)earlySingletonObjects

存在HashMap中,存放半成品的Bean,一般处于循环引用的Bean才会暂时保存在这个缓存里

3. 三级缓存(存ObjectFactory)singletonFactories

存在HashMap中,存放ObjectFactory实例(用来获取Bean的工厂类)、在IoC容器中,所有刚被创建出来的ObjectFactory,默认都会保存到该缓存中。

1. 作用

spring会提前将已经实例化的bean通过ObjectFactory半成品暴露出去

半成品是指:bean对象已经实例化,但是未进行属性填充,是一个不完整的bean实例对象

4. 三者之间的关系

  1. 一级二级缓存中的Bean是互斥的,同一个Bean只可能存在其中一个缓存
  2. 二级缓存中保存的是BeanFactory产生的对象或AOP代理类对象
  3. 三级缓存只存工厂类,不存Bean实例

8. Bean的作用域

  1. singleton(默认):所有线程共用一个实例
  2. prototype(原型,每次请求都会创建一个bean实例)
  3. request
  4. session
  5. global-session

设定方式:xml用scope指定、注解@Scope(value = “singleton”)指定

小tips:singleton作用域下才会走spring的生命周期流程、prototype用到的时候才走

1. 题外话:Spring框架中的单例bean是线程安全的吗

不是,但是像Controller、Service这些大多数都是无状态的,不存在除了查询以外的操作,所以很安全;对于有状态的Bean 可以设置Bean的作用域为prototype,对于每个线程都有各自的Bean,绝对安全

9. Spring框架中都用到了哪些设计模式

1. 单例模式(单例注册表)

spring默认创建的bean就是单例的
spring通过ConcurrentHashMap实现单例注册表的特殊方式实现单例模式

2. 工厂模式(BeanFactory、ApplicationContext )

通过BeanFactory 或 ApplicationContext 创建 bean 对象。

1. BeanFactory(懒汉思想生成Bean)

BeanFactory 延迟注入,用到的时候才会实例化Bean,优点启动块。(以前内存还不够用的时候)

白话文:懒汉模式的思想

2. ApplicationContext(饿汉思想生成Bean)

ApplicationContext 启动时就会实例化所以Bean,扩展了BeanFactory,有额外功能(用的多)

白话文:饿汉模式的思想

3. 代理模式(AOP)

Spring AOP的原理就是采用动态代理。例如AopProxy、CglibAopProxy、JDKDynamicProxy

4. 模板方法

一般实现:使用继承抽象父类并@Override其父类的abstract方法
jdbcTemplate使用Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性

5. 适配器模式(AdvisorAdapter、HandlerAdapter)

1. AdvisorAdapter

Spring AOP 的Advice通知用到了AdvisorAdapter,搭配拦截器使用

2. HandlerAdapter

SpringMvc中 DispatcherServlet根据请求信息调用HandlerMapper,解析出来的Handler,交给HandlerAdapter(适配器),适配对应的Controller类

6. 策略模式(BeanPostProcessor)

例如:BeanPostProcessor (具体哪里体现策略模式?todo)

10. Spring事务

1. ACID特性

1. 原子性

事务内的操作,要么全部执行,要么全部回滚。

2. 一致性(逻辑上的一致性)

强调数据库在逻辑上的一致性状态,依赖于原子性。

3. 隔离性

两个事务之间互不影响

4. 持久性(落库)

事务被提交之后,事务的一系列操作的结果是需要落库

1. 题外话:原子性和一致性的区别
  1. 一致性强调逻辑上一致,程序员可以写出原子性事务,但是在逻辑上是错误的代码(逻辑有问题,比如A转账给B,但代码只写了A扣钱,照样能符合原子性,但不符合一致性)。
  2. 一致性需要依赖于原子性,只有符合原子性的代码才可能有一致性

2. 隔离级别

1. 代码

@Transactional(isolation = Isolation.DEFAULT)

2. 级别

  1. 默认(default):数据库的隔离级别为主
  2. 读未提交(read_uncommit)
  3. 读已提交(read_commit)
  4. 不可重复读(repeat_table)
  5. 串行化(serializable)

3. 传播行为(7种)

1. 代码

@Transactional(propagation = Propagation.REQUIRE)

3. 总结(用事务、不用事务、报错)

对下面7种事务的方法就行总结

1. 如果当前有事务
  1. 用这个事务
  2. 创建个新的事务
  3. 把当前事务挂起,不用事务
  4. 报错
2. 如果当前没有事务
  1. 创建个新的事务
  2. 不用事务
  3. 报错

3. 支持事务

  • PROPAGATION_REQUIRED:如果当前没有事务,就创建一个;如果有事务,则使用这个事务(常用)
  • PROPAGATION_REQUIRES_NEW :不管有没有事务,都自己创建一个新事务(如果原来有事务,则会被挂起)
  • PROPAGATION_SUPPORTS:使用当前事务,如果当前没有事务,就以非事务方式执行
  • PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。

4. 不支持事务

  • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED:如果有当前事务,则嵌套;如果没有,就创建一个。(和PROPAGATION_REQUIRED类似)

4. 优点(用注解、集成多种数据库)

  1. 支持声明式事务管理。
  2. 对各种数据库抽象访问层有很好的集成
  3. 编程式事务管理提供了一套简单的 API,就算不用声明式也很方便

5. 实现方式

1. 声明式事务

采用注解@Transactional实现,原理是AOP,Spring启动的时候会扫描这个注解相关的类,并生成代理类,主要功能如下:

  • Before时,根据propagation属性确定事务的传播行为

  • After时,切面需要确定事务被提交,回滚或者继续运行,真正的数据库层的事务提交和回滚是通过bin log或者redo log实现的

2. 编程式

对代码有侵入性,不建议

  • 8
    点赞
  • 86
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值