动态注册bean到spring容器中

目录

一 dynamic-tp核心代码

二、注入方式

三、代码实现

四、运行效果

五、更新核心线程数数量


学习dynamic-tp时,思考如何通过数据库配置达到动态加载bean到spring容器中呢? 适配项目的最少维护代码量设计

案例:当某个时间段需要动态的调整线程池的大小,传统做法修改代码重启服务,如何能做到像

dynamic-tp一样,只需要通过修改数据库配置(最终页面可视化配置)就能达到调整,从而极大提高开发效率

以下代码仅展示如何动态加载到spring容器中,如何修改配置后刷,则需要实现调用接口反过来修改属性值实现,方式可参考dynamic-tp ,后期spring容器bean然后再设置属性[此处用常规办法使用BeanPostProcessor]

一 dynamic-tp核心代码

org.dromara.dynamictp.core.spring.DtpBeanDefinitionRegistrar#registerBeanDefinitions

github项目:https://github.com/dromara/dynamic-tp

官网地址:项目背景 | dynamic-tp

二、注入方式

   (需要了解bean加载到spring容器的过程,其中生成有效的BeanDefinition注入是关键)

  这里选择的注入方式和dynamic-tp一样,实现 ImportBeanDefinitionRegistrar 通过配置类或者启   动类导入方式注册到容器中

三、代码实现

如下代码展示基础部分

@SpringBootApplication
@Import(InitDBThreadPool.class) //此处导入实现 ImportBeanDefinitionRegistrar的类
public class PdfApplication {

public static void main(String[] args) {
SpringApplication.run(PdfApplication.class, args);
}
}

 核心类,自定义启动加载类

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * description: 注册db参数的配置到spring容器中
 */
@Slf4j
public class InitDBThreadPool implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        log.info("调用了registerBeanDefinitions......");
        /**
         * 注册 bean到spring容器中
         */
        Map<String, Object> propertyValues = new HashMap<>();
        propertyValues.put("corePoolSize", 2);
        propertyValues.put("maxPoolSize", 4);
        propertyValues.put("keepAliveSeconds", 60);
        propertyValues.put("queueCapacity", 100);
        propertyValues.put("allowCoreThreadTimeOut", false);
        propertyValues.put("rejectedExecutionHandler", new ThreadPoolExecutor.AbortPolicy());
        propertyValues.put("threadGroup", "haohaounique");
        propertyValues.put("beanName", "tuTask");
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(ThreadPoolTaskExecutor.class);
        for (Map.Entry<String, Object> entry : propertyValues.entrySet()) {
            beanDefinition.getPropertyValues().addPropertyValue(entry.getKey(), entry.getValue());
        }

        /**
         * 以上可通过调用db的数据进行动态加载进入
         */
        registry.registerBeanDefinition("tuTask", beanDefinition);
        log.info("注册完成registerBeanDefinitions......");
    }
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * description: 动态注册bean到spring容器中调用
 */
@RestController
@Slf4j
public class DBThreadTask {


    @Resource(name = "tuTask")
    private ThreadPoolTaskExecutor executor;

    @RequestMapping("/getTask")
    public Object getTask() {

        executor.execute(() -> {
            System.out.println("调用成功");
        });
        return "ok";
    }
}

四、运行效果

http://localhost:9002/getTask

jvisualvm.exe 展示效果(jdk bin目录下的工具)

五、更新核心线程数数量

实现BeanPostProcessor的postProcessAfterInitialization 方法,  在方法调用后获取到容器中的bean并设置对应的参数,调用一下postProcessAfterInitialization方法

import com.unique.utils.SpringBeanUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * description: 动态注册bean到spring容器中调用
 */
@RestController
@Slf4j
public class DBThreadTask implements BeanPostProcessor {


    @Autowired
    private SpringBeanUtils springBeanUtils;

    @Resource(name = "tuTask")
    private ThreadPoolTaskExecutor executor;

    @RequestMapping("/getTask")
    public Object getTask() {

        executor.execute(() -> {
            System.out.println("调用成功");
        });
        return "ok";
    }

    @RequestMapping("/updateTask")
    public Object updateTask() {
        ThreadPoolTaskExecutor tuTask = (ThreadPoolTaskExecutor) springBeanUtils.getBean("tuTask");
        tuTask.setCorePoolSize(8);
        postProcessAfterInitialization(tuTask, "tuTask");
        return "ok";
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * description:
 */
@Component
public class SpringBeanUtils implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public Object getBean(String name) {

        return applicationContext.getBean(name);
    }

    public <T> T getBean(Class<T> aclass) {
        return applicationContext.getBean(aclass);
    }


}

效果展示

多次调用:http://localhost:9002/getTask   

 http://localhost:9002/updateTask

jstack 查看栈区情况 jstack -l  pid

 jstack -l 11492
 

总结:动态加载bean和刷新bean的前提是要对spring容器启动加载bean的过程需要有一个很好的理解,借助一些资料就能够达到自己设计的目的

猜你喜欢

转载自blog.csdn.net/haohaounique/article/details/131823320
今日推荐