目录
一 dynamic-tp核心代码
二、注入方式
三、代码实现
四、运行效果
五、更新核心线程数数量
学习dynamic-tp时,思考如何通过数据库配置达到动态加载bean到spring容器中呢? 适配项目的最少维护代码量设计
案例:当某个时间段需要动态的调整线程池的大小,传统做法修改代码重启服务,如何能做到像
dynamic-tp一样,只需要通过修改数据库配置(最终页面可视化配置)就能达到调整,从而极大提高开发效率
以下代码仅展示如何动态加载到spring容器中,如何修改配置后刷,则需要实现调用接口反过来修改属性值实现,方式可参考dynamic-tp ,后期spring容器bean然后再设置属性[此处用常规办法使用BeanPostProcessor]
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 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 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 getBean(Class aclass) {
return applicationContext.getBean(aclass);
}
}
效果展示
多次调用:http://localhost:9002/getTask
http://localhost:9002/updateTask
jstack 查看栈区情况 jstack -l pid
jstack -l 11492
总结:动态加载bean和刷新bean的前提是要对spring容器启动加载bean的过程需要有一个很好的理解,借助一些资料就能够达到自己设计的目的