Spring概述
使用Spring框架的好处?
好处 | 说明 |
---|
轻量 | 轻量的,基本的版本大约2MB。 | 控制反转IoC | 通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。 | 面向切面的编程(AOP) | 支持面向切面的编程,并且把应用业务逻辑和系统服务分开。 | 容器 | 包含并管理应用中对象的生命周期和配置。 | MVC框架 | Web框架是个精心设计的框架,是 Web框架的一个很好的替代品。 | 事务管理 | 提供一个持续的事务管理接口,可以扩展到 上至本地事务 下至全局事务(JTA)。 | 异常处理 | 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常 |
架构是什么?
A 表现层 web层 —— MVC是表现层的一个设计模型 B 业务层 service层 C 持久层 dao层
控制反转IOC
你对IOC的理解?
IOC = inversion of control 控制反转。 借助ioc容器实现具有依赖关系的对象之间的解耦。
-
作用 在于解耦,可以解除对象之间的耦合,让对象和对象之间完全没有联系,这样完成或修改一个对象时不需要考虑其他对象。 -
出现原因 java是面向对象的语言,应用程序需要通过一个个对象之间的关联和作用来完成功能。 这样对象之间紧密的咬合形成的系统会具有较高的耦合度。导致一个对象出现问题,整个系统无法工作。 加入ioc容器,会使得对象之间的耦合性降低,修改一个对象不会对其他对象造成影响。 -
原理 没有ioc容器的时候,对象A依赖对象B,A在运行到某一时刻的时候回去创建B的对象,这样A具有主动权,它控制了对象B的创建; 引入ioc以后对象A和对象B之间就没有直接的联系,当对象A运行的时候由ioc容器创建B对象在适当的时候注入到对象A中, 这样对象B的控制权就由A对象转移到了ioc容器。
控制反转IOC 和 依赖注入(DI)关系?
控制反转是一种设计思想,依赖注入是该思想的具体实现。 例子:(创建userServiceImpl类中,有UserDao类的对象) 控制反转 = 将创建UserDao类的userDaoImpl对象的控制反转过来由userServiceImpl类交给ioc容器,强调的是一种能力和思想; 依赖注入 = ioc容器将UserServiceImpl所依赖的userDaoImpl,注入给userServiceImpl,强调的是一个过程和实现。
Spring常用的注入方式有哪些?
Spring通过依赖注入(DI)实现IOC(控制反转)。常用的注入方式主要有:构造方法注入、setter注入、基于注解的注入。 参考
Bean
Bean的理解?
在 Spring 中,构成应用程序主干 并由Spring IoC容器 管理的对象称为bean。 bean是一个由Spring IoC容器实例化、组装和管理的对象。
理解: bean是一个对象,一个或多个; bean由spring ioc容器管理; bean是应用程序的主干,也就是程序由bean组成;
spring容器会自动完成对@bean对象的实例化。 创建应用程序对象之间的协作关系的行为称为:装配(wiring)、即依赖注入的本质。
Bean的生命周期?
分为六个阶段: 定义
实例化(Instantiation)
属性设置(populate)
初始化(initialization)
生存期
销毁(destruction)
传统的Java应用中,bean的生命周期很简单,使用Java关键字 new 进行Bean 的实例化,然后该Bean 就能够使用了。一旦bean不再被使用,则由Java自动进行垃圾回收。
Bean的作用域?
bean的作用域可以通过scope属性来指定。 singleton:默认值。当IOC容器一创建就会创建bean的实例,而且是单例的,每次得到同一个。 prototype:原型的。当IOC容器创建时不会立即实例化该bean,每次调用getBean方法时再实例化该bean,而且每次调用都会返回一个新的实例。 request:每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境。 session:同一个HTTP Session 共享一个Bean,不同的 HTTP Session 使用不同的Bean。该作用域仅适用于WebApplicationContext环境。
原文链接:https://blog.csdn.net/weixin_44001568/article/details/111317836
Spring中的单例Bean的线程安全问题?
单例 bean 存在线程问题。spring默认的bean是单例的,也没有进行封装处理。 现象 当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。
解决
- 使用ThreadLocal,把变量变成线程私有的
- synchronized、lock、CAS,保证互斥访问临界区
- 把bean从单例(singleton)设置成原型(protopyte)
ApplicationContext通常的实现有哪些?
ApplicationContext,是Spring中的核心接口和容器,允许容器通过应用程序上下文环境创建、获取、管理bean。 在构建容器的时候,创建对象采用的策略是立即加载的方式,即只要一读取完配置文件就立即创建配置文件中配置的对象。BeanFactory采用的是延迟加载的方式,什么时候根据id获取对象了,什么时候才真正地创建对象。
实现名 | 说明 |
---|
FileSystem XmlApplicationContext | 此容器从一个XML文件中加载beans的定义,XML Bean配置文件的全路径名必须提供给它的构造函数。 | ClassPath XmlApplicationContext | 此容器也从一一个XML文件中加载beans的定义,需要正确设置classpath,因为这个容器将在classpath里找bean配置。 | We bXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。 | |
AOP
解释下什么是AOP?
AOP(Aspect-Oriented Programming)面向方面编程。
概念 Spring AOP 是基于 AOP 编程模式的一个框架,它能够有效的减少系统间的重复代码,达到松耦合的目的。 它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。该模块 = 与业务无关,却把业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
出现原因 是对OOP(Object-Oriented Programming)面向对象编程的补充和完善。 OOP 引入封装、继承和多态性等概念来建立一种对象层次结构,用来模拟公共行为的一个集合。
OOP 允许定义从上到下的关系,但并不适合定义从左到右的关系。 例如日志功能,日志代码往往水平地散布在所有对象层次中,而与它所散布到对象的核心功能毫无关系。 这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在 OOP 设计中,它导致了大量代码的重复,而不利于各个模块的重用。
组成 使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。 业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。 横切关注点的特点是,它们经常发生在核心关注点的多处,而各处都基本相似。比如:权限认证、日志、事务处理。 AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
作用 面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足。 利用AOP对业务逻辑的各个部分进行隔离,降低业务逻辑的耦合性,提高程序的可重用性和开发效率。 主要用于对同一对象层次的公用行为建模。
AOP的代理有哪几种方式?
两种方式:静态代理(AspectJ)、动态代理(基于接口的JDK代理 、 基于继承的CGLIB动态代理)。 具体使用哪种方式生成由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口,则使用 JDK 动态代理技术,否则使用 Cglib 来生成代理。
AspectJ 是静态代理的增强,所谓的静态代理就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强。 JDK动态代理 通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。 JDK动态代理的核心是InvocationHandler接口和Proxy类。 Spring默认的动态代理方式就是JDK动态代理。 如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。
CGLIB(Code Generation Library) 是一个代码生成的类库,可以在运行时动态的生成某个类的子类。 CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
怎么实现JDK动态代理?
JDK动态代理
CGLIB代理
AOP的基本概念:切面、连接点、切入点等?
- 横切关注点:
跨越应用程序多个模块的方法和功能,与业务逻辑无关的,但又需要关注的部分。 - 连接点(Joinpoint):
指那些被拦截的点,在spring中,指可以被动态代理拦截目标类的方法。 - 切入点(Pointcut):
指要对哪些joinpoint进行拦截,即被拦截的点。 - 通知(Advice):
指拦截到joinpoint之后要做的事情,即对切入点增强的内容。 - 目标(Target):
指代理的目标对象; - 植入(Weaving):
指把增强代码应用到目标上,生成代理对象的过程。 - 代理(Proxy):
指生成的代理对象; - 切面(Aspect):
切入点和通知的结合。
常见的通知类型?
类型 | 说明 |
---|
前置通知(Before advice) | 在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。 ApplicationContext 中在 < aop:aspect > 里面使用 < aop:before > 元素进行声明; | 后置通知(After advice) | 当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。 ApplicationContext 中在 < aop:aspect > 里面使用 < aop:after > 元素进行声明。 | 返回后通知(After return advice ) | 在某连接点正常完成后执行的通知,不包括抛出异常的情况。 ApplicationContext 中在 < aop:aspect > 里面使用 << after-returning >> 元素进行声明。 | 环绕通知(Around advice) | 包围一个连接点的通知,类似 Web 中 Servlet规范中的 Filter 的 doFilter 方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。 ApplicationContext 中在 < aop:aspect > 里面使用 < aop:around > 元素进行声明。 | 抛出异常后通知(After throwing advice) | 在方法抛出异常退出时执行的通知。 ApplicationContext 中在 < aop:aspect > 里面使用 < aop:after-throwing > 元素进行声明。 |
事务处理
你对Spring中的事务的理解?
本质: 是数据库对事物的支持,没有数据库的事务支持,spring是无法提供事务功能的。
事务是逻辑上的一组操作,要么都执行,要么都不执行。 事务的特性:原子性(事务执行的最小单位,要么全部执行或不执行)、一致性(事务执行前后,数据保持一致)、隔离性(并发访问,一个用户的事务不被其他事务影响)、持久性(一旦事务提交,修改内容被永久性保存在数据库)。
事务管理 按照给定的事务规则来执行提交或者回滚操作; 接口: PlatformTransactionManager:平台事务管理器; TransactionDefinition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则); TransactionStatus:事务运行状态;
Spring中事务的隔离级别?
TransactionDefinition接口中定义了五个表示隔离级别的常量:
隔离级别 | 说明 |
---|
TransactionDefinition.lSOLATION_DEFAULT | 使用后端数据库默认的隔离级别, MySQL默认采用的REPEATABLE_READ隔离级别(可重复读), Oracle 默认采用的READ_COMMITTED隔离级别(读已提交) | TransactionDefinition.ISOLATION_READ_UNCOMMITTED | 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读; | TransactionDefinition.ISOLATION_READ_COMMITTED | 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生; | TransactionDefinition.lSOLATION_REPEATABLE_READ | 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生; | TransactionDefinition.ISOLATION_SERIALIZABLE | 最高的隔离级别,完全服从ACID的隔离级别。 所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,该级别可以防止脏读、不可重复读以及幻读。 但是这将严重影响程序的性能。通常情况下也不会用到该级别。 |
Spring中的事务传播行为?
作用 为了解决业务层方法之间互相调用的事务问题。 当一个事务方法(当前方法B)被另一个事务方法(调用者A)调用时,该方法B对另一个事务方法A的态度
在TransactionDefinition定义的传播行为常量
支持当前事务 | 说明 |
---|
TransactionDefinition.Propagation_required | 当前事务方法B 必须在事务中运行,事务可以是调用者A的 或者 自己新开启的事务 | 调用者是否有事务 | 调用者A有事务——当前方法B加入调用者A事务中 调用者A没有事务——当前方法B自己新开启一个事务运行 | 调用者调用完成,抛出业务异常,如何回滚 | 调用者A抛出异常——方法B和调用者A一起回滚 调用者A抛出异常且已执行的SQL不回滚——当前方法B不回滚 | 当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚 | 调用者A提交事务抛出异常——调用者A和当前方法B在同一事务中,一起回滚 (不允许存在) 调用者以非事务形式运行——当前方法B回滚 | 当前方法出现异常,调用者不捕获当前方法异常,怎么回滚 | 调用者抛出当前方法异常——当前方法和调用者在同一个事务中一起回滚 调用者非事务形式运行,抛出当前方法异常,已执行的SQL不回滚——当前方法的事务回滚 | TransactionDefinition.Propagation_supports | 当前方法B 不必在事务中运行 | 调用者是否有事务 | 调用者A有事务——当前方法B加入调用者A事务中 调用者A没有事务——当前方法B以非事务的形式运行 | 调用者调用完成,抛出业务异常,如何回滚 | 调用者A抛出异常——当前方法B和调用者A一起回滚 调用者A抛出异常且已执行的SQL不回滚——当前方法B已执行的SQL不回滚 | 当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚 | 调用者提交事务抛出异常——调用者和方法B一起回滚 调用者A以非事务的形式运行——当前方法已执行的SQL不回滚 | 当前方法出现异常,调用者不捕获当前方法异常,怎么回滚 | 调用者抛出当前方法异常——调用者和当前方法一起回滚 调用者抛出当前方法异常且已执行的SQL不回滚——当前方法已执行的SQL不回滚 | TransactionDefinition.Propagation_mandatory | 当前事务方法B 必须在调用者事务中运行 | 调用者是否有是否 | 调用者A有事务——将事务方法B加入调用者A事务中 调用者A没有事务——当前方法B抛出异常 | 调用者调用完成,抛出业务异常,如何回滚 | 调用者抛出异常——当前方法B和调用者A一起回滚 调用者调用当前方法抛出异常(不允许该场景发生) | 当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚 | 调用者提交事务发生异常——调用者和当前方法B一起回滚(不允许存在) 调用者调用当前方法时抛出异常——调用者已运行的SQL不回滚 | 当前方法出现异常,调用者不捕获当前方法异常,怎么回滚 | 调用者抛出当前方法异常——调用者和当前方法一起回滚 调用者调用方法抛出异常且已执行的SQL不回滚(不允许该场景产生) | 非事务形式 | 非事务形式就是设置了自动提交,一个方法中有多个操作,每个操作都会在不同的事务中完成,不会保证他们的原子性。 |
不支持当前事务 | 说明 |
---|
TransactionDefinition.Propagation_required_new | 当前方法B必须在新事务中运行 | 调用者否是有事务 | 调用者A有事务需要挂起——当前方法B自己新开一个事务运行 调用者A没有事务——当前方法B自己开启一个新事务运行 | 调用者调用完成,抛出业务异常,如何回滚 | 调用者A抛出异常且回滚——当前方法B不回滚 调用者A抛出异常且已执行的SQL不回滚——当前B不回滚 | 当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚 | 调用者A以事务的形式正常执行——当前方法B抛出异常且回滚 调用者A非事务形式运行——当前方法B抛出异常且回滚 | 当前方法出现异常,调用者不捕获当前方法异常,怎么回滚 | 调用者抛出异常且回滚——当前方法抛出异常且回滚 调用者抛出异常且已执行的SQL不回滚——当前方法抛出异常且回滚 | TransactionDefinition.Propagation_not_supported | 当前方法B 不支持在事务中运行 | 调用者是否有事务 | 调用者A有事务方法执行期间挂起——当前方法B以非事务的形式运行 调用者A没有事务——当前方法B以非事务形式运行 | 调用者调用完成,抛出业务异常,如何回滚 | 调用者A抛出异常且回滚——当前方法B已执行的SQL不回滚 调用者A抛出异常且已执行SQL不回滚——当前方法B已执行的SQL不回滚 | 当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚 | 调用者A正常执行——当前方法B抛出异常且已执行的SQL不回滚 调用者A正常执行——当前方法B抛出异常且已执行的SQL不回滚 | 当前方法出现异常,调用者不捕获当前方法异常,怎么回滚 | 调用者抛出异常且回滚——当前方法抛出异常且已执行的SQL不回滚 调用者抛出异常且已执行的SQL不回滚 当前方法抛出异常且已执行的SQL不回滚 | TransactionDefinition.Propagation_never | 调用者必须以非事务的形式运行 | 调用者是否有事务 | 调用者A有事务——抛出异常 调用者A没有事务——当前方法B以非事务的形式运行 | 调用者调用完成,抛出业务异常,如何回滚 | 调用者调用当前方法抛出异常(不允许存在该场景) 调用者A抛出异常且已执行的SQL不回滚——当前方法B已执行的SQL不回滚 | 当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚 | 调用者A抛出异常 该场景不存在 调用者A以非事务的形式正常执行——当前方法B已执行的SQL不回滚 | 当前方法出现异常,调用者不捕获当前方法异常,怎么回滚 | 调用者调用当前方法抛出异常(不允许存在该场景) 调用者抛出异常且已执行的SQL不回滚——当前方法抛出异常且已执行的SQL不回滚 |
其他情况 | 说明 |
---|
TransactionDefinition.Propagation_nested | 当前方法B 必须在自己的事务中运行 | 调用者是否有事务 | 调用者A有事务——当前方法B以“嵌套事务”的形式加入到调用者A的事务中 调用者A没有事务——当前方法B自己新开启一个事务运行 | 调用者调用完成,抛出业务异常,如何回滚 | 调用者A抛出异常——调用者和当前方法一起回滚 调用者抛出异常且已执行的SQL语句不回滚——当前方法B不回滚 | 当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚 | 调用者A正常运行——当前方法B以嵌套事务回滚 调用者以非事务形式运行——当前方法的事务回滚 | 当前方法出现异常,调用者不捕获当前方法异常,怎么回滚 | 调用者抛出异常且回滚——当前方法嵌套事务回滚 调用者抛出当前方法异常且已执行的SQL不回滚 |
Spring框架中用到了哪些设计模式?
设计模式 | 说明 |
---|
厂设计模式 | Spring使用工厂模式通过BeanFactory、ApplicationContext 创建bean对象; | 代理设计模式 | Spring AOP功能的实现 | 单例设计模式: | Spring中的Bean默认都是单例的 | 模板方法模式 | Spring中jdbcTemplate、hibernate’ Template等以Template结绳的对数据库操作的类 | 包装器设计模式 | 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源 | 观察者模式 | Spring 事件驱动模型就是观察者模式很经典的一个应用 | 适配器模式 | Spring AOP的增强或通知(Advice)使用到了适配器模式、SpringMVC 中也是用到了适配器模式适配Controller |
Spring如何解决循环依赖问题?
循环依赖 = 多个bean之间相互依赖,形成了一个闭环。 循环依赖是Spring容器注入时候出现的问题。 参考
|