mirror of
				https://github.com/continew-org/continew-starter.git
				synced 2025-10-26 19:00:53 +08:00 
			
		
		
		
	feat: 新增线程池自动配置
This commit is contained in:
		| @@ -43,6 +43,9 @@ import top.charles7c.continew.starter.core.constant.StringConsts; | ||||
| @EnableConfigurationProperties(CorsProperties.class) | ||||
| public class CorsAutoConfiguration { | ||||
|  | ||||
|     /** | ||||
|      * 跨域过滤器 | ||||
|      */ | ||||
|     @Bean | ||||
|     @ConditionalOnMissingBean | ||||
|     public CorsFilter corsFilter(CorsProperties properties) { | ||||
| @@ -67,7 +70,7 @@ public class CorsAutoConfiguration { | ||||
|         UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); | ||||
|         source.registerCorsConfiguration("/**", config); | ||||
|         CorsFilter corsFilter = new CorsFilter(source); | ||||
|         log.info("[ContiNew Starter] - Auto Configuration 'Cors' completed initialization."); | ||||
|         log.info("[ContiNew Starter] - Auto Configuration 'CorsFilter' completed initialization."); | ||||
|         return corsFilter; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,78 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.starter.core.autoconfigure.threadpool; | ||||
|  | ||||
| import cn.hutool.core.util.ArrayUtil; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.scheduling.annotation.AsyncConfigurer; | ||||
| import org.springframework.scheduling.annotation.EnableAsync; | ||||
| import top.charles7c.continew.starter.core.exception.BaseException; | ||||
|  | ||||
| import java.util.Arrays; | ||||
| import java.util.concurrent.Executor; | ||||
| import java.util.concurrent.ScheduledExecutorService; | ||||
|  | ||||
| /** | ||||
|  * 异步任务自动配置 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @author Lion Li(RuoYi-Vue-Plus) | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @Slf4j | ||||
| @Lazy | ||||
| @Configuration(proxyBeanMethods = false) | ||||
| @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true") | ||||
| @EnableAsync(proxyTargetClass = true) | ||||
| public class AsyncAutoConfiguration implements AsyncConfigurer { | ||||
|  | ||||
|     private final ScheduledExecutorService scheduledExecutorService; | ||||
|  | ||||
|     public AsyncAutoConfiguration(ScheduledExecutorService scheduledExecutorService) { | ||||
|         this.scheduledExecutorService = scheduledExecutorService; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 异步任务 @Async 执行时,使用 Java 内置线程池 | ||||
|      */ | ||||
|     @Override | ||||
|     public Executor getAsyncExecutor() { | ||||
|         log.info("[ContiNew Starter] - Auto Configuration 'AsyncConfigurer' completed initialization."); | ||||
|         return scheduledExecutorService; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 异步任务执行时的异常处理 | ||||
|      */ | ||||
|     @Override | ||||
|     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { | ||||
|         return (throwable, method, objects) -> { | ||||
|             throwable.printStackTrace(); | ||||
|             StringBuilder sb = new StringBuilder(); | ||||
|             sb.append("Exception message: ").append(throwable.getMessage()).append(", Method name: ") | ||||
|                 .append(method.getName()); | ||||
|             if (ArrayUtil.isNotEmpty(objects)) { | ||||
|                 sb.append(", Parameter value: ").append(Arrays.toString(objects)); | ||||
|             } | ||||
|             throw new BaseException(sb.toString()); | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,90 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.starter.core.autoconfigure.threadpool; | ||||
|  | ||||
| import cn.hutool.core.thread.ThreadUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||||
| import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | ||||
| import top.charles7c.continew.starter.core.util.ExceptionUtils; | ||||
|  | ||||
| import java.util.concurrent.ScheduledExecutorService; | ||||
| import java.util.concurrent.ScheduledThreadPoolExecutor; | ||||
| import java.util.concurrent.ThreadPoolExecutor; | ||||
|  | ||||
| /** | ||||
|  * 线程池自动配置 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @author Lion Li(RuoYi-Vue-Plus) | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @Slf4j | ||||
| @Lazy | ||||
| @Configuration(proxyBeanMethods = false) | ||||
| @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true") | ||||
| @EnableConfigurationProperties(ThreadPoolProperties.class) | ||||
| public class ThreadPoolAutoConfiguration { | ||||
|  | ||||
|     /** 核心(最小)线程数 = CPU 核心数 + 1 */ | ||||
|     private final int corePoolSize = Runtime.getRuntime().availableProcessors() + 1; | ||||
|  | ||||
|     /** | ||||
|      * Spring 内置线程池:ThreadPoolTaskExecutor | ||||
|      */ | ||||
|     @Bean | ||||
|     public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties properties) { | ||||
|         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); | ||||
|         // 核心(最小)线程数 | ||||
|         executor.setCorePoolSize(ObjectUtil.defaultIfNull(properties.getCorePoolSize(), corePoolSize)); | ||||
|         // 最大线程数 | ||||
|         executor.setMaxPoolSize(ObjectUtil.defaultIfNull(properties.getMaxPoolSize(), corePoolSize * 2)); | ||||
|         // 队列容量 | ||||
|         executor.setQueueCapacity(properties.getQueueCapacity()); | ||||
|         // 活跃时间 | ||||
|         executor.setKeepAliveSeconds(properties.getKeepAliveSeconds()); | ||||
|         // 配置当池内线程数已达到上限的时候,该如何处理新任务:不在新线程中执行任务,而是由调用者所在的线程来执行 | ||||
|         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); | ||||
|         log.info("[ContiNew Starter] - Auto Configuration 'ThreadPoolTaskExecutor' completed initialization."); | ||||
|         return executor; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Java 内置线程池:ScheduledExecutorService(适用于执行周期性或定时任务) | ||||
|      */ | ||||
|     @Bean | ||||
|     @ConditionalOnMissingBean | ||||
|     public ScheduledExecutorService scheduledExecutorService(ThreadPoolProperties properties) { | ||||
|         ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(ObjectUtil.defaultIfNull(properties.getCorePoolSize(), corePoolSize), | ||||
|                 ThreadUtil.newNamedThreadFactory("schedule-pool-%d", true), | ||||
|                 new ThreadPoolExecutor.CallerRunsPolicy()) { | ||||
|             @Override | ||||
|             protected void afterExecute(Runnable runnable, Throwable throwable) { | ||||
|                 super.afterExecute(runnable, throwable); | ||||
|                 ExceptionUtils.printException(runnable, throwable); | ||||
|             } | ||||
|         }; | ||||
|         log.info("[ContiNew Starter] - Auto Configuration 'ScheduledExecutorService' completed initialization."); | ||||
|         return executor; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,57 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.starter.core.autoconfigure.threadpool; | ||||
|  | ||||
| import lombok.Data; | ||||
| import org.springframework.boot.context.properties.ConfigurationProperties; | ||||
|  | ||||
| /** | ||||
|  * 线程池配置属性 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @author Lion Li(RuoYi-Vue-Plus) | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @Data | ||||
| @ConfigurationProperties(prefix = "thread-pool") | ||||
| public class ThreadPoolProperties { | ||||
|  | ||||
|     /** | ||||
|      * 是否启用 | ||||
|      */ | ||||
|     private boolean enabled = false; | ||||
|  | ||||
|     /** | ||||
|      * 核心/最小线程数(默认:CPU 核心数 + 1) | ||||
|      */ | ||||
|     private Integer corePoolSize; | ||||
|  | ||||
|     /** | ||||
|      * 最大线程数(默认:(CPU 核心数 + 1) * 2) | ||||
|      */ | ||||
|     private Integer maxPoolSize; | ||||
|  | ||||
|     /** | ||||
|      * 队列容量 | ||||
|      */ | ||||
|     private int queueCapacity = 128; | ||||
|  | ||||
|     /** | ||||
|      * 活跃时间(单位:秒) | ||||
|      */ | ||||
|     private int keepAliveSeconds = 300; | ||||
| } | ||||
| @@ -0,0 +1,33 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.starter.core.exception; | ||||
|  | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| /** | ||||
|  * 自定义异常基类 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @NoArgsConstructor | ||||
| public class BaseException extends RuntimeException { | ||||
|  | ||||
|     public BaseException(String message) { | ||||
|         super(message); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,160 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * <p> | ||||
|  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * <p> | ||||
|  * http://www.gnu.org/licenses/lgpl.html | ||||
|  * <p> | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.starter.core.util; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import top.charles7c.continew.starter.core.constant.StringConsts; | ||||
|  | ||||
| import java.util.concurrent.CancellationException; | ||||
| import java.util.concurrent.ExecutionException; | ||||
| import java.util.concurrent.Future; | ||||
| import java.util.function.Consumer; | ||||
|  | ||||
| /** | ||||
|  * 异常工具类 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 1.0.0 | ||||
|  */ | ||||
| @Slf4j | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class ExceptionUtils { | ||||
|  | ||||
|     /** | ||||
|      * 打印线程异常信息 | ||||
|      * | ||||
|      * @param runnable | ||||
|      *            线程执行内容 | ||||
|      * @param throwable | ||||
|      *            异常 | ||||
|      */ | ||||
|     public static void printException(Runnable runnable, Throwable throwable) { | ||||
|         if (null == throwable && runnable instanceof Future<?> future) { | ||||
|             try { | ||||
|                 if (future.isDone()) { | ||||
|                     future.get(); | ||||
|                 } | ||||
|             } catch (CancellationException e) { | ||||
|                 throwable = e; | ||||
|             } catch (ExecutionException e) { | ||||
|                 throwable = e.getCause(); | ||||
|             } catch (InterruptedException e) { | ||||
|                 Thread.currentThread().interrupt(); | ||||
|             } | ||||
|         } | ||||
|         if (null != throwable) { | ||||
|             log.error(throwable.getMessage(), throwable); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 如果有异常,返回 null | ||||
|      * | ||||
|      * @param exSupplier | ||||
|      *            可能会出现异常的方法执行 | ||||
|      * @param <T> | ||||
|      *            / | ||||
|      * @return / | ||||
|      */ | ||||
|     public static <T> T exToNull(ExSupplier<T> exSupplier) { | ||||
|         return exToDefault(exSupplier, null); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 如果有异常,执行异常处理 | ||||
|      * | ||||
|      * @param supplier | ||||
|      *            可能会出现异常的方法执行 | ||||
|      * @param exConsumer | ||||
|      *            异常处理 | ||||
|      * @param <T> | ||||
|      *            / | ||||
|      * @return / | ||||
|      */ | ||||
|     public static <T> T exToNull(ExSupplier<T> supplier, Consumer<Exception> exConsumer) { | ||||
|         return exToDefault(supplier, null, exConsumer); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 如果有异常,返回空字符串 | ||||
|      * | ||||
|      * @param exSupplier | ||||
|      *            可能会出现异常的方法执行 | ||||
|      * @return / | ||||
|      */ | ||||
|     public static String exToBlank(ExSupplier<String> exSupplier) { | ||||
|         return exToDefault(exSupplier, StringConsts.EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 如果有异常,返回默认值 | ||||
|      * | ||||
|      * @param exSupplier | ||||
|      *            可能会出现异常的方法执行 | ||||
|      * @param defaultValue | ||||
|      *            默认值 | ||||
|      * @param <T> | ||||
|      *            / | ||||
|      * @return / | ||||
|      */ | ||||
|     public static <T> T exToDefault(ExSupplier<T> exSupplier, T defaultValue) { | ||||
|         return exToDefault(exSupplier, defaultValue, null); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 如果有异常,执行异常处理,返回默认值 | ||||
|      * | ||||
|      * @param exSupplier | ||||
|      *            可能会出现异常的方法执行 | ||||
|      * @param defaultValue | ||||
|      *            默认值 | ||||
|      * @param exConsumer | ||||
|      *            异常处理 | ||||
|      * @param <T> | ||||
|      *            / | ||||
|      * @return / | ||||
|      */ | ||||
|     public static <T> T exToDefault(ExSupplier<T> exSupplier, T defaultValue, Consumer<Exception> exConsumer) { | ||||
|         try { | ||||
|             return exSupplier.get(); | ||||
|         } catch (Exception e) { | ||||
|             if (null != exConsumer) { | ||||
|                 exConsumer.accept(e); | ||||
|             } | ||||
|             return defaultValue; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 异常提供者 | ||||
|      * | ||||
|      * @param <T> | ||||
|      *            / | ||||
|      */ | ||||
|     public interface ExSupplier<T> { | ||||
|         /** | ||||
|          * 获取返回值 | ||||
|          * | ||||
|          * @return / | ||||
|          * @throws Exception | ||||
|          *             / | ||||
|          */ | ||||
|         T get() throws Exception; | ||||
|     } | ||||
| } | ||||
| @@ -1 +1,3 @@ | ||||
| top.charles7c.continew.starter.core.autoconfigure.cors.CorsAutoConfiguration | ||||
| top.charles7c.continew.starter.core.autoconfigure.threadpool.ThreadPoolAutoConfiguration | ||||
| top.charles7c.continew.starter.core.autoconfigure.threadpool.AsyncAutoConfiguration | ||||
		Reference in New Issue
	
	Block a user