spring 知识点总结

2021-10-31  本文已影响0人  JackSpeed

spring是什么?你是怎么理解的?

spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架;
从打小和开销两个方面来说spring都是轻量级的;它可以通过IOC和AOP达到松耦合的目的;它提供了买你想切面编程的丰富支持,允许通分离应用逻辑与系统及服务进行内聚性的开发(常用的有日志);它包含并管理应用对象的配置和生命周期,在这个意义上是可以说明它是一个容器;spring还可以将简单的应用组件配置组合成复杂的应用,这个意义上它是一个框架。

AOP的理解

系统是由多个不同的组件构成的,每个组件各负责一些特定的功能,处理自身的核心功能之外组件往往还承担这额外的职责,比如安全、事务、日志等逻辑需要融入到核心逻辑中去,这些系统服务经常被称为横切关注点,因为他们会跨越多个组件。
AOP是将程序中的交叉业务逻辑(安全、日志、事务等)封装成一个切面,然后注入到目标对象(具体业务逻辑)中去,AOP的作用是可以对某个对象或某些对象的功能进行增强;比如对象中的方法增强,可以在执行某个方法之前或者之后额外的做一些事情。

IOC的理解

IOC主要包含三个概念,IOC容器、控制反转、依赖注入;

  1. IOC容器:它本质上是一个map,在项目启动时利用反射将xml中的bean和@Configuration、@Component、@Service注解作用的类创建对象并放入到map中,在后面需要使用时再通过ID注入获得对象(ID注入包含@Autowire、@Resource等注解和xml中bean的ref属性)
  2. 控制反转:在没有引入IOC容器之前,A、B两个对象,如果A对象依赖于B对象,当程序初始化或者A对象初始化时,A必须主动去创建B对象或者使用已经创建的B对象,这时A要获得B都是主动,控制权都在A对象自己手上;在引入IOC容器之后,A对象和B对象之前失去了直接的关联,当A对象需要使用B对象时,会由IOC容器来创建B对象并且注入到A对象需要的地方;这种在引入IOC之后A获得B对象的过程有主动创建变成了被动注入就称为控制反转
  3. 依赖注入:依赖注入是实现IOC的方法,就是在IOC容器运行期间,动态的将某种回来关系注入到对象之中

BeanFactory和ApplicationContext的区别是什么?

ApplicationContext是BeanFactory的子接口,ApplicationContext继承了BeanFactory的子接口,它有更完善的功能——

  1. 它继承了messageSource接口拥有国际化的能力;
  2. 有统一的资源文件访问方式;
  3. 提供在监听器中注册bean事件的能力;
  4. 能同时加载多个配置文件;
  5. 可以载入多个上下文,是的每个上下文都专注于特定的层次。

区别:

  1. BeanFactory采用的是延迟加载形式来注入bean,也就是在调用getBean方法时才初始化实例。这样比ApplicationContext节约一点内存开销。BeanFactory通常以代码编程的当时被创建来初始化bean,ApplicationContext除了代码编程创建还支持声明的方式创建,例如使用ContextLoader.
  2. ApplicationContext是在启动容器时就一次性创建了所有的bean,这样的有点就是能及时发现所依赖属性是否注入;同时也因为提前初始化bean提高了后期访问时的效率,但是提前都初始化bean会占用更大的内存空间,还可能因为实例化bean较多导致启动变慢
  3. BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor是使用,但两者的区别是BeanFactory需要手动注册而ApplicationContext是自动注册

Spring 中Bean的生命周期

创建:首先解析得到beanDefinition;然后判断是否有多个构造方法,有多个则推断构造方法;确定构造方法后进行实例化得到对象;接着再根据对象属性进行属性填充和依赖注入(有依赖关系);接着就是回调Aware方法(比如BeanAware、BeanFactoryAware),再然后就是调用BeanPostProcessor的初始化前方法和初始化方法;最后再调一次BeanPostProcessor的初始化后方法;(如果当前bean是单例则会把bean放入单例池中)
使用:在程序中调用
销毁:spring容器关闭时调用 DisposableBean中的destroy方法

Spring中bean的作用域

一共包含七种,常用的有六种:
singleton:spring默认的作用域,也就是每个容器中只有一个实例,生命周期与其所在的IOC容器一致(唯一的区别是实例只有第一次被注入时才创建)
request:每次请求都创建一个单例对象,在整个请求过程中都使用这个单例对象
session:与request类似,也就是每个session中只有一个bean的单例,session过期之后bean的单例会是被回收
prototype:这种形式为每次获取bean时提供一个新的对象,也就是每次注入时都会新建一个对象
application:表示bean被定义为在servletContext的生命周期中一直会复用一个单例对象
websocket:表示bean被定义为在整个websocket生命周期中复用一个单例对象
前面为常用的六种作用域
global-session:全局作用域,它和Portlet应用相关联;它的作用效果与servlet中session的作用域效果相同;

Spring 中bean是否是线程安全的

在spring中bean默认是以单例模式存在,如果bean是有状态的则需要开发者自行保证线程安全。最简单的方式就是修改bean的作用域,修改为prototype,这样每次获取bean就相当于new一个bean对象,bean就是线程安全了。
有状态:例如bean中有数据存储,全局变量赋值等
无状态:只调用bean的方法,不存在全局变量赋值和数据存储

如果有数据状态,尽可能使用ThreadLocal把变量变成当前线程私有的值。如果在多线程个下需要数据存储或者是对全局变量操作和线程间共享,那么只能使用synchronize、lock、CAS等这些来实现线程同步。

列举几个spring中用到的设计模式

单例模式:spring中的对象默认都是单例的
工厂模式:BeanFactory用的就是工厂模式
适配器模式:

Spring事务的实现方式和原理以及隔离级别

实现方式:
在spring事务有中两种实现方式,第一种是编程式,一种是声明式;变成式就是使用begin commit/rollback;声明式就是使用@Transaction注解。

原理:
spring中的事务只是基于数据库事务的扩展,它提供了一些更便捷的事务操作方式。在给方法加上@Transaction之后,Spring会基于当前这个类生成一个代理对象,存入IOC容器中 ,然后按照AOP的整套流程来执行具体的事务逻辑,事务的具体实现是由TransactionIntercept的invoke方法完成的。

执行流程: image.png
从源码角度来分析:
Invoke方法逻辑中,
  1. 根据AopUtils获取到事务的代理对象
  2. 解析代理对象的各个方法,根据属性判断是否需要开启新事务,判断是声明式事务还是编程式,如果是声明式事务,先解析方法上的属性——隔离级别、传播特性、回滚异常类型等
  3. 需要开启则获取数据库连接,关闭自动提交、开启事务
  4. 执行当前业务SQL
  5. 如果业务SQL执行失败则通过completeTransactionAfterThrowing来完成事务回滚——具体实现是获取数据库连接Collection对象,通过连接对象来进行回滚Collection.rollback。
  6. 如果事务执行成功则通过commitTransactionAsterReturning完成——具体实现也是通过Collection连接对象,通过连接对象的commit来提交事务
  7. 最后清除事务标记

隔离级别:
本质上Spring事务隔离级别就是事务的隔离级别,默认使用底层数据库的隔离级别
读未提交:read uncommitted
读已提交:read committed
可重复度:repeatable read
可串行化:serializable
如果spring中的隔离级别与数据中的隔离级别不同,会使用spring中配置的隔离级别。mysql默认的是repeatable read 。Oracle默认级别read committed

spring事务传播机制

https://zhuanlan.zhihu.com/p/148504094
事务传播机制就是多个事务方法间调用时,事务在方法间的传播;例如存在ma方法和mb方法,ma调用mb时事务的传播。事务调用时不能在当前类中用this.调用,这样获得的实例对象不在IOC容器中,会导致事务失效。

class Test{
  public void ma(){
    insertSql(a);
    }
}

class TestB{
   public void mb(){
    insertSql(b);
    }
}

根据源码有其中传播机制:

  1. REQUIRED:spring默认的传播机制,含义:如果当前没有事务,则自己创建一个新事务,创建事务场景下两个方法只存在一个事务;如果存在事务则加入这个事务,加入事务场景下,两个方法使用的是同一个事务,回滚时两个方法的SQL会同时回滚;

  2. SUPPORTS:它表示如果当前存在事务怎加入当前事务;如果当前没有事务就以非事务方式执行

     class Test{
         @AutoWire
         private TestB tb;
         @Transaction(propagation = Propagation.REQUIRED)
         public void ma(){
               insertSql(a1);
               tb.mb();
         }
         @Transaction(propagation = Propagation.SUPPORTS)
         public void mb(){
             insertSql(b1);
             throw RuntimeException();
             insertSql(b2);
           }
       }
      //执行结果:没有数据写入,mb方法回滚之后ma方法的SQL也跟着回滚了
    
    class Test{
        @AutoWire
        private TestB tb;
    
        public void ma(){
            insertSql(a1);
            tb.mb();
        }
    
        @Transaction(propagation = Propagation.SUPPORTS)
        public void mb(){
            insertSql(b1);
            throw RuntimeException();
            insertSql(b2);
        }
      }
      //执行结果:写入a1,b1对象,mb方法由于抛出异常b2没有写入
    
  3. MANDATORY:它表示如果当前存在事务则加入当前事务;如果不存在事务则抛出异常;

    class Test{
      @AutoWire
      private TestB tb;
    
        @Transaction(propagation = Propagation.REQUIRED)
        public void ma(){
            insertSql(a1);
            tb.mb();
        }
    
        @Transaction(propagation = Propagation.MANDATORY)
        public void mb(){
            insertSql(b1);
            throw RuntimeException();
            insertSql(b2);
        }
    }
        //执行结果为: 没有数据写入
    
    class Test{
      @AutoWire
      private TestB tb;
    
        public void ma(){
            insertSql(a1);
            tb.mb();
        }
    
        @Transaction(propagation = Propagation.MANDATORY)
        public void mb(){
            insertSql(b1);
            throw RuntimeException();
            insertSql(b2);
        }
    }
        //执行结果为: 抛出异常,没有任何数据写入
    
  4. REQUIRES_NEW:它表示首先mb方法会创建一个新事务,如果ma方法存在事务则将ma事务挂起,需要等ma的事务提交或者回滚之后才执行ma的事务;

  5. NOT_SOPPORTED:表示(mb)始终以非事务方式执行,如果当前(ma)存在事务则将事务挂起,先无事务执行mb;

  6. NEVER:表示不使用事务,如果当前(ma)存在事务则(mb)抛出异常

  7. NESTED:表示如果当前(ma)存在事务则在嵌套事务中执行;不存在则自己(mb)创建一个事务执行,mb的的事务嵌套在ma事务里面。

    class Test{
        @AutoWire
        private TestB tb;
    
       @Transaction(propagation = Propagation.REQUIRED)
       public void ma(){
           insertSql(a1);
           tb.mb();
           insertSql(a2);
           throw RuntimeException();
       }
     
       @Transaction(propagation = Propagation.NESTED)
       public void mb(){
          insertSql(b1);
          insertSql(b2);
       }
     }
    //执行结果:没有数据写入,因为在NESTED模式下父事务回滚会导致子事务的回顾
    
      class Test{
       @AutoWire
       private TestB tb;
    
         @Transaction(propagation = Propagation.REQUIRED)
         public void ma(){
             insertSql(a1);
             tb.mb();
             insertSql(a2);
         }
    
         @Transaction(propagation = Propagation.NESTED)
         public void mb(){
             insertSql(b1);
             throw RuntimeException();
             insertSql(b2);
         }
     }
         //执行结果为: 没有数据写入,因为mb这个子事务抛出了异常导致insertSql(b1)回滚,在ma方法中没有捕获事务,导致insertSql(a1)被父事务回滚
    
    class Test{
       @AutoWire
       private TestB tb;
    
        @Transaction(propagation = Propagation.REQUIRED)
        public void ma(){
             insertSql(a1);
            try{
              tb.mb();
             }catch(Exception e){}
             insertSql(a1);
         }
    
         @Transaction(propagation = Propagation.MANDATORY)
         public void mb(){
             insertSql(b1);
             throw RuntimeException();
             insertSql(b2);
         }
     }
         //执行结果为: 写入a1,a2,因为mb子事务中抛出异常导致insertSql(b1)被回滚,
         //而在ma中父事务与子事务相隔离,子事务的异常被catch之后不影响父事务,所以写入了a1,a2
    

Spring事务什么时候会失效?

spring事务的默认实现使用的是AOP,AOP会在底层生成一个代理对象,使用时需要用代理对象事务才能有效,那么失效的根本原因可能就是AOP切面失效,常见的失效有以下5中场景。

  1. 发生在类内部的this自调用;因为this使用的当前的类的对象不是代理对象,会导致事务失效
  2. 索引注解修饰的方法不是public
  3. 数据库不支持事务,比如数据库引擎为MYASAM
  4. 事务注解修饰的方法对应的类没有没spring管理;比如自己的工具类没有在IOC容器中则会失效
  5. 异常被吃掉了或者抛出的异常没有被定义(默认为RuntimeException),会导致事务不会被回滚

什么是bean的自动装配?有哪些方式?

bean的自动装配就是自动给bean的属性赋值;装配方式可分为自动和手动
装配:bean的装配只需在xml配置文件<bean>中定义autowire属性即可,autowire有五种不同的方式,也就是说bean有有五种装配方式;

不写autowire属性则为是手动装配方式,手动装配需要以value或者ref属性明确执行属性的值,需要通过ref属性来链接bean
autowire有值时表示自动装配,

  1. byName:根据bean的属性名称自动装配需要的属性,通过setter方法
  2. byType:根据bean的类型自动装配
  3. construct:类似于byType,不过是根据构造器的参数类去找对应的bean,如果一个类的类型与构造器参数类型相同则进行装配,否则导致异常
  4. autodetect:如果有默认构造器则通过construct方式进行装配,如果不存在默认构造器则使用byType的方式进行装配

spring、spring MVC、springboot的区别

spring 是一个用来管理bean的ICO容器,它使用依赖注入实现控制反转可以很方便的整合各种框架,提供AOP机制弥补了OOP带来的代码重复问题,AOP更方便将不同类不同方法的共同处理抽取成切面,自动注入给方法执行。比如常用的日志、异常、参数校验拦截器等
spring mvc是spring对web框架的整合,提供了一个总的dispatchServlet控制器用来接收请求,然后定义了从URL到Handle的映射,将handle结果使用视图解析技术生成视图展现给前端的这样一个框架

spring boot是spring提供的一个快速开发的工具包,能更方便、更快捷的开发spring+spring MVC应用,它使用约定大于配置的理念简化了很多配置,同时还整合了了一系列的解决方案,比如redis-starter、MongoDB-starter,达到开箱即用的效果

Spring的工作流程

  1. 用户发送请求,请求进入前前端控制器DispatcherServlet
  2. dispatcherServlet根据收到的请求调用HandlerMapping(处理器映射器)获取handler(处理器)
  3. HandlerMapping得到处理器之后把handler返回给dispatcherServlet
  4. dispatcherServlet调用HandlerAdapter(处理器适配器)处理Handler,HandlerAdapter经过适配调用据图的处理器Controller,Controller执行业务逻辑之后返回modelView
  5. HandlerAdapter将ModelView返回给DispatcherServlet,DispatcherServlet将modelView传给ViewResolver(视图解析器)解析得到View
  6. DispatcherServlet在根据View进行渲染,将数据模型填充到视图中返回给用户

Spring中的Handler

spring中Handler也就是处理器,直接对应着MVC里面的C(Controller),它的具体表现形式有很多,可以是类、方法;在Controller层中@RequestMapping标注的所有方法都可以看成是一个Handler,只要可以实际处理请求就可以是Handler

Spring MVC的主要组件(九大组件)

  1. HandlerMapping:处理器映射器,主要作用是根据用户请求的资源uri查找对应的处理器(Handler);在Spring中每个请求都需要一个Handler来处理,HandlerMapping负责查找对应处理器的工作

  2. HandlerAdapter:处理器是配置,因为SpringMVC中Handler可以是任意形式,比如有Controller、Servlet、@RequestMapping等,每种形式的Handler有不同的Handler适配器;HandlerAdapter主要作用就是根据处理器找到对应的是处理器适配器(HandlerAdapter),找到之后调动各自的处理方法处理业务。比如有:SimpleControllerHandlerAdapter、SimpleServletHandlerAdapter、RequestMappingHandlerAdapter等

  3. HandlerExceptionResolver :全局异常处理器,它的作用是根据异常设置ModelAndView,然后交给render方法进行渲染

  4. ViewResolver:视图解析器,用来讲String类型的视图名和Locale解析为View类型的视图;View是用来渲染页面的,也就是将程序返回的参数填入模板里生成html或者其他类型的文件。ViewResolver主要做的是找到渲染所需要的模板和视图类型,据图的渲染过程则由不同的视图自己完成

  5. RequestToViewNameTranslator:首先ViewResolver是根据ViewName查找view的,但有的Handler处理器处理完任务之后并没有返回view也没有viewName,这时候就需要使用RequestToViewNameTranslator从Request中获取viewName了,而且它在SpringMVC容器里面只允许配置一个,所有的request到viewname的转换都这里全部实现

  6. LocaleResolver:做国际化时用到的组件,在视图解析的时候可以用加载不同国际化资源
    7.ThemeResolver:由于主题解析。

  7. MultipartResolver:文件上传解析器

  8. FlashMapManager:用来管理FlashMap,FlashMap主要用在redirect中传递参数

比较重要的是前两个核心组件

springboot自动配置原理

主要是根据@import+@Configuration+Spring SPI 来实现的;自动配置类由各个starter提供,它们使用@Configuration+@Bean的方式定义配置类,把这些bean以KV的形式放在META-INF/spring.factories文件中,接着使用Spring SPI扫描META-INF/spring.factories文件中的类,然后使用@Import导入到OIC容器中提供给程序调用。

如何理解springboot中的starter

starter就是顶一个一个starter的jar包,写一个@Configuration配置类将当前需要的bean定义在里面,然后再starter包的META-INF/spring.factories中写入该配置类,springboot会按照约定来加载该配置类;因为配置有了@Configuration,所以在配置类里面@Bean定义的bean都会被加载到IOC容器中。starter定义好之后,开发人员只需引入starter依赖,再加上一些配置(有默认则可以不用配置)就可以使用相应的功能了。

什么是Nacos

nacos是主要包含服务注册中心与配置中心,通过心跳机制和健康检查机制来感知客户服务存活状态。服务注册是通过发送http请求的方式把当前客户服务名注册到nacos naming 中心;心跳这部分就是每个五秒向nacos注册中心发送一次心跳包;健康检查这块简单来说就是客户service注册到nacos的注册表之后,延迟五秒间隔五秒执行一次健康检查,如果客户service超过15秒还未发送心跳,则健康状态为false,如果30秒还没收到心跳则冲注册表删除当前客户service,下次心跳过来再重新注册服务。服务发现有两个逻辑,第一个是服务订阅者轮询请求nacos注册中心查询自己订阅的可用服务列表,第二个是nacos注册中心从服务队列中取出服务实例通过监听器通知到客户service提供调用。配置中心能力也是通过客户端长轮询请求配置中心的方式来实现的,其中用到线程池、MD5校验之类的。

什么是Sentinel

什么是Seata

是有1+3的套件组成,1是全局唯一事务ID(XID),剩下的三个分别是事务协调器(TC)、事务管理器(TM)、资源管理器(RM);实际中@GlobalTransaction就是TM,Seata服务器就是TC事务协调器,各个分库就是RM。大概的流程就是在每个库增加undo_log表记录事务操作的前置、后置数据记录和分支事务ID与全局事务id关联,事务结束(执行、回滚)之后再删除undo_log记录
事务协调器(TC)主要是控制事务的全局提交和回滚;
事务管理器(TM)复制开启或者关闭事务,并发起最终的执行和终止事务决议;
资源管理器(RM)主要控制分支事,负责分支事务的注册、状态同步,并驱动分支事务的提交或者回滚。
工作流程可分为以下几个步骤:

  1. 由事务管理器TM申请开启全局事务并生成全局事务XID
  2. XID在分布式服务上下文中传播
  3. 资源管理器RM想TC注册分支事务,并纳入XID对应的全局事务管辖
  4. TM想TC发起针对XID的全局事务提交或回滚决议
  5. TC调度XID下管辖的全部分支事务完成提交或者回滚指令
上一篇下一篇

猜你喜欢

热点阅读