一统java框架的Spring高频话术总结

Spring

什么是spring

spring是一个轻量级的java开发框架,解决企业应用开发的业务逻辑层和各层的耦合问题

为什么要使用spring

  • 属于低侵入式设计,代码的污染极低

  • Aop面向切面编程

  • IOC控制反转

  • 对于主流的应用框架提供了集成的支持

你们项目中为什么使用Spring框架?

这么问的话,就直接说Spring框架的好处就可以了。比如说Spring有以下特点:

轻量:Spring 是轻量的,基本的版本大约2MB。

控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找

依赖的对象们。

面向切面的编程**(AOP):**Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。

容器:Spring 包含并管理应用中对象的生命周期和配置。

MVC**框架**:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。

事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务

(JTA)。

异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛

出的)转化为一致的unchecked 异常。

说一下Spring中用到的设计模式

单例模式:Spring 中的 Bean 默认情况下都是单例的。无需多说。

工厂模式:工厂模式主要是通过 BeanFactory 和 ApplicationContext 来生产 Bean 对象。

代理模式:最常见的 AOP 的实现方式就是通过代理来实现,Spring主要是使用 JDK 动态代理和 CGLIB 代理。

模板方法模式:主要是一些对数据库操作的类用到,比如 JdbcTemplate、JpaTemplate,因为查询数据库的建立连接、执行查询、关闭连接几个过程,非常适用于模板方法。

spring中的两大核心

AOP

aop是面向切面编程,用于将一些与业务无关的,但却对多个对象产生影响的公共行为和逻辑,抽取出来封装成一个模块,而这个模块就叫做切面。优点:减少系统中的重复代码,降低了模块间的耦合度,提高了系统的可维护性。主要用权限认证、日志、事务处理

aop的实现关键主要在于代理模式,分为JDK动态代理和CGLIB动态代理

JDK动态代理和CGLIB动态代理对比

  • JDK动态代理值提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler通过的是invoke()方法反射来代用目标类中的代码的。动态将逻辑和业务绑定在一起 。Proxy是利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象

  • 如果代理类中没实现InvocationHandler接口,那么Aop会选择使用CGLIB来动态代理目标类。CGLIB是一个代码生成的类库,可以在运行时动态生成指定类的一个子类对象,并且覆盖其中特定的方法来实现的aop。CGLIB是通过继承的方式做的动态代理。所以如果某各类被 标记为final,那么他是无法使用CGLIB代理的

aop中切面、切点、连接点、通知之间的关系

1、切面:就是将公共行为封装成模块之后的类

2、切点:描述何时执行通知的规则

3、连接点:被切点匹配执行通知的位置

4、通知:是程序公共行为执行的代码操作

IOC

  • IOC是控制反转,指的是创建对象的控制权利的转移,以前创建对象的主动权是自己,而现在 是将主动权交给了spring去管理,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,有利用功能的复用。DI依赖注入,和IOC是同一个概念的不同角度的描述,一个程序在运行时依赖IOC容器来动态注入对象 需要的外部资源

  • IOC就是 创建对象的时候不用在去new了,可以通过spring自动生产,使用java的反射机制,根据配置文件运行时动态去创建和管理,之后再去调用

  • spring的IOC是由三种注入方式:构造器注入、set方法注入、根据注解注入

Spring Bean 的作用域范围

1、singleton(单例模式):一个bean中只有一个实例 (默认)

2、prototype:一个bean的定义可以有多 个实例

3、request

4、session

5、global Session

springMVC 的原理

客户端发起请求到dispatchservlet,dispatchservlet根据请求调用handlerMappering,解析出一个对应的handler,解析出的handler由handlerAdapter适配器去处理完成之后得到对应的moudleAndView,最后将view返回给客户

Spring事务

数据库不支持事务

注解放在了私有方法上

类内部调用

未捕获异常

多线程场景

spring的传播特性

传播行为(PROPAGATION) 描述
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
PROPAGATION_SUPPORTS 支持当前事务,如果没有当前事务,就以非事务方法执行。
PROPAGATION_MANDATORY 使用当前事务,如果没有当前事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行操作,如果当前事务存在则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED 类似的操作

负载均衡之feign与ribbon比较

  • feign本身就包含了ribbon

  • feign自身是一个声明式的伪http客户端,写起来更加思路清晰和方便

  • feign是采用接口的注解编程方式

使用feign造成的问题:

一般在feign调用相应的service接口时候,第一次调用会因为超时而导致调用失败,所以需要设置超时时长

spring 中 BeanFactory 和 FactoryBean 的区别

首先呢,beanFactory是一个factory工厂,factoryBean是一个特殊的bean接口

beanfactory是一个IOC的特殊工厂,是IOC的一个基本接口,他主要是用来管理和创建bean的实例 的,它提供了通过名字去创建bean的实例、判断此类是否在工厂中以及判断是否为单例模式

factorybean是一个特殊的bean接口,他主要有三个方法:getbean(),返回实现factorybean的类接口创建bean的实例。getbojecttype()返回该实例的类型,isSingleton()判断是否为单例模式

AOP的应用场景有哪些呢

1、日志记录

2、权限验证

3、效率检查(个人在代码上,喜欢用注解+切面,实现校验,redis分布式锁等功能)

4、事务管理(spring 的事务就是用AOP实现的)

Spring Boot 的底层原理

Spring Boot 底层实际上主要的有三个注解:

configuration

EnableAutoConfiguation

ComponentScan

读取 mate-inf下的 spring.factories 文件信息

约定大于配置 自动装配

Spring Bean的默认作用域为:singleton。它相比其他作用域的优点是系统开销小,Bean实例一旦创建成功便可重复使用。

prototype

request

session

global session

SpringBean 生命周期

  1. 实例化,创建一个Bean对象

  2. 填充属性,为属性赋值

  3. 初始化

    • 如果实现了xxxAware接口,通过不同类型的Aware接口拿到Spring容器的资源

    • 如果实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzationpostProcessAfterInitialization方法

    • 如果配置了init-method方法,则会执行init-method配置的方法

  4. 销毁

    • 容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy方法

    • 如果配置了destroy-method方法,则会执行destroy-method配置的方法

###

Spring中循环依赖的解决方案

  1. 使用context.getBean(A.class),获取容器内的单例A(若A不存在,就会走A这个Bean的创建流程),显然初次获取A是不存在的,因此走A的创建之路~

  2. 实例化A(注意此处仅仅是实例化),并将它放进缓存(此时A已经实例化完成,已经可以被引用了)

  3. 初始化A:@Autowired依赖注入B(此时需要去容器内获取B)

  4. 为了完成依赖注入B,会通过getBean(B)去容器内找B。但此时B在容器内不存在,就走向B的创建之路~

  5. 实例化B,并将其放入缓存。(此时B也能够被引用了)

  6. 初始化B,@Autowired依赖注入A(此时需要去容器内获取A)

  7. 此处重要:初始化B时会调用getBean(A)去容器内找到A,上面我们已经说过了此时候因为A已经实例化完成了并且放进了缓存里,所以这个时候去看缓存里是已经存在A的引用了的,所以getBean(A)能够正常返回

  8. B初始化成功(此时已经注入A成功了,已成功持有A的引用了),return(注意此处return相当于是返回最上面的getBean(B)这句代码,回到了初始化A的流程中~)。

  9. 因为B实例已经成功返回了,因此最终A也初始化成功

  10. 到此,B持有的已经是初始化完成的A,A持有的也是初始化完成的B

    我们开始三级缓存,首先通过A进入已经缓存,去寻找发现没有初始化的B(并且他使用了singlobject 提前曝光了自己) 在去二级缓找发现没有B,就去开启了创建B,

    B开始从一级缓存寻找A发现没有A,但是鱿鱼我们的A刚刚提前曝光了自己,我们B可以获取到A(但是这个A并不是初始化的而是实例化的但是A可以被引用)我们B待着A去三级缓存最实例化了,现在我们的B是实例化的,我回到刚刚的A在去获取b发现可以获得一个实例化好的B,然后在去三级缓存最后把自己也成功了实例化了这个时候我们的A和B都已经实例化过来就不会出现循环依赖的问题了

    spring解决循环依赖

    这是别人总结的三级缓存

    第一级缓存:用来保存实例化、初始化都完成的对象

    第二级缓存:用来保存实例化完成,但是未初始化完成的对象

    第三级缓存:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象

    假设一个简单的循环依赖场景,A、B互相依赖。

    A对象的创建过程:

    1. 创建对象A,实例化的时候把A对象工厂放入三级缓存

    1. A注入属性时,发现依赖B,转而去实例化B

    2. 同样创建对象B,注入属性时发现依赖A,一次从一级到三级缓存查询A,从三级缓存通过对象工厂拿到A,把A放入二级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入一级缓存。

    1. 接着继续创建A,顺利从一级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除二级缓存中的A,同时把A放入一级缓存

    2. 最后,一级缓存中保存着实例化、初始化都完成的A、B对象

    因此,由于把实例化和初始化的流程分开了,所以如果都是用构造器的话,就没法分离这个操作,所以都是构造器的话就无法解决循环依赖的问题了。

    为什么要三级缓存?二级不行吗

    不可以,主要是为了生成代理对象。

    因为三级缓存中放的是生成具体对象的匿名内部类,他可以生成代理对象,也可以是普通的实例对象。

    使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。

    假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,BeanPostProcessor去生成代理对象之后,覆盖掉二级缓存中的普通Bean对象,那么多线程环境下可能取到的对象就不一致了。

Spring框架中的单例Beans是线程安全的么

谈到beans线程安全那么就看bean是否有多种状态,如果始终只有一种状态 就是线程安全的,否则需要自己去保证线程的安全,可以采用将singleton变为prototype

@autowired和@resource注解的区别是什么

autowired是按照bytype注入的,是由spring提供的,resource是j2ee提供的,按照名字去注入的

如果一个接口有多个实现,那么采用Qualifier

什么时候spring事务不生效

猜你喜欢

转载自blog.csdn.net/Fristm/article/details/125607357