Spring 扩展点

在这里插入图片描述

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获取组件

在这里插入图片描述
在这里插入图片描述

生命周期回调

  1. 如果通过aware获取组件, 那么肯定也会用初始化的回调方式进行初始化, 而不是用构造函数
  2. 用构造函数初始化, 由于构造函数在实例化这步, 获取不到像aware这些组件。
  3. 构造函数不确定性, 所以作为初始化不合适

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的配置文件(后续还可以利用配置中心)

  1. 根据配置的参数, 动态创建线程池
  2. 将动态线程池bean交给Spring容器管理
  3. 后续使用线程池可以从Spring容器中获取动态线程池bean使用
  4. 后续修改可以直接对动态线程池bean进行修改
  5. 最好还能监控如果达到阈值进行(发邮件)警告。

需求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时是获取不到注解绑定的配置信息的:

  1. 动态创建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
都行

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值