Spring基本知识

174 阅读8分钟

1. Spring事务原理

Spring事务管理的原理主要基于AOP(面向切面编程)机制实现。

基于@Transactional注解的事务底层实现原理,如下:

1. 事务管理基础

Spring框架通过抽象出PlatformTransactionManager接口来提供与具体数据库事务API(如JDBC、JPA、Hibernate等)无关的事务管理能力。开发者通过配置相应的事务管理器实现类(如DataSourceTransactionManagerJpaTransactionManager等)来与具体的持久化技术对接。

2. @Transactional注解

注解使用

  • 方法级别:将@Transactional注解放置在具体服务类或组件的方法上,表明该方法是一个事务方法,应在其执行过程中保持事务的一致性。
  • 类级别:将@Transactional注解放置在类上,表示该类的所有公共(public)方法都将启用事务管理。类级别的注解可以被方法级别的注解覆盖。

注解属性

@Transactional注解提供了若干可配置属性,如:

  • propagation:事务传播行为,如REQUIRED(默认)、REQUIRES_NEWSUPPORTS等,用于控制嵌套事务的处理方式。
  • isolation:事务隔离级别,如READ_UNCOMMITTEDREAD_COMMITTED(默认)、REPEATABLE_READSERIALIZABLE,用于控制事务之间的隔离程度。
  • timeout:事务超时时间(秒),超过该时间未完成则自动回滚。
  • readOnly:指示事务是否只读,默认为false。某些数据库引擎针对只读事务可以采取优化措施。
  • rollbackFor / noRollbackFor:指定发生哪些异常时应触发回滚,或不应触发回滚。

3. AOP代理与事务拦截

当Spring容器检测到类或方法上存在@Transactional注解时,会通过AOP代理机制来增强目标方法的执行过程。常见的AOP代理有两种形式:

  • JDK动态代理:对于实现了接口的服务类,Spring使用JDK的java.lang.reflect.Proxy类来创建代理对象。
  • CGLIB代理:对于没有实现接口的类,Spring使用CGLIB库生成子类作为代理。

无论是哪种代理方式,都会在代理对象中织入事务管理逻辑。当客户端代码调用代理对象的方法时,实际上是调用了代理对象内部的增强方法。这个增强方法会在目标方法执行前后执行以下操作:

  • 前置处理:开始事务。根据@Transactional注解的属性和全局配置,由事务管理器创建或加入一个合适的事务上下文。
  • 目标方法执行:调用原始方法,执行业务逻辑。
  • 后置处理:根据目标方法执行的结果决定提交或回滚事务。如果方法正常返回或抛出了未在rollbackFor属性中指定的异常,则提交事务;否则,如果抛出了rollbackFor属性中指定的异常或未捕获的运行时异常,事务管理器会回滚事务。

4. 异常处理与回滚策略

默认情况下,Spring仅对未被捕获的、继承自RuntimeException的异常以及Error进行事务回滚。如果希望在特定检查型异常(非运行时异常)发生时也触发回滚,可以通过rollbackFor属性明确指定这些异常类型。

5. 事务同步与线程绑定

Spring事务管理器会将事务与当前线程绑定,确保同一个线程中方法调用的事务一致性。在多线程环境下,每个线程有自己的事务上下文,互不影响。

6. 事务管理器的选择与配置

在Spring配置中,需要指定事务管理器的实现类,并将其注入到Spring容器中。此外,还需要配置<tx:annotation-driven>元素(XML配置)或使用@EnableTransactionManagement注解(Java配置),以激活对@Transactional注解的处理。

注:如果是使用Spring Boot开发的话,大部分工作Spring Boot都帮我们自动配置了,如下:

  1. 选择事务管理器
  • 自动配置:Spring Boot会根据项目中引入的依赖自动选择合适的事务管理器。例如,如果使用了spring-boot-starter-data-jpa,则会自动配置JpaTransactionManager;如果使用了JDBC相关依赖,则会配置DataSourceTransactionManager。对于分布式事务(JTA),如果使用了如Narayana这样的支持库,Spring Boot也会自动配置JtaTransactionManager
  1. 定义事务管理器 bean
  • 自动配置:Spring Boot会自动创建并注册所需的事务管理器bean到Spring容器中,开发人员无需手动编写bean定义。
  1. 启用注解驱动事务管理
  • 自动配置:Spring Boot会自动启用注解驱动的事务管理,无需在配置中显式添加<tx:annotation-driven>@EnableTransactionManagement注解。
  1. 配置事务属性
  • 半自动配置:Spring Boot提供了默认的事务属性配置,开发人员可以在application.propertiesapplication.yml中轻松覆盖这些默认值,如设置全局的默认超时时间、隔离级别等。这是通过Spring Boot的外部化配置特性实现的,开发人员无需手动编写代码来配置事务属性。
  1. 数据源配置
  • 自动配置:Spring Boot会根据application.propertiesapplication.yml中提供的数据库连接信息自动配置数据源(如HikariCP、Tomcat JDBC Pool等),并将其注入到事务管理器中。开发人员只需提供数据库连接参数,无需手动配置数据源bean。

2. Spring中过滤器和拦截器的区别?

在实现原理、适用范围、生命周期、与容器的关系等方面存在显著区别:

  1. 实现原理与位置

    • 过滤器:过滤器是基于Java Servlet规范实现的,属于Servlet容器(如Tomcat、Jetty等)的一部分,不依赖于Spring框架。它们实现了javax.servlet.Filter接口,并通过在web.xml或Spring Boot的WebMvcConfigurer中声明来配置。当请求到达Servlet容器时,过滤器按照配置的顺序链式执行。
    • 拦截器:拦截器是Spring框架提供的特性,基于Java反射机制实现。它们通常实现org.springframework.web.servlet.HandlerInterceptor接口,或者使用Spring AOP的@Aspect注解定义。拦截器在Spring MVC的DispatcherServlet的请求处理流程中起作用,更贴近业务逻辑层面。
  2. 使用范围与粒度

    • 过滤器:过滤器的使用范围较广,几乎可以对任何进入Servlet容器的请求(包括静态资源请求、Servlet请求、JSP请求等)进行拦截和处理。过滤器的拦截粒度相对粗犷,通常用于处理与具体业务逻辑无关的通用任务,如字符编码转换、压缩、登录状态验证等。
    • 拦截器:拦截器主要针对Spring MVC框架中的Controller请求进行拦截,即针对处理业务逻辑的Action请求。拦截器的拦截粒度更为精细,专注于与业务逻辑相关的处理,如身份认证、权限控制、数据校验、事务管理等。拦截器不会拦截静态资源或其他非Controller处理的请求,除非特别配置。
  3. 与容器的关系与功能

    • 过滤器:过滤器不依赖于Spring容器,因此无法直接访问Spring容器中的Bean或利用Spring的功能。它们通常通过ThreadLocal等方式传递共享状态,或者通过硬编码的方式访问底层服务。
    • 拦截器:拦截器是Spring容器的一部分,可以直接注入其他Bean作为依赖,利用Spring的IoC(控制反转)和AOP功能。这意味着拦截器可以方便地访问服务层对象、使用事务管理、进行AOP通知等,与业务逻辑的交互更加紧密。
  4. 生命周期与调用次数

    • 过滤器:过滤器的生命周期与Servlet容器的生命周期一致,一般在容器启动时初始化,直至容器关闭。对于每个请求,过滤器链中的每个过滤器都会被调用一次,按照配置的doFilter()方法执行。
    • 拦截器:拦截器的生命周期与Spring Bean相同,随Spring容器的启动和关闭而创建和销毁。一个请求在Spring MVC处理过程中可能触发多个拦截器,每个拦截器的preHandle()postHandle()afterCompletion()方法可能会分别被调用一次(根据拦截器链的配置和请求处理结果)。拦截器的调用次数和顺序更灵活,可以实现更复杂的调用逻辑。
  5. 访问资源与上下文

    • 过滤器:过滤器通常只能访问到请求和响应对象、Servlet API提供的上下文信息(如HttpServletRequestHttpServletResponseFilterChain等),对Action上下文(如Spring MVC的ModelAndView)以及值栈对象的访问有限。
    • 拦截器:拦截器不仅可以访问请求和响应对象,还能访问到Spring MVC提供的Handler(处理器对象,如HandlerMethod),从而获得更丰富的上下文信息,如方法参数、Controller类、请求方法名等。拦截器还可以修改或增加模型数据(ModelAndView),与业务逻辑的交互更为深入。