项目拓展-Spring实现策略类统一管理

发布于:2025-06-22 ⋅ 阅读:(15) ⋅ 点赞:(0)

使用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应用上下文,用于获取容器中的Bean
    protected ApplicationContext applicationContext;

    // 泛型参数V的实际Class类型,通过反射获取
    protected Class<?> type;

    // 存储键值对的内部映射,键为K,值为V类型的Bean
    private 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
     */
    @Override
    public 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 {
            // 普通类型直接转换为Class
            this.type = (Class<?>) actualTypeArguments[1];
        }

        // 加载所有策略Bean
        this.loadBeans();
    }

    /**
     * 实现ApplicationContextAware接口的方法,由Spring自动调用注入应用上下文
     * @param applicationContext Spring应用上下文
     */
    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 根据键获取对应的策略Bean
     * @param key 查找Bean的键
     * @return 对应的策略Bean,如果不存在则返回null
     */
    @Override
    public V getBean(K key) {
        return beanMap.get(key);
    }

    /**
     * 获取容器中所有策略Bean的集合
     * @return 包含所有策略Bean的集合
     */
    @Override
    public Collection<V> getBeans() {
        return new ArrayList<>(beanMap.values());
    }

    /**
     * 获取容器中所有策略Bean的不可变映射
     * @return 包含所有键值对的Map副本
     */
    @Override
    public 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>的映射表
     */
    @Override
    protected 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列表,不存在则创建新ArrayList
                    List<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 的映射表
     */
    @Override
    public 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 会覆盖先处理的 Bean
                    beansMap.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)
    @Override
    public 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;

    @Override
    public void close() {
        log.info("Spring容器销毁,开始关闭线程池...");
        shutdownAllExecutorServices();
    }

    //在 MultipleBeanContainer 和 DefaultBeanContainer 中,该方法的返回值会被用作存储策略 Bean 的 Map 集合的键(Key)
    @Override
    public 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("所有线程池已优雅关闭,所有任务执行完成");
//        }));

    }


    //获取所有的关闭策略类
    @Autowired
    CloseStrategyContainer closeStrategyContainer;

    // 同时保留@PreDestroy作为备选关闭方式
    @PreDestroy
    public void onDestroy() {

        // 获取所有"close"类型的关闭策略,然后执行策略类的close()关闭方法
        List<CloseStrategy> strategies = closeStrategyContainer.getBean("close");
        if (strategies != null) {
            for (CloseStrategy strategy : strategies) {
                strategy.close(); // 执行关闭操作
            }
        }

    }

}

网站公告

今日签到

点亮在社区的每一天
去签到