项目拓展-Spring实现策略类统一管理
使用Spring将所有的策略分类进行统一管理
基类-Container基本加载容器
BeanContainer接口-定义基本方法
package com.kira.scaffoldmvc.Strategy;import java.util.Collection;
import java.util.Map;public interface BeanContainer<K, V> {V getBean(K k);Collection<V> getBeans();Map<K, V> getBeansMap();
}
DefaultBeanContainer-抽象类-定义Bean的初始化处理逻辑
连接ApplicationContextAware接口
为了获取Spring的上下文
Bean 能够获取 Spring 的 ApplicationContext(应用上下文),从而访问 Spring 容器中的其他 Bean 或资源
连接InitializingBean-执行Bean的初始化方法
afterPropertiesSet()方法进行初始化,将该Bean的类型Class赋值给成员变量,将该Bean的类型进行保留
并且在初始化的过程中执行loadBean()方法
loadBean()方法其实就是创建我们的BeanMap,对bean对象进行统一管理
这个createBeanMap()方法是抽象方法,我们会在子实现类对createBeanMap()方法进行重写
public void loadBeans() {this.beanMap = this.createBeanMap();}
源代码
package com.kira.scaffoldmvc.Strategy;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;/*** 默认的Bean容器实现,用于管理和存储类型为V的策略Bean,通过键K进行索引* 实现了ApplicationContextAware接口以获取Spring应用上下文* 实现了InitializingBean接口以便在Bean属性设置完成后执行初始化逻辑* 实现了BeanContainer接口定义的核心方法*/
//ApplicationContextAware 接口,让 Bean 能够获取 Spring 的 ApplicationContext(应用上下文),从而访问 Spring 容器中的其他 Bean 或资源
//InitializingBean 接口,当一个 Bean 实现了 InitializingBean 接口,Spring 在完成该 Bean 的所有属性注入后(初始化),会自动调用 afterPropertiesSet() 方法
//类似于 XML 配置中的 init-method 或 @PostConstruct 注解
public abstract class DefaultBeanContainer<K, V> implements ApplicationContextAware, InitializingBean, BeanContainer<K, V> {// Spring应用上下文,用于获取容器中的Beanprotected ApplicationContext applicationContext;// 泛型参数V的实际Class类型,通过反射获取protected Class<?> type;// 存储键值对的内部映射,键为K,值为V类型的Beanprivate Map<K, V> beanMap;/*** 加载所有策略Bean到容器中* 调用抽象方法createBeanMap()获取映射关系并赋值给内部beanMap*/public void loadBeans() {this.beanMap = this.createBeanMap();}/*** 由子类实现的抽象方法,用于创建具体的Bean映射关系* 通常通过applicationContext.getBeansOfType()方法获取特定类型的所有Bean* @return 包含键值映射关系的Map*/protected abstract Map<K, V> createBeanMap();/*** Bean初始化完成后自动调用的方法* 1. 通过反射获取泛型参数V的实际类型* 2. 调用loadBeans()方法加载所有策略Bean*/@Overridepublic void afterPropertiesSet() {// 获取当前类的泛型父类类型Type genericSuperclass = this.getClass().getGenericSuperclass();// 转换为参数化类型以便获取泛型参数ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;// 获取实际的泛型参数数组(对于DefaultBeanContainer<K, V>有两个参数)Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();// 获取第二个泛型参数V的类型Type valueType = actualTypeArguments[1];// 处理泛型参数是带泛型的类型的情况(例如List<String>)if (valueType instanceof ParameterizedType) {this.type = (Class<?>) ((ParameterizedType) valueType).getRawType();} else {// 普通类型直接转换为Classthis.type = (Class<?>) actualTypeArguments[1];}// 加载所有策略Beanthis.loadBeans();}/*** 实现ApplicationContextAware接口的方法,由Spring自动调用注入应用上下文* @param applicationContext Spring应用上下文*/@Overridepublic void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}/*** 根据键获取对应的策略Bean* @param key 查找Bean的键* @return 对应的策略Bean,如果不存在则返回null*/@Overridepublic V getBean(K key) {return beanMap.get(key);}/*** 获取容器中所有策略Bean的集合* @return 包含所有策略Bean的集合*/@Overridepublic Collection<V> getBeans() {return new ArrayList<>(beanMap.values());}/*** 获取容器中所有策略Bean的不可变映射* @return 包含所有键值对的Map副本*/@Overridepublic Map<K, V> getBeansMap() {return new HashMap<>(beanMap);}
}
IBeanType
package com.kira.scaffoldmvc.Strategy;public interface IBeanType<K> {K beanType();}
在子类中返回对应变量,这个变量是作为MultipleBeanContainer和DefaultBeanContainer的Key来管理对应的策略类的
多实例Bean容器-MultipleBeanContainer
将同一类 用List<>进行统一管理
例如Map<策略实现类父类.Class,List<策略实现类>>
也就是用一个Key,管理多个策略实现类
并且已经写好了按照Order优先级顺序加载
package com.kira.scaffoldmvc.Strategy;import org.springframework.core.annotation.AnnotationAwareOrderComparator;import java.util.*;
import java.util.stream.Collectors;/*** 多实例Bean容器,用于管理同一类型键对应多个策略Bean的场景* 继承自DefaultBeanContainer,泛型参数K为键类型,V为策略Bean类型且需实现IBeanType接口* 容器中存储的Value类型为List<V>,支持同一键对应多个策略Bean*/
public abstract class MultipleBeanContainer<K, V extends IBeanType<K>> extends DefaultBeanContainer<K, List<V>> {/*** 创建Bean映射关系的核心方法(由父类DefaultBeanContainer调用)* 实现逻辑:* 1. 从Spring容器中获取所有指定类型的策略Bean* 2. 按beanType()方法的返回值对Bean进行分组* 3. 对每组Bean按@Order注解排序(或实现Ordered接口)* @return 键为K、值为List<V>的映射表*/@Overrideprotected Map<K, List<V>> createBeanMap() {// 获取Spring容器中所有类型为this.type的Bean实例(值类型为V)Collection<V> beanList = (Collection<V>) this.applicationContext.getBeansOfType(this.type).values();// 使用Stream API对Bean进行分组处理Map<K, List<V>> beanMap = beanList.stream()// 过滤掉beanType()返回null的无效Bean.filter(bean -> bean.beanType() != null)// 分组归约操作:// - 初始值:容量为8的HashMap// - 累加器:将每个Bean按beanType()分组到List中// - 组合器:并行流时合并两个Map(此处简单返回a).reduce(new HashMap<>(8), (beansMap, bean) -> {// 获取当前Bean对应的键(通过IBeanType接口的beanType()方法)K beanType = bean.beanType();// 从Map中获取该键对应的Bean列表,不存在则创建新ArrayListList<V> existedBeanList = beansMap.getOrDefault(beanType, new ArrayList<>());// 将当前Bean添加到列表中existedBeanList.add(bean);// 更新Map中的列表beansMap.put(beanType, existedBeanList);return beansMap;}, (a, b) -> a); // 并行流时的合并逻辑(此处无实际作用)// 对每个键对应的Bean列表按@Order注解或Ordered接口排序for (List<V> beans : beanMap.values()) {AnnotationAwareOrderComparator.sort(beans);}return beanMap;}
}
单实例Bean容器-SingleBeanContainer
单个Key对应单个Value
也就是一个策略对应一个策略,之前那个是用List<>将同一策略的类用List收集起来
这个就是通过一个策略父类能找到一个实现类
并且已经写好了按照Order优先级顺序加载
package com.kira.scaffoldmvc.Strategy;import org.springframework.beans.factory.InitializingBean;import java.util.Collection;
import java.util.HashMap;
import java.util.Map;/*** 单实例 Bean 容器,用于管理每个键对应单个策略 Bean 的场景* 继承自 DefaultBeanContainer,泛型参数 K 为键类型,V 为策略 Bean 类型且需实现 IBeanType 接口* 容器中存储的每个键对应唯一的策略 Bean,若存在重复键会被覆盖*/
public abstract class SingleBeanContainer<K, V extends IBeanType<K>> extends DefaultBeanContainer<K, V> implements InitializingBean {/*** 创建 Bean 映射关系的核心方法(由父类 DefaultBeanContainer 调用)* 实现逻辑:* 1. 从 Spring 容器中获取所有指定类型的策略 Bean* 2. 按 beanType() 方法的返回值作为键,将 Bean 存入 Map* 3. 若存在相同键的 Bean,后处理的 Bean 会覆盖先处理的 Bean* @return 键为 K、值为 V 的映射表*/@Overridepublic Map<K, V> createBeanMap() {// 获取 Spring 容器中所有类型为 this.type 的 Bean 实例(值类型为 V)Collection<V> beans = (Collection<V>) this.applicationContext.getBeansOfType(this.type).values();// 使用 Stream API 将 Bean 按 beanType() 分组到 Map 中return beans.stream()// 过滤掉 beanType() 返回 null 的无效 Bean.filter(bean -> bean.beanType() != null)// 归约操作:将每个 Bean 按 beanType() 作为键存入 Map.reduce(new HashMap<>(8), (beansMap, bean) -> {// 若存在相同键的 Bean,后处理的 Bean 会覆盖先处理的 BeanbeansMap.put(bean.beanType(), bean);return beansMap;}, (a, b) -> a); // 并行流时的合并逻辑(此处无实际作用)}
}
使用MultipleBeanContainer实现多个策略类组合优雅关闭Java线程
关闭策略方法接口-CloseStrategy
这是一个关闭策略接口,连接IBeanType
因为这是在java程序关闭的时候执行的自定方法
所以只有一个方法,也就是close()
package com.kira.scaffoldmvc.ShutDownHook;import com.kira.scaffoldmvc.Strategy.IBeanType;public interface CloseStrategy extends IBeanType<String> {void close();}
IBeanType<>的意义
package com.kira.scaffoldmvc.Strategy;public interface IBeanType<K> {K beanType();
}
在子类中返回对应变量,这个变量是作为MultipleBeanContainer和DefaultBeanContainer的Key来管理对应的策略类的
CloseStrategyContainer-管理所有的Close关闭策略
这是一个容器,里面通过键值对的方式装填了所有的CloseStrategy关闭策略实现类
package com.kira.scaffoldmvc.ShutDownHook;import com.kira.scaffoldmvc.Strategy.MultipleBeanContainer;
import org.springframework.stereotype.Component;/*** 关闭策略容器,用于管理所有实现了 CloseStrategy 接口的策略*/
@Component
public class CloseStrategyContainer extends MultipleBeanContainer<String, CloseStrategy> {}
ThreadPoolCloseStrategy-线程池关闭策略实现类
"close"字符串作为Key来管理所有的关闭策略
//在 MultipleBeanContainer 和 DefaultBeanContainer 中,该方法的返回值会被用作存储策略 Bean 的 Map 集合的键(Key)@Overridepublic String beanType() {//close作为Key,存储所有的关闭策略return "close";}
package com.kira.scaffoldmvc.ShutDownHook;import com.kira.scaffoldmvc.Strategy.IBeanType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;@Slf4j
@Component
public class ThreadPoolCloseStrategy implements CloseStrategy {@Autowired(required = false)private Map<String, ThreadPoolExecutor> threadPoolExecutorMap;@Overridepublic void close() {log.info("Spring容器销毁,开始关闭线程池...");shutdownAllExecutorServices();}//在 MultipleBeanContainer 和 DefaultBeanContainer 中,该方法的返回值会被用作存储策略 Bean 的 Map 集合的键(Key)@Overridepublic String beanType() {//close作为Key,存储所有的关闭策略return "close";}/*** 优雅关闭所有线程池,确保所有任务执行完成*/public void shutdownAllExecutorServices() {if (threadPoolExecutorMap != null && !threadPoolExecutorMap.isEmpty()) {threadPoolExecutorMap.forEach((name, executor) -> {log.info("正在关闭线程池: " + name);shutdownExecutorServiceCompletely(name, executor);});}}/*** 优雅关闭线程池,确保所有任务执行完成** @param poolName 线程池名称* @param executor 线程池实例*/private void shutdownExecutorServiceCompletely(String poolName, ExecutorService executor) {// 停止接收新任务executor.shutdown();// 等待所有任务执行完成,不设置超时try {// 定期检查线程池状态while (!executor.awaitTermination(5, TimeUnit.SECONDS)) {// 输出剩余任务信息,方便监控if (executor instanceof ThreadPoolExecutor) {ThreadPoolExecutor threadPool = (ThreadPoolExecutor) executor;log.info("线程池[{}]关闭中: 活跃线程数={}, 队列任务数={}, 已完成任务数={}, 总任务数={}",poolName,threadPool.getActiveCount(),threadPool.getQueue().size(),threadPool.getCompletedTaskCount(),threadPool.getTaskCount());}}log.info("线程池[{}]已完全关闭,所有任务执行完成", poolName);} catch (InterruptedException ie) {// 被中断时,继续尝试关闭log.info("线程池[{}]关闭过程被中断,继续尝试关闭...", poolName);Thread.currentThread().interrupt();//将中断标志为设为true,方面后面逻辑拓展//当我们抛出错误后,为了保证这个线程池的任务执行完我们选择继续等待,而不是shutdownNow()// 注意:这里不调用shutdownNow(),确保任务完成}}}
@PreDestory-实现Java程序的优雅关闭
获取关闭策略类,通过Bean拿出所有的关闭策略然后调用close()方法进行执行
package com.kira.scaffoldmvc;import com.kira.scaffoldmvc.ShutDownHook.CloseStrategy;
import com.kira.scaffoldmvc.ShutDownHook.CloseStrategyContainer;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;@SpringBootApplication
@Slf4j
public class ScaffoldMvcApplication {@Autowired(required = false)private Map<String, ThreadPoolExecutor> threadPoolExecutorMap;public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(ScaffoldMvcApplication.class, args);// 获取应用实例ScaffoldMvcApplication application = context.getBean(ScaffoldMvcApplication.class);// // 注册 JVM 关闭钩子
// Runtime.getRuntime().addShutdownHook(new Thread(() -> {
// log.info("JVM 关闭钩子触发,开始优雅关闭线程池...");
// application.shutdownAllExecutorServices();
// log.info("所有线程池已优雅关闭,所有任务执行完成");
// }));}//获取所有的关闭策略类@AutowiredCloseStrategyContainer closeStrategyContainer;// 同时保留@PreDestroy作为备选关闭方式@PreDestroypublic void onDestroy() {// 获取所有"close"类型的关闭策略,然后执行策略类的close()关闭方法List<CloseStrategy> strategies = closeStrategyContainer.getBean("close");if (strategies != null) {for (CloseStrategy strategy : strategies) {strategy.close(); // 执行关闭操作}}}}