谈谈Spring IOC
- ioc 控制反转,是一种设计思想,而不是具体实现
- 从原来应用代码创建对象,交由spring容器来管理对象的创建和管理,这样应用程序只需要关注业务逻辑便可。
- ioc使得对象的耦合度降低
IOC和DI有区别吗
- IOC最常见以及最合理的实现方式就是DI依赖注入: 将对象的依赖关系注入到对象内部来实现控制反转。
- 常见方式:
- setter: 通过类的setter方法来注入依赖项。
- 依赖field字段注入:使用@Autowried或@Resources依赖注入依赖项。
- 构造器注入:使用类的构造器来注入,推荐,避免空指针异常。
什么是SpringBean,作用域有哪些?
-
Bean就指那些被ioc容器所管理的对象
-
作用域:
- singleton: Ioc容器中唯一实例,对应的bean都是单例的,对应单例设计模式。
- propotype: 每次获取都会创建一个新的bean实例。
- request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
- session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
- application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
- websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
bean是线程安全的吗
- 取决于作用域和状态:
- propotype,每次获取都会创建新bean实例,不会有线程安全问题
- 几乎所有场景默认使用singleton, 如果bean有状态就会出现线程安全问题。有状态:类中包含可变的成员变量
- 解决方案:
- 尽量设计无状态的bean
- 使用ThreadLocal,将可变成员变量存在ThreadLocal中,保证线程独立。
- 利用Synchonrized 或 ReentrantLock 来保证线程安全。
Spring Bean的生命周期?
- 整体上:实例化-> 属性赋值 -> 初始化 -> 销毁
- 其中初始化步骤包含:Aware接口的依赖注入、BeanPostProcessor初始化前后的的处理、InitializingBean和init-method的初始化操作。
- 项目结合bean生命周期的操作
BeanDefinition的作用? 懒加载是什么?
- BeanDefinition 用来描述和封装一个bean的元信息的概念。包含了Spring容器再创建Bean对象所需要的全部信息,比如:Bean的Class类型,作用域,懒加载(Lazy Initialization)等。
- 懒加载:决定Bean是否应当延迟加载,如果设置了懒加载,Bean只会在第一次请求时创建实例,而不是容器启动时立即创建。
Spring的AOP有什么用?你项目中哪里用到了AOP?
-
AOP:面向切面编程,能够将与业务无关,却为业务模块所共同调用的逻辑和责任封装起来。(如日志管理、事务管理、权限管理等)减少系统的重复代码,降低模块耦合,提高系统的可拓展性和可维护性。
-
类型
- Before(前置通知):目标对象的方法调用之前触发
- After (后置通知):目标对象的方法调用之后触发
- AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发
- AfterThrowing(异常通知):目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
- Around (环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法
-
常见的:
- 基于AOP实现统一日志管理
- 基于Redssion+AOP实现接口防刷,一个注解即可实现限制单个用户访问接口的次数
- Spring Security 的@PreAuthor,也是基于AOP实现权限控制的。
- 基于@Transtional实现事务管理。
Spring事务管理了解吗? @Transcational有什么用? 项目中时如何使用的?
-
事务:逻辑上一组的操作,要么都执行,要么都不执行
-
ACID: 只有保证了原子性、隔离性、持久性才能保证一致性,也就是AID都是手段, C是目的。
- 原子性(
Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
- 一致性(
Consistency):执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的; - 隔离性(
Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的; - 持久性(
Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
- 原子性(
-
Spring支持两种事务管理:
- 编程式事务管理:通过TranscationTemplate和TransactionManager管理事务,实际很少使用。
- 声明式事务管理: 代码侵入性最小,推荐使用,通过AOP实现(基于@Transactional全注解的方式使用最多)
-
Spring事务管理最重要的三个接口:
-
PlantformTransactionManager: 事务管理器,Spring事务管理的核心
- 其他平台实现此接口,例如JDBC Hibernate
- 定义了3个方法:
- getTransaction(): 获取事务
- commit() 提交
- rollback() 回滚
-
TransactionDefinition: 事务定义信息(隔离级别,传播行为,超时,只读,回滚规则)
-
事务的传播行为:
- PROPAGATION_REQUIRED: 如果当前存在事务,就加入该事务;如果当前不存在事务,就创建新事务。
- PROPAGATION_REQUIRED_NEW: 创建一个新事务,如果已存在事务,就将此事务挂起
- PROPAGATION_NESTED: 如果当前存在事务,就创建一个事务作为当前事务的子事务执行。 父事务提交,子事务提交;父事务回滚,子事务回滚;子事务回滚,可以不引发父事务回滚。、
- PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果不存在,就抛出异常。
- TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
-
-
TransactionStatus:事务运行的状态。
-
-
@Transactional的常用参数
| 属性名 | 说明 |
|---|---|
| Propagation | 事务的传播行为,默认REQUIRED |
| isloation | 事务的隔离级别,默认DEFAULT |
| timeout | 事务超时,默认-1(不会超时),如果超过时间限制还没有执行完就自动回滚事务 |
| readOnly | 事务是否为只读事务,默认为false |
| rollbackFor | 指定触发回滚的异常类型,可以指定多个异常类型 |
Spring的循环依赖了解吗?怎么解决?
- 循环依赖:是Bean对象循环引用,两个或者多个对象互相持有对方的引用。
- 创建Bean对象的三级缓存:
- 先去一级缓存
SingletonObject获取,存在就返回 - 如果不存在或对象正在创建,则去二级缓存
earlySingletonObject获取 - 如果还没获取到,去三级缓存
SingletonFactories获取,通过执行objectFactory的getFactory()生成原始的Bean对象或者代理对象,获取成功后,从三级缓存中删除,放到二级缓存中
- 先去一级缓存
- 解决方案: 从三级缓存 SingletonFactories中获取执行objectFactory的getObject方法,获取此循环依赖对象的前期暴漏对象(虽然没有初始化完成,但是可以拿到对象在堆中的存储地址了),并且将这个前期暴露对象放入二级缓存,这样就解决了循环依赖,不会重复初始化了。
- Spring2.6.x之前允许循环依赖这种写法,即使出现也不会报错; Spring2.6.x之后默认不允许循环依赖,官方也不推荐了。
@Lazy能解决循环依赖吗?
- @Lazy标识的标识延迟加载/懒加载,可以作用在类上、方法上、构造器上、方法参数上、成员变量上。
- 懒加载一定程度上打破了循环依赖链,允许Spring容器顺利完成Bean的创建和注入。但不是根本上解决此问题,在构造器注入、更复杂场景中,懒加载并不能有效解决此问题。
- 最佳实践应该是避免循环依赖。
SpringMVC包含那些组件?当收到请求的流程是什么样的?
DispatcherServlet:核心的中央处理器,负责接收请求、分发,并给予客户端响应。HandlerMapping:处理器映射器,根据 URL 去匹配查找能处理的Handler,并会将请求涉及到的拦截器和Handler一起封装。HandlerAdapter:处理器适配器,根据HandlerMapping找到的Handler,适配执行对应的Handler;Handler:请求处理器,处理实际请求的处理器。ViewResolver:视图解析器,根据Handler返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给DispatcherServlet响应客户端
- 流程:
- 客户端发送请求,DispatcherServlet拦截请求
- DispatcherSerlvet根据请求调用HandlerMapping,根据URL查找能够处理的Handler
- DispatcherServlet根据HandlerAdapter适配器执行Handler
- Handler完成对用户的请求处理后,返回一个ModelAndView(模型和视图信息)对象给DispatcherSerlvet
- DispatcherServlet调用ViewResolver视图解析器解析view
- DispatcherServlet将的得到的Model交给View进行视图渲染
- 最后把View返回给客户端。
Spring\SpringMVC SpringBoot的区别?
- SpringMVC是spring的一个重要模块,主要还是赋予Spring快速构建MVC架构的Web程序。
- SpringBoot目的是简化Spring的开发(减少配置文件,开箱即用)
SpringBoot自动配置是如何实现的?
- SpringBoot的自动配置机制 通过 @EnableAutoConfiguration 启动。该注解利用 @Import 注解导入了 AutoConfigurationImportSelector类,而 AutoConfigurationImportSelector 了负责加载并管理所有的自动配置类。这些自动配置类通常在
META-INF/spring.factories文件中声明,并根据项目的依赖和配置条件,通过条件注解(如 @ConditionalOnClass @ConditionalOnBean等)判断是否应该生效。
如何使用Spring Boot实现全局异常处理
- 可以借助
@RestControllerAdvice和@Exceptionhandler实现全局异常统一处理。
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public Result businessExceptionHandler(HttpServletRequest request, BusinessException e){
...
return Result.faild(e.getCode(), e.getMessage());
}
}
- @RestControllerAdvice 是ControllerAdvice和@ResponseBody的结合体。
Spring的缺点
- Spring提供了 依赖注入和面向切面编程
- 虽然 组件代码 是轻量级的,但是配置重(需要大量的xml配置)
- 虽然 Spring2.5 引入了基于注解的 组件扫描,消除了 大量 基于针对应用程序自身组件的显式XML配置; Spring 3.0 引入基于Java的配置,这是一种安全的可重构配置方式 ,可以代替xml配置。
为什么要求springBoot?或者说 作用?
- 为了简化Spring的 开发部署过程,使得能够 专注 业务的逻辑的开发。
SpringBoot的优势:
- 提高生产力:SpringBoot的自动配置和开箱即用的功能,显著减少了手动配置和 样板代码的 编写 时间,使得开发者能够更快速地 构建和交付应用程序。
- 与 Spring生态系统的无缝衔接。
- 减少手动配置:提供了合理 的默认配置,大多数情况可以直接使用默认配置来去启动项目 。
- 嵌入式服务器:SpringBoot自带内嵌的 HTTP服务器 (tomcat,jetty)
- 适合微服务 架构: SpringBoot使得每个微服务都可以独立运行和配置。简化了微服务的开发、测试和运维 工作。
- 内置多插件:内置maven和gradle等工具。
SpringBoot Starters
- Spring Boot Starters是一组便捷的依赖描述符,它们预先打包了常用的库和配置。当我们开发Spring应用时,只需要添加一个Starter依赖项,即可自动引入所有必要的库和配置,而无需手动逐一添加和配置相关依赖。
-
常用的starter组件
- spring-boot-starter:基础的 Starter,包含了启动 Spring 应用所需的核心依赖,如 Spring 框架本身和日志系统(默认使用 SLF4J 和 Logback)。
- spring-boot-starter-web:用于构建 Web 应用程序,包括 RESTful 服务。它包含了 Spring MVC、Tomcat(默认嵌入式服务器)、Jackson 等依赖。
- spring-boot-starter-data-jpa:用于构建 JPA 应用程序,包含 Spring Data JPA 和 Hibernate。它简化了数据库访问层的开发,提供了对关系型数据库的便捷操作。
- spring-boot-starter-security:用于集成 Spring Security,提供身份验证和授权功能,帮助开发者快速实现安全机制。
- spring-boot-starter-test:提供测试所需的依赖,包含了 JUnit、Mockito、Spring Test 等库,帮助开发者编写单元测试、集成测试和 Mock 测试。
- spring-boot-starter-actuator:可以监控应用程序的运行状态,还可以收集应用程序的各种指标信息。
- spring-boot-starter-aop:提供对面向切面编程(AOP)的支持,包含 Spring AOP 和 AspectJ。
- spring-boot-starter-validation:集成了 Hibernate Validator,用于实现 Java Bean 的校验机制,通常与 Spring MVC 或 Spring Data 一起使用。
如何在springBoot应用程序中使用Jetty而不是Tomcat
- spring-boot-starter-web 使用Tomcat作为默认的嵌入式servlet 容器,如果想要使用需要修改pom.xml
<!--从Web启动器依赖中排除Tomcat-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加Jetty依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
SpringBoot 的SpringBootApplication注解
-
可以把@SpringBootApplication看作为 @Conofiguration @EnableAutoConfiguration @ComponentScan的 集合 。
- @EnableAutoConfiguration:启动SpringBoot自动配置机制
- @ComponentScan: 扫描被(@Controller @Service @Component)注解的 bean,注解默认会扫描该类 所在包下的所有类。
- @Configurtion: 允许上下文中注册额外的bean或 导入 其他 配置类。