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 对象来查询任务的状态,取消任务,或者在任务完成时获取结果;
异步方法中的异常需要通过全局异常处理类来进行处理;
异步方法使用事务时,必须开启新的、当前无关的事务;
调用时以回调函数的形式指定异步方法执行顺序。