【Spring】八股总结一:IOC、AOP、生命周期、循环依赖等

164 阅读16分钟

Spring视频零基础入门到高级,spring全套视频教程详解_哔哩哔哩_bilibili动力节点

课程笔记:Spring6 密码mg9b

动力节点老杜,详细讲解。(这部分涉及spring基础、手写spring框架,基础忘了可以看)

2021最新版 SpringBoot 源码剖析全集(27P),P8阿里大神都收藏的全网最详细教程,从0到1手把手教你写源码_哔哩哔哩_bilibili上面是拉钩教育是springboot。

孙哥说Spring5:从设计模式到基本应用到应用级底层分析,一次深入浅出的Spring全探索。学不会Spring?只因你未遇见孙哥_哔哩哔哩_bilibili

什么是Spring? 死背咯

  • Spring是一种轻量级Java框架
  • 一般指的是SpringFramework,它是很多模块的集合,模块方便协助我们开发。比如Spring支持IOC和AOP,有web、aop、核心容器、工具、消息、测试模块。

Spring的一些模块?死背咯

  • Spring Core:这是Spring框架最基础的部分, 它提供了依赖注入(DependencyInjection)特征来实现容器对Bean的管理。核心容器的主要组件是 BeanFactory,BeanFactory是工厂模式的一个实现,是任何Spring应用的核心。它使用IoC将应用配置和依赖从实际的应用代码中分离出来。
  • Spring Aspects:该模块为与AspectJ的集成提供支持。
  • Spring AOP:提供面向切面的编程实现。
  • Spring Bean:Bean的创建、配置和管理。
  • Spring JDBC:Java数据库连接。
  • Spring tx: 事务的支持
  • Spring Web:为创建Web应用程序提供支持。
  • Spring Test:提供了对JUnit和TestNG测试的支持。
  • Spring JMS:Java消息服务。

结合图记一下

Spring中用到了哪些设计模式【重点】?

单例,代理,模板方法,工厂,观察者

Spring 中的设计模式详解

014-对Spring的第一个程序小细节2_哔哩哔哩_bilibili(BeanFactory)

单例:bean的作用域用到了单例bean,spring本身就是一个单例bean的管理。

代理:AOP的时候用到了动态代理,有两种实现方式,jdk和cglib。

模板方法:定义父抽象类或者接口,父类定义模板方法,部分抽象方法延迟到子类中定义。像JdbcTemplateRedisTemplate还要hibernateTemplate就是典型的模板方法模式。

工厂模式:Spring中顶级接口BeanFactory就是工厂模式的实现。包括其扩展接口ApplicationContext容器类似于工厂模式的一种实现。 (所以说SpringIOC底层是基于 工厂模式+xml解析+反射实现的)

观察者模式:在我的项目中用到了Spring事件驱动模型,通过实现ApplicationContextAware接口获取ApplicationContext上下文,调用上下文的publishEven方法。监听者通过@EventListener注解进行监听。

IOC

开闭原则?

004-软件开发原则之OCP开闭原则_哔哩哔哩_bilibili

对扩展开放。对修改关闭。

只要你在扩展系统功能的时候,没有修改以前写好的代码,那么你就是符合0CP原则的。

反之,如果在扩展系统功能的时候,你修改了之前的代码,那么这个设计是失败的,违背0CP原则。

依赖倒置原则?

006-控制反转IoC思想的理解_哔哩哔哩_bilibili

上层强依赖下层。

原则:面向接口编程,不要面向具体实现编程。

目的:降低耦合度,提高扩展力。

说一说IOC和AOP?背前几句话

Ioc控制反转

  • IOC也叫控制反转,其实是一种设计思想。就是说将本在程序中 手动创建对象的控制权,交给spring管理。Ioc这个思想在其他语言中也有。

  • 控制 是指对象的创建和管理的权利

  • 反转 控制权交给Spring等Ioc容器 (new 对象不需要程序员new了,对象直接的关系也不用管了,给IOC容器管)

  • 对象之间的依赖关系给Ioc容器管理,简化开发。我们需要一个对象的时候,只需要用配置文件/注解获取,不用关系对象的创建。(换句话说将创建对象过程和具体业务解耦,有点像工厂)

  • 例如在项目中,controller中需要用Service类,service可能又依赖了其他的类,之前的方式是实例化这个service时还需要搞清楚他的构造函数去手动构造它,利用Ioc时只需要配置好,在需要的地方引用注入就行了。

DI 依赖注入是什么

007-依赖注入DI_哔哩哔哩_bilibili

  1. 控制反转是思想。依赖注入是这种思想的具体实现
  2. 依赖注入DI(就是给属性赋值),又包括常见的两种方式:
    1. 第一种:set注入
    2. 第二种:构造方法注入
    3. autowired注解注入

DI依赖注入是如何实现的呢?(阿里)

依赖注入嘛,也就是属性赋值,是Bean的生命周期的一部分,在Bean实例化完成之后进行依赖注入

  1. set方法注入

  2. 构造器注入

  3. autowired和resource注解的注入

那IOC和工厂模式有什么区别(进阶)

Spring IOC与工厂模式_spring ioc用的是哪种工厂模式-CSDN博客

记住BeanFactory和applicationContext这两个接口。

这篇文章讲了springIOC和工厂模式的区别,以及springIOC容器创建对象过程的原理 (读文件全类名-->构造BeanDefinetion-->根据BeanDefinetion创建Bean对象 并放入缓冲池中 )


我的理解:springIOC和工厂模式,类似于一种实现方式

  • 工厂模式使用new创建对象,而springIOC使用反射创建的(通过反射读取xml文件构造beanDefinition,根据beanDefinition创建Bean)。
  • 所以springIOC更灵活,可以在运行时添加新的对象。而工厂模式中有新的对象需要放入工厂需要改代码

依赖注入,注入的时候有几种方法?

  1. 基于构造函数的注入。

  2. 基于setter方法的注入。

  3. 基于 @Autowired注解注入。

Bean如何实现懒加载?效果是什么?(华为)

@Lazy注解

  • 懒加载在 依赖注入的时候,不会真的去单例池(一级缓存)中找对象,而是创建一个新的对象?
    后面要用的时候再去Spring容器一级缓存中寻找这个Bean,可以解决一个循环依赖的问题。

AOP

AOP面向切面编程

  • AOP面向切面编程,核心思想是 将与 核心业务无关,但被业务模块 共同调用的 横切关注点(例如日志、事务、权限控制、接口限流)核心业务中 分离出来实现解耦。通过动态代理字节码操作 实现代码解耦合。
  • 横切关注点 一般是 分散在多个对象和类 中的公共行为(例如日志、事务、权限控制、接口限流) ,如果每个类或方法都重复,会导致代码冗余。(达到减少重复代码,解耦合,可拓展的目的)
  • 例如日志,对于一些方法记录日志,没有AOP时需要重复挨个写。有AOP后,将日志逻辑封装成切面,然后指定哪些方法需要日志记录。

AOP的实现方式?

动态代理字节码操作 两种方式:

动态代理: (SpringAOP采用的是动态代理,且是运行时增强)

  • JDK动态代理:要求被代理的类实现了某个接口,生成的代理类同样是这个接口的实现,但不是目标类与目标类是兄弟关系
  • Cglib动态代理:目标类如果没有接口,生成一个 目标类 的子类 作为代理类。(是目标类的儿子

字节码操作(静态代理): AspectJ基于字节码操作的方式,是编译时增强


拓展: 为什么JDK动态代理要求 被代理类 实现接口,只能代理接口

因为JDK动态 代理自动生成的代理类 $Proxy0继承了java.lang.reflect.Proxy类,由于java是单继承的,所以这里没有机会再去继承被代理类

动态代理和静态代理?(重点)

具体看动力节点老杜

094-GoF代理模式之静态代理代码实现_哔哩哔哩_bilibili

  1. 静态代理:自己写一个公共接口,然后写一个目标类代理类实现 接口并重写方法,将目标类对象作为代理类的成员, 并在代理类中调用目标类的方法。通过代理类的方法去操作目标类并进行相应的增强。
  2. 动态代理运行时创建代理类,如基于jdk的动态代理。

静态代理和动态代理有什么区别?

  1. 一个编译时确定,一个是运行时才确定。
  2. 静态代理为目标类手动写代理类,动态代理通过反射生成代理类,更加灵活。

动态代理是什么?(记一记重点)

  • 运行时生成一个目标类的代理类。目标类和代理类实现相同的接口或者是继承关系。
  • 代理类方法中对目标类进行增强,并调用目标类的一些方法。

有 jdk和CGlib两种方式.........,满足开闭原则。

jdk动态代理如何实现

Proxy.newInstance(classLoader, interface数组,目标类)

@Autowired 和 @Resource 的区别是什么?

  • @Autowired 是 Spring 提供的注解,@Resource 是 JDK 提供的注解。

  • @Autowired 默认的注入方式为byType(根据类型进行匹配),如果有多个实现类注入方式会变为 byName@Resource默认注入方式为 byName(根据名称进行匹配),如果通过name找不到会变成byType

  • 当一个接口存在多个实现类的情况下,@Autowired 和@Resource都需要通过名称才能正确匹配到对应的 Bean。Autowired 可以通过 @Qualifier 注解来显式指定名称,@Resource可以通过 name 属性来显式指定名称。

  • @Autowired 支持在构造函数、方法、字段和参数上使用。@Resource 主要用于字段方法上的注入,不支持在构造函数或参数上使用。

Spring中bean的作用域有哪些?死背咯?

  • singleton: 单例的。
  • prototype: 每次请求都会创建一个新的bean实例。
  • request(仅 Web 应用可用) 每次HTTP请求都会产生一个新的bean(请求bean),仅在当前HTTPrequest内有效。
  • session(仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。什么是HTTPsession:Session_http session 什么意思-CSDN博客
  • application/global-session(仅 Web 应用可用) :每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
  • websocket(仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。

定义方式,通过scope注解定义

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
    return new Person();
}

Spring Bean 的生命周期说一下

Spring常见面试题总结

小白也能懂的最详细之spring-bean生命周期讲解_哔哩哔哩_bilibili

如何叙述Spring Bean 的生命周期,让面试官眼前一亮!_哔哩哔哩_bilibili

  1. 创建实例化bean,通过反射进行实例化。(读取xml配置文件构造beanDefinition集合,根据beanDefinition进行反射实例化)
  2. Bean的属性赋值/填充 和 依赖注入。(例如@Autowired 等注解注入的对象、@Value 注入的值、setter方法或构造函数注入依赖和值、@Resource注入的各种资源)
  3. 调用bean的初始化方法,某些bean可能通过 init-method xml配置 @postConstruct注解设置了初始化方法。
  4. 使用Bean
  5. 销毁Bean,调用bean销毁前的方法。调用配置文件中的定义包含 destroy-method 属性 或者 通过@PreDestroy 注解标记 Bean 销毁之前执行的方法。

bean生命周期七步的版本

或者看老杜

小白也能懂的最详细之spring-bean生命周期讲解_哔哩哔哩_bilibili

在Bean初始化前后beanPostProcessor接口 的方法

实现 beanPostProcessor 接口 并实现两个方法(这个接口是另外需要注入的bean,并在方法中判断bean的名称)。

bean生命周期十个步骤版本

在beanPostProcessor的before方法前后,又有两个方法

  • 实现BeanNameAware接口的setBeanName方法

  • 实现 InitializingBean接口的afterPropertiesSet方法

  • 实现 DisposableBean接口 的 destroy 方法,(配置的destroy-method之前调用)

spring aop对象的创建

在执行IOC流程的时候,(在bean进行初始化之后)顺带就会创建代理对象,但不是所有bean都需要代理对象。

Bean初始化(不是实例化)之后执行代理对象流程,判断是否需要创建代理,获取所有advisor与目标bean进行匹配,然后将目标对象绑定advisor

然后传递给 由proxyFactory去创建代理对象

Spring如何解决循环依赖问题?难

Spring 如何解决循环依赖 - LARRY1024 - 博客园

极客时间课程

058-Bean的循环依赖之源码分析_哔哩哔哩_bilibili(老杜,讲得比较浅)

如何解决Spring循环依赖?P8大佬带你源码深度剖析!【2022最新版】_哔哩哔哩_bilibili(详细版本)

华为二面:Spring是如何解决循环依赖的?为什么一定要三级缓存?二级缓存能不能解决循环依赖?_哔哩哔哩_bilibili

循环依赖产生的地方

A依赖B,B依赖A。

在A实例化完之后进行依赖注入属性赋值,因为依赖B所以需要getBean(B)去map中找。B不存在这时也需要进行创建,B也是实例化然后属性注入B属性注入A,再去getBean(A) ,A这时因为还没创建完成也不存在,就死循环了。

在没有AOP的情况下用两级缓存可以解决

例如,在A进行实例化之后先放入 二级对内的缓存(外部不能获取),那么在B注入getBean(A)时可以从二级缓存中获取到A的引用,然后A完成注入和初始化后 把A从二级缓存放入一级缓存。

二级缓存存在的问题

但这样在aop的情况下有问题,因为A初始化后会产生A的代理对象(并且后续使用也是用代理对象),而B中从二级缓存获取的A是原目标对象。 (目标对象在后续是被抛弃的对象了)

如何解决?

058-Bean的循环依赖之源码分析_哔哩哔哩_bilibili(老杜,讲得比较浅)

三级缓存,三个map

一级缓存Map<String, Object> singletonObjects

  • 对外提供单例bean,也就是创建完成的完整的bean。

二级缓存Map<String, Object> earlySingletonObjects

  • 存放正在创建的bean,对内,三级缓存获取的代理对象会放入二级缓存中。

三级缓存Map<String, ObjectFactory> singletonFactories

  • 存beanid与bean的对象工厂ObjectFactory映射,该工厂会通过目标bean创建代理对象ObjectFactory.getObject()方法只会被调用一次因为只会创建一个代理),创建的代理对象会放入二级缓存。

步骤,

  1. 创建bean A时,将刚实例化(未创建完成)的bean的工厂立即放入三级缓存beanFactory(beanA专属的Factory)。

  1. 后续在内部获取注入bean A时,在一级缓存找不到,二级缓存找不到,获取三级缓存的beanA的beanFactory,从beanFactory获取beanA的代理对象(如果不需要代理工厂就返回目标对象) 后立马放入二级缓存,然后返回给内部调用者。后续内部获取bean都从二级缓存获取代理对象。

什么情况下不能解决循环依赖?

Spring 如何解决循环依赖 - LARRY1024 - 博客园

  1. 构造器注入的依赖

因为bean都是需要实例化之后放入三级缓存才能解决问题的。加入现在有A和B互相依赖。

A构造注入B,那么在实例化A时,需要B。去缓存中找,找不到B那么就构造B,由于B也依赖了A,那么需要注入A,这时A并没有实例化完成(因为A的构造方法没调用完毕),所以无法解决循环依赖。

  1. Bean不为单例时不能解决循环依赖问题

spring里面有很多常用的这些注解。举一两个常用的,以及介绍一下,就是他们实现什么功能,以及它大概是怎么实现的话就更好。(阿里。。。难)

  1. autowired 和 resources
  2. bean和configuration
  3. component、controller、service
  4. configurationProperties(prefix)

拦截器和过滤器了解么?

四、SpringMVC实战:构建高效表述层框架

过滤器和拦截器的区别_拦截器和过滤器的区别-CSDN博客

都可以对请求进行拦截,统一处理请求。

区别

  1. 所属范围
    1. 过滤器工作在 Servlet 容器中 , 不由spring管理不能直接获取IOC中的bean
    2. 拦截器是Spring提供并管理的,可以获取IOC容器的bean
  1. 调用顺序
    1. 过滤器是请求进入servlet之前进行预处理的结束返回也是,是在servlet处理完后,返回给前端之前。
    2. 拦截器是执行controller方法之前进行拦截。
  1. 底层实现
    1. 过滤器的实现基于回调函数
    2. 拦截器实现是动态代理 基于反射
  1. 应用场景
    1. 过滤器 过滤敏感词汇(防止sql注入),我的项目中添加跨域的响应头(修改response)

    2. 拦截器 登录校验、权限校验、日志记录等。

这张图很重要,其中包括了filter、dispatcherServlet和拦截器

Spring 动态代理默认用哪一种

spring默认是jdk动态代理,用不了jdk动态代理才会用cglib。

BeanFactoryPostProcessor是干嘛的?(注意不是beanPostProcessor)

BeanFactory创建完成后,所有的Bean初始化之前。调用PostProcessor的PostProcessoBeanfactory方法,通常用于新增BeanDefinition

面试篇-06_前置知识_BeanFactoryPostProcessor_哔哩哔哩_bilibili

转发和重定向的区别?

  1. 重定向是两次请求,转发是同一次请求。
  2. 重定向可以让浏览器请求新的url,转发只是服务端将请求转发至 同一服务器 中的资源(前端url不会变)。
  3. 重定向的状态码是3xx,转发的话任然显示200。