23.Spring 事务的种类?

Spring 支持编程式事务管理和声明式事务管理两种方式:

  1. 编程式事务

编程式事务管理使用 TransactionTemplate,需要显式执行事务。

  1. 声明式事务
  2. 声明式事务管理建立在 AOP 之上的。其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务
  3. 优点是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过 @Transactional 注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。

24.Spring 的事务隔离级别?

Spring的接口TransactionDefinition中定义了表示隔离级别的常量,当然其实主要还是对应数据库的事务隔离级别:

  1. ISOLATION_DEFAULT:使用后端数据库默认的隔离界别,MySQL 默认可重复读,Oracle 默认读已提交。
  2. ISOLATION_READ_UNCOMMITTED:读未提交
  3. ISOLATION_READ_COMMITTED:读已提交
  4. ISOLATION_REPEATABLE_READ:可重复读
  5. 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,类图如下:
  • 事务拦截器TransactionInterceptorinvoke方法中,通过调用父类TransactionAspectSupportinvokeWithinTransaction方法进行事务处理,包括开启事务、事务提交、异常回滚。

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 的核心组件?

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

29.Spring MVC 的工作流程?

  1. 客户端向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet(也叫中央控制器)。
  2. DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该请求该由哪个Controller来处理(并未调用Controller,只是得知)
  3. DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller
  4. HandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视图),并层层返回给DispatcherServlet
  5. DispatcherServlet将ModelAndView交给ViewReslover视图解析器解析,然后返回真正的视图。
  6. DispatcherServlet将模型数据填充到视图中
  7. 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大体上相同,但是细节上有一些不同:

  1. 客户端向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet
  2. DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该请求该由哪个Controller来处理
  3. DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller
  4. Controller被封装成了ServletInvocableHandlerMethod,HandlerAdapter处理器适配器去执行invokeAndHandle方法,完成对Controller的请求处理
  5. HandlerAdapter执行完对Controller的请求,会调用HandlerMethodReturnValueHandler去处理返回值,主要的过程:5.1. 调用RequestResponseBodyMethodProcessor,创建ServletServerHttpResponse(Spring对原生ServerHttpResponse的封装)实例5.2.使用HttpMessageConverter的write方法,将返回值写入ServletServerHttpResponse的OutputStream输出流中5.3.在写入的过程中,会使用JsonGenerator(默认使用Jackson框架)对返回值进行Json序列化
  6. 执行完请求后,返回的ModealAndView为null,ServletServerHttpResponse里也已经写入了响应,所以不用关心View的处理

Spring Boot

31.介绍一下SpringBoot,有哪些优点?

Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。

Spring Boot 以约定大于配置核心思想开展工作,相比Spring具有如下优势:

  1. Spring Boot 可以快速创建独立的Spring应用程序。
  2. Spring Boot 内嵌了如Tomcat,Jetty和Undertow这样的容器,也就是说可以直接跑起来,用不着再做部署工作了。
  3. Spring Boot 无需再像Spring一样使用一堆繁琐的xml文件配置。
  4. Spring Boot 可以自动配置(核心)Spring。SpringBoot将原有的XML配置改为Java配置,将bean注入改为使用注解注入的方式(@Autowire),并将多个xml、properties配置浓缩在一个appliaction.yml配置文件中。
  5. Spring Boot 提供了一些现有的功能,如量度工具,表单数据验证以及一些外部配置这样的一些第三方功能。
  6. 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,这个方法是获取自动装配类的关键,主要流程可以分为这么几步:
    1. 获取注解的属性,用于后面的排除
    2. 获取所有需要自动装配的配置类的路径:这一步是最关键的,从META-INF/spring.factories获取自动配置类的路径
    3. 去掉重复的配置类和需要排除的重复类,把需要自动加载的配置类的路径存储起来
     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());
        }
    }

运行结果

  1. 至此,随手写的一个自定义SpringBoot-Starter就完成了,虽然比较简单,但是完成了主要的自动装配的能力。

34.Springboot 启动原理?

SpringApplication 这个类主要做了以下四件事情:

  1. 推断应用的类型是普通的项目还是 Web 项目
  2. 查找并加载所有可用初始化器 , 设置到 initializers 属性中
  3. 找出所有的应用程序监听器,设置到 listeners 属性中
  4. 推断并设置 main 方法的定义类,找到运行的主类

SpringBoot 启动大致流程如下 :

Spring Cloud

35.对SpringCloud了解多少?

SpringCloud是Spring官方推出的微服务治理框架。

什么是微服务?

  1. 2014 年 Martin Fowler 提出的一种新的架构形式。微服务架构是一种架构模式,提倡将单一应用程序划分成一组小的服务,服务之间相互协调,互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务之间采用轻量级的通信机制(如HTTP或Dubbo)互相协作,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具(如Maven)对其进行构建。
  2. 微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事情,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动或销毁,拥有自己独立的数据库。

微服务架构主要要解决哪些问题?

  1. 服务很多,客户端怎么访问,如何提供对外网关?
  2. 这么多服务,服务之间如何通信? HTTP还是RPC?
  3. 这么多服务,如何治理? 服务的注册和发现。
  4. 服务挂了怎么办?熔断机制。

有哪些主流微服务框架?

  1. Spring Cloud Netflix
  2. Spring Cloud Alibaba
  3. SpringBoot + Dubbo + ZooKeeper

SpringCloud有哪些核心组件?

分类: springSSM

0 条评论

发表回复

Avatar placeholder

您的邮箱地址不会被公开。 必填项已用 * 标注