@EnableAsync 来激活,底层基于 Spring AOP,对目标 Spring Bean 做方法级别的拦截。
拦截器类 - AnnotationAsyncExecutionInterceptor,扩展了 AsyncExecutionInterceptor
使用 Spring IoC 容器中的 TaskExecutor 作为具体任务执行器,如果找不到的话,会使用 SimpleAsyncTaskExecutor。因此,推荐基于 ThreadPoolExecutor 来实现的 TaskExecutor,如 ThreadPoolTaskScheduler。
@EnableScheuding 来激活,底层基于 Spring AOP,对目标 Spring Bean 做方法级别的拦截。
ScheduledAnnotationBeanPostProcessor
常见的使用场景:分析 Spring BeanDefinition(RootBeanDefintion) 的相关元信息,比如 @Autowired
常见的使用场景:拦截 Spring Bean 销毁动作,前置动作
当 Spring IoC 容器预实例化所有非 Lazy 单例 Spring Bean 之后的回调操作。
@Override
public void afterSingletonsInstantiated() {
// Remove resolved singleton classes from cache
this.nonAnnotatedClasses.clear();
if (this.applicationContext == null) {
// Not running in an ApplicationContext -> register tasks early...
finishRegistration();
}
}
可以通过 JDK 5 ThreadPoolExecutor 来整合 @Async 和 @Scheduled
是 Spring Bean,满足 Spring Bean 生命周期,通常是单例,继承了 ExecutorConfigurationSupport 的行为。
不推荐使用,这方式会重新初始化整合 Spring Bean,代价有点高。
问题来源:
@Valid 注解不生效 该单元测试如何在非 Spring MVC 下,执行 Bean Validation
在 Spring WebMVC 中,通过 org.springframework.web.method.annotation.ModelAttributeMethodProcessor#validateValueIfApplicable 方法来处理 @Validated 注解,以及触发 Bean Validation 框架。
如果要在 Spring Testing 或者Spring Boot Testing 场景下,处理 Controller 实现类的话,方法如下:
- Spring MockMvc Testing 处理
- 基于 Spring AOP 与 Bean Validation 整合类 - MethodValidationInterceptor,增加到 Spring 继承测试中
巧用 Exit Code,退出码,正常退出 0,其他情况退出其他整数。
方便运维了解具体情况。
假设配置校验错误的退出码:-1
Spring Boot 有一个 SPI :ExitCodeGenerator。
基于 Spring Boot 监听器来实现启动检测。
实现方式:
- SpringApplicationRunListener
- ApplicationListener
- ApplicationContextInitializer
- EnvrionmentPostProcessor
- 其他
- Spring Bean 实现:
- @PreDestroy 标注方法
- DisposableBean 实现
- 自定义指定 destroy 方法
- Spring ApplicationListener 实现:
- 监听 ContextClosedEvent
- 监听 ApplicationFailedEvent
- Spring SmartLifecycle 实现:
Spring 任务或调度执行 Bean 实现往往继承 ExecutorConfigurationSupport
@Override
public void afterPropertiesSet() {
initialize();
}
/**
* Set up the ExecutorService.
*/
public void initialize() {
if (logger.isDebugEnabled()) {
logger.debug("Initializing ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));
}
if (!this.threadNamePrefixSet && this.beanName != null) {
setThreadNamePrefix(this.beanName + "-");
}
this.executor = initializeExecutor(this.threadFactory, this.rejectedExecutionHandler);
}
this.executor 是 ExecutorService 实现,比如 ThreadPoolExecutor 或者 ScheduledExecutorService,具体依赖于 initializeExecutor 抽象方法实现。
@Override
public void destroy() {
shutdown();
}
/**
* Perform a shutdown on the underlying ExecutorService.
* @see java.util.concurrent.ExecutorService#shutdown()
* @see java.util.concurrent.ExecutorService#shutdownNow()
*/
public void shutdown() {
if (logger.isDebugEnabled()) {
logger.debug("Shutting down ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));
}
if (this.executor != null) {
if (this.waitForTasksToCompleteOnShutdown) {
this.executor.shutdown();
}
else {
for (Runnable remainingTask : this.executor.shutdownNow()) {
cancelRemainingTask(remainingTask);
}
}
awaitTerminationIfNecessary(this.executor);
}
}
当 waitForTasksToCompleteOnShutdown 为true,等待未完成执行结束。
否则,立即取消先有任务。
特别注意:需要显示地指明 destroy 方法,比如:
@Bean(initMethod="prestartAllCoreThreads" , destroyMethod="shutdown")
public ScheduledThreadPoolExecutor executor(){
return new ScheduledThreadPoolExecutor();
}
小于 Spring Boot 2.3 的话,可以参考该代码来实现。
Spring Kafka、Spring AMQP 等
Spring Bean 销毁逻辑与 Spring Cloud 应用关闭时实习细节。
ContextClosedEvent 事件触发时机会早于 Spring Beans 销毁
ContextClosedEvent < SmartLifecycle < Spring Beans 销毁
JVM Runtime Shutdown 来整合,信号的方式来控制 Spring 应用上下文的关闭。
SqlSession 和 SqlSessionFactory 的默认实现均不是线程安全的,不过在企业并发场景中不会遇到线程安全问题。
判断一个对象是否存在线程安全:
- 该对象是否有状态
- 有状态,可能会面临线程安全风险
- 只读状态是没有分享
- 无状态,没有线程安全分享
- 有状态,可能会面临线程安全风险
- 该对象是否为容器对象
DefaultSqlSessionFactory 可以通过 getConfiguration() 拿到 Configuration 对象,因此它可能会多线程修改。
DefaultSqlSession 也依赖于 Configuration 对象,它也不是严格意义上的线程安全。
企业级 MyBatis 开发过程中,往往通过 Mapper 接口来操作 JDBC 数据。
Mapper 接口会生成 Mapper 动态代理
理论上,Mapper 每次执行会有触发 SqlSessionFactory 实现的 openSession 方法,来获取 SqlSession。
假设,某个 Mapper 执行了 N 次,那么是否意味着 SqlSession 对象也会创建 N 次?
实际上,SqlSession 仅会创建一个。
SqlSessionFactory 底层实现,的确每次调用 openSession 都会创建 SqlSession 对象,但是 Mapper 代理对象
使用到的 SqlSessionFactory 对象,并非是 SqlSessionFactory 底层对象,而是被包装成 SqlSessionManager
SqlSessionFactory 底层实现调用 openSession 方法时,会使用 TransactionFactory 来创建 Transaction 对象,该对象能够返回 JDBC 底层的 Connnection 对象。
从 Mybatis Spring 角度分析,Mapper Bean 是通过 MapperFactoryBean 创建,调用其 getObject() 方法:
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
MyBatis JDBC Connection 与 Spring JDBC Connection,从代码上来看,如果 Spring 与 MyBatis 整合,Spring Connection 与 Mybatis Connection 是独立的?
Spring Connection 来自于 Spring DataSource Bean
Mybatis Connection 来自于 DataSource#getConnection
在 MyBatis Spring 中提供了一个 SqlSessionFactory 的实现,即 SpringManagedTransactionFactory:
@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
return new SpringManagedTransaction(dataSource);
}
private void openConnection() throws SQLException {
this.connection = DataSourceUtils.getConnection(this.dataSource);
this.autoCommit = this.connection.getAutoCommit();
this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
LOGGER.debug(() -> "JDBC Connection [" + this.connection + "] will"
+ (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring");
}