SpringBoot 学习笔记

SpringBoot 全局异常处理类

@RestControllerAdvice 注解,@ControllerAdvice 注解,@ExceptionHandler注解


声明式事务

声明式事务通过配置文件来管理事务,不需要在业务代码中处理事务逻辑。

对启动类添加注解 @EnableTransactionManagement,对业务层方法添加注解 @Transactional(rollbackFor={SQLException.class}),该方法发生SQL异常就会回滚。主从数据库读写分离情况下,可能无法切换数据源。

与之相对的是编程性事务:

SqlSession sqlSession = sqlSessionFactory.openSession(false);

try {

    sqlSession.insert("insertUser", user);

    sqlSession.update("updateUser", user);

    sqlSession.commit();

} catch (Exception e) {

    sqlSession.rollback();

} finally {

    sqlSession.close();

}

IoC 与 DI

Inversion of Control 控制反转 是一种设计思想;

DI 依赖注入 是这种思想的一种实现方法;

将原本在程序中手动创建对象的控制权交给第三方,比如 IoC 容器。


AOP

AOP 的常见实现方式有动态代理、字节码操作等方式。

Spring AOP 就是基于动态代理的;

如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象;

对于没有实现接口的对象, Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。

Spring AOP 已经集成了 AspectJ,AspectJ 性能更好。

Spring AOP 属于运行时增强,而 AspectJ 是编译时增强;

Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作。

  • Before(前置通知):目标对象的方法调用之前触发

  • After (后置通知):目标对象的方法调用之后触发

  • AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发

  • AfterThrowing(异常通知):目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。

  • Around (环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法。


Spring 事务

事务的特性:ACID

只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的。

原子性,隔离性和持久性是数据库的属性,而一致性是应用程序的属性。应用可能依赖数据库的原子性和隔离属性来实现一致性,但这并不仅取决于数据库。因此,字母 C 不属于 ACID 。

mysql 如何保证原子性:使用回滚日志(undo log),先写入回滚日志(即对数据的操作步骤)再执行操作,如遇异常可依靠回滚日志进行回滚。

属性名 说明
propagation 事务的传播行为,默认值为 REQUIRED。事务 A 调用事务 B,一方回滚时另一方如何行为。
isolation 事务的隔离级别,默认值采用 DEFAULT。指定数据库的隔离级别。
timeout 事务的超时时间,默认值为-1(不会超时)。如果超过该时间限制但事务还没有完成,则自动回滚事务。
readOnly 指定事务是否为只读事务,默认值为 false。
rollbackFor 用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型。

事务方法应为 public。在同一个类中的其他方法调用事务方法时,无法生效,因为AOP 代理对象无法拦截这种调用。所以避免同一类中自调用或者使用 AspectJ 取代 Spring AOP 代理。


SpringBoot 自动装配

@EnableAutoConfiguration:启用 SpringBoot 的自动配置机制。

所有 Spring Boot Starter 下的META-INF/spring.factories都会被读取到

 Spring Boot 提供的 条件注解(@ConditionalOnBean 等) 筛选所需的 bean。

总结:

Spring Boot 通过@EnableAutoConfiguration开启自动装配,通过 SpringFactoriesLoader 最终加载META-INF/spring.factories中的自动配置类实现自动装配,自动配置类其实就是通过@Conditional按需加载的配置类,想要其生效必须引入spring-boot-starter-xxx包实现起步依赖。


@Async 注解

被该注解标注的类或方法会在 异步线程 中执行。这意味着当方法被调用时,调用者将不会等待该方法执行完成,而是可以继续执行后续的代码。在启动类上添加注解 @EnableAsync ,开启异步任务。在需要异步执行的方法或类上添加注解 @Async 。本质上是使用 动态代理 来实现的。

如果没有显式地配置线程池,在 @Async 底层会先在 BeanFactory 中尝试获取线程池,如果获取不到,则会创建一个 SimpleAsyncTaskExecutor 实现。SimpleAsyncTaskExecutor 本质上不算是一个真正的线程池,因为它对于每个请求都会启动一个新线程而不重用现有线程,这会带来一些潜在的问题,例如资源消耗过大。

配置线程池:

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(name = "executor1")
    public Executor executor1() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(50);
        executor.setThreadNamePrefix("AsyncExecutor1-");
        executor.initialize();
        return executor;
    }

    @Bean(name = "executor2")
    public Executor executor2() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(4);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("AsyncExecutor2-");
        executor.initialize();
        return executor;
    }
}

指定线程池:

@Service
public class AsyncService {

    @Async("executor1")
    public void performTask1() {
        // 任务1的逻辑
        System.out.println("Executing Task1 with Executor1");
    }

    @Async("executor2")
    public void performTask2() {
        // 任务2的逻辑
        System.out.println("Executing Task2 with Executor2");
    }
}

不能在同一类中调用异步方法,不能使用 static 关键字修饰异步方法;

建议将 @Async 注解方法的返回值类型定义为 void 或 Future 。调用者可以使用这个返回的 Future 对象来查询任务的状态,取消任务,或者在任务完成时获取结果;

异步方法中的异常需要通过全局异常处理类来进行处理;

异步方法使用事务时,必须开启新的、当前无关的事务;

调用时以回调函数的形式指定异步方法执行顺序。