23.Spring 事务的种类?
Spring 支持编程式事务
管理和声明式
事务管理两种方式:
- 编程式事务
编程式事务管理使用 TransactionTemplate,需要显式执行事务。
- 声明式事务
- 声明式事务管理建立在 AOP 之上的。其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务
- 优点是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过 @Transactional 注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
24.Spring 的事务隔离级别?
Spring的接口TransactionDefinition中定义了表示隔离级别的常量,当然其实主要还是对应数据库的事务隔离级别:
- ISOLATION_DEFAULT:使用后端数据库默认的隔离界别,MySQL 默认可重复读,Oracle 默认读已提交。
- ISOLATION_READ_UNCOMMITTED:读未提交
- ISOLATION_READ_COMMITTED:读已提交
- ISOLATION_REPEATABLE_READ:可重复读
- ISOLATION_SERIALIZABLE:串行化
25.Spring 的事务传播机制?
Spring 事务的传播机制说的是,当多个事务同时存在的时候——一般指的是多个事务方法相互调用时,Spring 如何处理这些事务的行为。
事务传播机制是使用简单的 ThreadLocal 实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。

Spring默认的事务传播行为是PROPAFATION_REQUIRED,它适合绝大多数情况,如果多个ServiceX#methodX()都工作在事务环境下(均被Spring事务增强),且程序中存在调用链Service1#method1()->Service2#method2()->Service3#method3(),那么这3个服务类的三个方法通过Spring的事务传播机制都工作在同一个事务中。
26.声明式事务实现原理了解吗?
就是通过AOP/动态代理。
- 在Bean初始化阶段创建代理对象:Spring容器在初始化每个单例bean的时候,会遍历容器中的所有BeanPostProcessor实现类,并执行其postProcessAfterInitialization方法,在执行AbstractAutoProxyCreator类的postProcessAfterInitialization方法时会遍历容器中所有的切面,查找与当前实例化bean匹配的切面,这里会获取事务属性切面,查找@Transactional注解及其属性值,然后根据得到的切面创建一个代理对象,默认是使用JDK动态代理创建代理,如果目标类是接口,则使用JDK动态代理,否则使用Cglib。
- 在执行目标方法时进行事务增强操作:当通过代理对象调用Bean方法的时候,会触发对应的AOP增强拦截器,声明式事务是一种环绕增强,对应接口为
MethodInterceptor
,事务增强对该接口的实现为TransactionInterceptor
,类图如下:

- 事务拦截器
TransactionInterceptor
在invoke
方法中,通过调用父类TransactionAspectSupport
的invokeWithinTransaction
方法进行事务处理,包括开启事务、事务提交、异常回滚。
27.声明式事务在哪些情况下会失效?
1、@Transactional 应用在非 public 修饰的方法上
如果Transactional注解应用在非 public 修饰的方法上,Transactional将会失效。
是因为在Spring AOP 代理时,TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的intercept方法 或 JdkDynamicAopProxy的invoke方法会间接调用AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute方法,获取Transactional 注解的事务配置信息。
此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。
2、@Transactional 注解属性 propagation 设置错误
- TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
3、@Transactional 注解属性 rollbackFor 设置错误
rollbackFor 可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查unchecked异常(继承自 RuntimeException的异常)或者 Error才回滚事务,其他异常不会触发回滚事务。

若在目标方法中抛出的异常是 rollbackFor 指定的异常的子类,事务同样会回滚。
4、同一个类中方法调用,导致@Transactional失效
开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。
那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
//@Transactional
@GetMapping("/test")
private Integer A() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
/**
* B 插入字段为 3的数据
*/
this.insertB();
/**
* A 插入字段为 2的数据
*/
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert;
}
@Transactional()
public Integer insertB() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("3");
cityInfoDict.setParentCityId(3);
return cityInfoDictMapper.insert(cityInfoDict);
}
这种情况是最常见的一种@Transactional注解失效场景
@Transactional
private Integer A() throws Exception {
int insert = 0;
try {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
cityInfoDict.setParentCityId(2);
/**
* A 插入字段为 2的数据
*/
insert = cityInfoDictMapper.insert(cityInfoDict);
/**
* B 插入字段为 3的数据
*/
b.insertB();
} catch (Exception e) {
e.printStackTrace();
}
}
如果B方法内部抛了异常,而A方法此时try catch了B方法的异常,那这个事务就不能正常回滚了,会抛出异常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

MVC
28.Spring MVC 的核心组件?
- DispatcherServlet:前置控制器,是整个流程控制的核心,控制其他组件的执行,进行统一调度,降低组件之间的耦合性,相当于总指挥。
- Handler:处理器,完成具体的业务逻辑,相当于 Servlet 或 Action。
- HandlerMapping:DispatcherServlet 接收到请求之后,通过 HandlerMapping 将不同的请求映射到不同的 Handler。
- HandlerInterceptor:处理器拦截器,是一个接口,如果需要完成一些拦截处理,可以实现该接口。
- HandlerExecutionChain:处理器执行链,包括两部分内容:Handler 和 HandlerInterceptor(系统会有一个默认的 HandlerInterceptor,如果需要额外设置拦截,可以添加拦截器)。
- HandlerAdapter:处理器适配器,Handler 执行业务方法之前,需要进行一系列的操作,包括表单数据的验证、数据类型的转换、将表单数据封装到 JavaBean 等,这些操作都是由 HandlerApater 来完成,开发者只需将注意力集中业务逻辑的处理上,DispatcherServlet 通过 HandlerAdapter 执行不同的 Handler。
- ModelAndView:装载了模型数据和视图信息,作为 Handler 的处理结果,返回给 DispatcherServlet。
- ViewResolver:视图解析器,DispatcheServlet 通过它将逻辑视图解析为物理视图,最终将渲染结果响应给客户端
29.Spring MVC 的工作流程?

- 客户端向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet(也叫中央控制器)。
- DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该请求该由哪个Controller来处理(并未调用Controller,只是得知)
- DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller
- HandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视图),并层层返回给DispatcherServlet
- DispatcherServlet将ModelAndView交给ViewReslover视图解析器解析,然后返回真正的视图。
- DispatcherServlet将模型数据填充到视图中
- DispatcherServlet将结果响应给客户端
Spring MVC 虽然整体流程复杂,但是实际开发中很简单,大部分的组件不需要开发人员创建和管理,只需要通过配置文件的方式完成配置即可,真正需要开发人员进行处理的只有 Handler(Controller) 、View 、Model。
当然我们现在大部分的开发都是前后端分离,Restful风格接口,后端只需要返回Json数据就行了。
30.SpringMVC Restful风格的接口的流程是什么样的呢?
我们都知道Restful接口,响应格式是json,这就用到了一个常用注解:@ResponseBody
@GetMapping("/user")
@ResponseBody
public User user(){
return new User(1,"张三");
}
加入了这个注解后,整体的流程上和使用ModelAndView大体上相同,但是细节上有一些不同:

- 客户端向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet
- DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该请求该由哪个Controller来处理
- DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller
- Controller被封装成了ServletInvocableHandlerMethod,HandlerAdapter处理器适配器去执行invokeAndHandle方法,完成对Controller的请求处理
- HandlerAdapter执行完对Controller的请求,会调用HandlerMethodReturnValueHandler去处理返回值,主要的过程:5.1. 调用RequestResponseBodyMethodProcessor,创建ServletServerHttpResponse(Spring对原生ServerHttpResponse的封装)实例5.2.使用HttpMessageConverter的write方法,将返回值写入ServletServerHttpResponse的OutputStream输出流中5.3.在写入的过程中,会使用JsonGenerator(默认使用Jackson框架)对返回值进行Json序列化
- 执行完请求后,返回的ModealAndView为null,ServletServerHttpResponse里也已经写入了响应,所以不用关心View的处理
Spring Boot
31.介绍一下SpringBoot,有哪些优点?
Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。

Spring Boot 以约定大于配置
核心思想开展工作,相比Spring具有如下优势:
- Spring Boot 可以快速创建独立的Spring应用程序。
- Spring Boot 内嵌了如Tomcat,Jetty和Undertow这样的容器,也就是说可以直接跑起来,用不着再做部署工作了。
- Spring Boot 无需再像Spring一样使用一堆繁琐的xml文件配置。
- Spring Boot 可以自动配置(核心)Spring。SpringBoot将原有的XML配置改为Java配置,将bean注入改为使用注解注入的方式(@Autowire),并将多个xml、properties配置浓缩在一个appliaction.yml配置文件中。
- Spring Boot 提供了一些现有的功能,如量度工具,表单数据验证以及一些外部配置这样的一些第三方功能。
- Spring Boot 可以快速整合常用依赖(开发库,例如spring-webmvc、jackson-json、validation-api和tomcat等),提供的POM可以简化Maven的配置。当我们引入核心依赖时,SpringBoot会自引入其他依赖。
32.SpringBoot自动配置原理了解吗?
SpringBoot开启自动配置的注解是@EnableAutoConfiguration
,启动类上的注解@SpringBootApplication
是一个复合注解,包含了@EnableAutoConfiguration:

EnableAutoConfiguration
只是一个简单的注解,自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector
类
@AutoConfigurationPackage //将main同级的包下的所有组件注册到容器中
@Import({AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfiguration
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
AutoConfigurationImportSelector
实现了ImportSelector
接口,这个接口的作用就是收集需要导入的配置类,配合@Import()
就可以将相应的类导入到Spring容器中- 获取注入类的方法是selectImports(),它实际调用的是
getAutoConfigurationEntry
,这个方法是获取自动装配类的关键,主要流程可以分为这么几步:- 获取注解的属性,用于后面的排除
- 获取所有需要自动装配的配置类的路径:这一步是最关键的,从META-INF/spring.factories获取自动配置类的路径
- 去掉重复的配置类和需要排除的重复类,把需要自动加载的配置类的路径存储起来
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
//1.获取到注解的属性
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//2.获取需要自动装配的所有配置类,读取META-INF/spring.factories,获取自动配置类路径
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//3.1.移除重复的配置
configurations = this.removeDuplicates(configurations);
//3.2.处理需要排除的配置
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
```
33.如何自定义一个SpringBoot Srarter?
-----------------------------
知道了自动配置原理,创建一个自定义SpringBoot Starter也很简单。
1. 创建一个项目,命名为demo-spring-boot-starter,引入SpringBoot相关依赖
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
2.编写配置文件
这里定义了属性配置的前缀
@ConfigurationProperties(prefix = "hello")
public class HelloProperties {
private String name;
//省略getter、setter
}
3.自动装配
创建自动配置类HelloPropertiesConfigure
@Configuration
@EnableConfigurationProperties(HelloProperties.class)
public class HelloPropertiesConfigure {
}
4.配置自动类
在/resources/META-INF/spring.factories
文件中添加自动配置类路径
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.fighter3.demo.starter.configure.HelloPropertiesConfigure
5.测试
- 创建一个工程,引入自定义starter依赖
<dependency>
<groupId>cn.fighter3</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
在配置文件里添加配置
hello.name=张三
测试类
@SpringBootTest
public class HelloTest {
//如果模块B使用 @Configuration 定义 Bean,需要在模块A中显式导入:
//@Import(HelloProperties .class) // 导入模块B的配置类
@Autowired
HelloProperties helloProperties;
@Test
public void hello(){
System.out.println("你好,"+helloProperties.getName());
}
}
运行结果

- 至此,随手写的一个自定义SpringBoot-Starter就完成了,虽然比较简单,但是完成了主要的自动装配的能力。
34.Springboot 启动原理?
SpringApplication 这个类主要做了以下四件事情:
- 推断应用的类型是普通的项目还是 Web 项目
- 查找并加载所有可用初始化器 , 设置到 initializers 属性中
- 找出所有的应用程序监听器,设置到 listeners 属性中
- 推断并设置 main 方法的定义类,找到运行的主类
SpringBoot 启动大致流程如下 :

Spring Cloud
35.对SpringCloud了解多少?
SpringCloud是Spring官方推出的微服务治理框架。

什么是微服务?
- 2014 年 Martin Fowler 提出的一种新的架构形式。微服务架构是一种架构模式,提倡将单一应用程序划分成一组小的服务,服务之间相互协调,互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务之间采用轻量级的通信机制(如HTTP或Dubbo)互相协作,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具(如Maven)对其进行构建。
- 微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事情,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动或销毁,拥有自己独立的数据库。
微服务架构主要要解决哪些问题?
- 服务很多,客户端怎么访问,如何提供对外网关?
- 这么多服务,服务之间如何通信? HTTP还是RPC?
- 这么多服务,如何治理? 服务的注册和发现。
- 服务挂了怎么办?熔断机制。
有哪些主流微服务框架?
- Spring Cloud Netflix
- Spring Cloud Alibaba
- SpringBoot + Dubbo + ZooKeeper
SpringCloud有哪些核心组件?

0 条评论