项目拓展-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应用上下文,用于获取容器中的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(); // 执行关闭操作
            }
        }

    }

}
相关推荐
信码由缰4 分钟前
单体架构中的事件驱动架构:Java应用程序的渐进式重构
java
用户11063097550631 分钟前
CSDN-uniapp陪诊小程序
后端
初学小白...36 分钟前
实现Runnable接口
java·开发语言
37手游后端团队38 分钟前
构建AI会话质检平台:技术架构与实践分享
人工智能·后端
JavaArchJourney1 小时前
分布式事务与最终一致性
分布式·后端
阿杰AJie1 小时前
Jackson 常用注解与完整用法总结
后端·sql
李少兄1 小时前
记一次 Spring Boot 项目中 Redis 工具类的重构实践
spring boot·redis·重构
墨着染霜华1 小时前
Java Optional orElse orElseGet orElseThrow()
java
czhc11400756631 小时前
JAVA1026 方法;类:抽象类、抽象类继承;接口、接口继承 Linux:Mysql
java·linux·mysql
智能小怪兽1 小时前
ubuntu desktop激活root
后端