BeanDefinitionRegistryPostProcessor
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
//这段代码相当于 动态注册beanDefinition的作用
//RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(MyService.class);
//动态注册beanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyService.class);
BeanDefinition beanDefinition = builder.getBeanDefinition();
//动态注入属性
beanDefinition.getPropertyValues().add("age",18);
//动态设置beanDefinition信息
beanDefinition.setLazyInit(true);
// beanDefinition.setScope();
//...
//动态设置构造函数
// beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,...);
beanDefinitionRegistry.registerBeanDefinition("yuyang",beanDefinition);
}
//扩展性更高 更关注修改beanDefinition
//BeanFactoryPostProcessor 接口下的方法
//postProcessBeanDefinitionRegistry更关注注册beanDefinition
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
@import—ImportBeanDefinitionRegistrar
注意: 1. 必须要结合@Import
,单独配置为bean不会起作用!
2.ImportBeanDefinitionRegistrar不是一个bean, 没有bean的生命周期, 没有依赖注入功能。
但是! 它有一个优势, 注意它有一个importingClassMetadata参数, 这个参数可以获取@Import注解所在类的其他注解信息, 比如@MapperScan根据包创建beandefinition 。 这是BeanDefinitionRegistryPostProcessor不具备的!
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
System.out.println("===================");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyService.class);
BeanDefinition beanDefinition = builder.getBeanDefinition();
registry.registerBeanDefinition("myservice4",beanDefinition);
annotationMetadata.getAnnotationAttributes(ComponentScan.class.getName());
}
}
SpringIoc之Bean创建过程的扩展点详解
BeanPostProcessor
更多是为了Spring自己得扩展性, 为以后得版本升级留出更多的扩展余地。
也可以提供给程序员进行扩展,不同阶段的作用不同,实际根据情况进行选择。
不同阶段可以做不同的事情:
1. mainStart实例化前.如果返回了对象会中断bean生命周期
2. mainStart实例化中. .可以指定构造函数
3. mainStart实例化后. .为属性注入做准备,可以给beanDefinition指定注入的值
5. mainStart属性注入前. .返回true中断依赖注入
6. mainStart属性注入中. . @Autowired就是通过此bpp进行自动装配的
7. mainStart初始化前
8. mainStart初始化后: bean已经完整可以单独管理
Aware
基于底层扩展很少会用@Autowired来注入Spring组件, 因为顺序问题, 基本都会通过Aware获取组件
生命周期回调
- 如果通过aware获取组件, 那么肯定也会用初始化的回调方式进行初始化, 而不是用构造函数
- 用构造函数初始化, 由于构造函数在实例化这步, 获取不到像aware这些组件。
- 构造函数不确定性, 所以作为初始化不合适
SpringIoc之容器加载完毕的扩展点详解
SmartInitializingSingleton.
它其实是初始化回调的一个补充,可以再所有Bean创建完后初始化.比如想对一批bean一起同时做一些动作
bean初始化完成后调用SmartInitializingSingleton接口
初始化的一个补充
spring源码的onRefresh方法中
/**
* 在所有单例bean创建完后调用, 做初始化工作
* 比如需要依赖创建完后的bean 进行一些初始化工作
*
*/
@Component
public class MySmartInitializingSingleton implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
System.out.println("所有bean创建完后调用..");
}
}
SmartLifecycle
控制一个组件的生命周期 ,比如定时器组件\资源预热\缓存预热
- 容器启动完: 定时任务启动 / 缓存预热
- 容器关闭: 定时任务停止 / 缓存清空
所有同Spring容器同开启/关闭 的服务可以基于SmartLifecycle完成, 就不需要自己单独管理开启关闭了
/**
* 控制一个组件的生命周期 ,比如定时器组件\资源预热\缓存预热
* 容器启动完: 定时任务启动
* 容器关闭: 定时任务停止
*/
@Component
public class MyLifecycle implements SmartLifecycle {
boolean isRunning;
@Override
public void start() {
isRunning=true;
System.out.println("容器加载完毕,组件启动!");
}
@Override
public void stop() {
isRunning=false;
System.out.println("容器关闭,组件停止!");
}
// isRunning=false 调用 start isRunning=true 调用stop
@Override
public boolean isRunning() {
System.out.println("组件是否运行判断");
return isRunning;
}
@Override
public boolean isAutoStartup() {
return SmartLifecycle.super.isAutoStartup();
}
}
ContextRefreshedEvent
基于事件
@Component
public class ContextRefreshedEventListener{ //implements
ApplicationListener<ContextRefreshedEvent> {
//@Async
@EventListener(ContextRefreshedEvent.class)
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("______________\n容器加载完毕\n———————");
}
}
利用扩展点实现动态加载线程池小案例演练
此案例根据美团动态线程池dynamic-tp开源项目提取关键扩展点讲解
在开发中,关于线程池会遇到:
- 由于不同服务器的资源、不同时刻的请求量不一样, 代码中创建了一个 ThreadPoolExecutor,但是不知道那几个核心参数设置多少比较合适
- 参数设置好后,上线发现需要调整,改代码重启服务非常麻烦。
- 线程池相对于开发人员来说是个黑箱,运行情况在出现问题 前很难被感知。
public static void main(String[] args) {
// 创建ThreadPoolExecutor对象
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
workQueue,
handler
);
}
/*
corePoolSize:核心线程数,表示线程池中始终保持活动状态的线程数。
maximumPoolSize:最大线程数,表示线程池中可以同时执行的最大线程数。
keepAliveTime:空闲线程销毁的时间,表示当线程池中的线程数超过核心线程数时,多余的空闲线程在被销毁之前等待的最长时间。
TimeUnit:时间单位,用于指定keepAliveTime的单位。
workQueue:任务队列,用于存储待执行的任务。
handler:拒绝策略,用于处理无法执行的任务。
*/
实现思路:
利用SpringBoot的配置文件(后续还可以利用配置中心)
- 根据配置的参数, 动态创建线程池
- 将动态线程池bean交给Spring容器管理
- 后续使用线程池可以从Spring容器中获取动态线程池bean使用
- 后续修改可以直接对动态线程池bean进行修改
- 最好还能监控如果达到阈值进行(发邮件)警告。
需求1: 根据配置动态加载信息并且创建
spring:
dtp:
executors:
# 线程池1
- poolName: dtpExecutor1
corePoolSize: 5
maximumPoolSize: 10
#...其他参数
# 线程池2
- poolName: dtpExecutor2
corePoolSize: 2
maximumPoolSize: 15
#...其他参数
#线程池3\4\5
毫无疑问要一个Pojo类接收这些配置
@Data
public class DtpProperties {
private List<ThreadPoolProperties> executors;
}
@Data
public class ThreadPoolProperties {
/**
* 标识每个线程池的唯一名字
*/
private String poolName;
private String poolType = "common";
/**
* 是否为守护线程
*/
private boolean isDaemon = false;
/**
* 以下都是核心参数
*/
private int corePoolSize = 1;
private int maximumPoolSize = 1;
private long keepAliveTime;
private TimeUnit timeUnit = TimeUnit.SECONDS;
private String queueType = "arrayBlockingQueue";
private int queueSize = 5;
private String threadFactoryPrefix = "-td-";
private String RejectedExecutionHandler;
}
1.如何获取配置?
1、@Value
通过@Value单个获取;一个个设置,太麻烦
@Value("${com.tuling.bean.bean-class}")
private Class<?> beanClass;
// Todo... 一个个获取
2、@ConfigurationProperties
通过@ConfigurationProperties(prefix = “com.yuyang”)可以批量获取,比较方便
3、EnvironmentAware——选它!
Spring提供很多XXXAware接口、其中EnvironmentAware接口就可以通过其提供的Environment动态获取。
- 第一步:实现EnvironmentAware接口
@Component
public class TestEnvironmentAware implements EnvironmentAware {
@Override
public void setEnvironment(Environment environment) {
// Todo 绑定配置信息...
}
}
- 第二步:获取/绑定配置,提供两种方式:
a. 获取方式一:单个获取
public void setEnvironment(Environment environment) {
environment.getProperty("com.tuling.bean.bean-class");
// ToDo: 一个个获取更多配置信息..
}
b. 获取方式二:通过Binder绑定到properties对象
@Override
public void setEnvironment(Environment environment) {
BindResult<BeanProperties> bindResult =Binder.get(environment).bind("com.yuyang.bean", BeanProperties.class);
BeanProperties beanProperties= bindResult.get();
}
@Value 和@ConfigurationProperties 注解方式获取配置为什么不可以?Why?~
因为顺序原因!这里就要清楚:
@Value 和@ConfigurationProperties注解依赖BeanPostProcessor解析,要调用BeanPostProcessor就要先注册,而BeanPostProcessor的注册是在BeanDefinition
的注册之后的。
所以在注册BeanDefinition时是获取不到注解绑定的配置信息的:
- 动态创建Bean的几种方式:
注意!我们需要的是动态!动态!!是在运行过程中经过逻辑代码创建Bean, 不是通过配置、 @Component这种配置方式这种方式不能自由控制业务逻辑。
想要动态创建Bean先了解Bean创建的大概过程:
如果想动态注册Bean,可以通过先动态注册BeanDefintion即可,Spring提供了动态注册BeanDefinition的接口:
1、ImportBeanDefinitionRegistrar
第一步:创建实现ImportBeanDefinitionRegistrar接口的类, 演示了一个DeanDefintion的注册
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
GenericBeanDefinition beandefinition=new GenericBeanDefinition();
beandefinition.setBeanClassName("com.tuling.beans.TestComponent");
beandefinition.getPropertyValues().add("id",1);
beandefinition.getPropertyValues().add("name","图灵");
registry.registerBeanDefinition("testComponent",beandefinition);
}
}
第二步:结合@Import让它生效
@Import(MyImportBeanDefinitionRegistrar.class)
2、BeanDefinitionRegistryPostProcessor ——选它!
创建实现BeanDefinitionRegistryPostProcessor接口的类, 演示一个DeanDefintion的注册
public class MyBeanDefinitionRegistryPostProcessor implements
BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)throws BeansException {
GenericBeanDefinition beandefinition=new GenericBeanDefinition();
beandefinition.setBeanClassName("com.tuling.beans.TestComponent");
beandefinition.getPropertyValues().add("id",1);
beandefinition.getPropertyValues().add("name","渔阳");
registry.registerBeanDefinition("testComponent",beandefinition);
}
}
3、通过BeanFactoryPostProcessor
BeanFactoryPostProcessor也可以,但是没有BeanDefinitionRegistryPostProcessor这么明确的责任是用来注册的。
ImportBeanDefinitionRegistrar为什么不行?
ImportBeanDefinitionRegistrar不是一个bean, 没有bean的生命周期, 没有依赖注入功能。
但是!其实ImportBeanDefinitionRegistrar在这个场景也行,啊????? , 不是说不会调用EnvironmentAware
吗
" 是的,这里比较特殊"
在解析@Import的ImportBeanDefinitionRegistrar时候, 会调用BeanClassLoaderAware、BeanFactoryAwar、EnvironmentAware、ResourceLoaderAware
有兴趣可以看源码:
org.springframework.context.annotation.ParserStrategyUtils#invokeAwareMethods
最终实现:
ImportBeanDefinitionRegistrar+EnvironmentAware
BeanDefinitionRegistryPostProcessor
+EnvironmentAware
都行