当前位置: 首页 > article >正文

系列十六、Spring IOC容器的扩展点

一、概述

        Spring IOC容器的扩展点是指在IOC加载的过程中,如何对即将要创建的bean进行扩展。

二、扩展点

2.1、BeanDefinitionRegistryPostProcessor

2.1.1、概述

        BeanDefinitionRegistryPostProcessor是bean定义的后置处理器,在BeanDefinition加载后,实例化bean之前,调用 invokeBeanFactoryPostProcessors时进行扩展,通过改变BeanDefinition的定义信息进行扩展,源码如下:

2.1.2、继承结构

2.1.3、案例

2.1.3.1、ATM
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:06
 * @Description:
 */
@Slf4j
@Component(value = "atm")
public class ATM {

    public int withdrawMoney(int money) {
        log.info("取钱方法正在执行...");
        if (money == 100) {
            throw new RuntimeException("自定义的异常");
        }
        return money;
    }

}
2.1.3.2、MySpringConfig
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:29
 * @Description:
 */
@Configuration
@ComponentScan(basePackages = {"org.star"})
public class MySpringConfig {

}
 2.1.3.3、MyBeanDefinitionRegistryPostProcessor 
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/25 17:38
 * @Description:
 */
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    /**
     * 作用:动态注册BeanDefinition
     * 调用时机:IOC加载时注册BeanDefinition的时候会调用
     * @param registry the bean definition registry used by the application context
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        RootBeanDefinition definition = new RootBeanDefinition(ATM.class);
        // 设置ATM bean为多实例
        definition.setScope("prototype");
        registry.registerBeanDefinition("atm",definition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("atm");
        // 将atm设置为懒加载,这样在容器启动时将不会创建bean,只有在getBean时才会创建对象
        beanDefinition.setLazyInit(true);
    }
}
2.1.3.4、AopFullAnnotationMainApp 
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:14
 * @Description:
 */
@Slf4j
public class AopFullAnnotationMainApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);

        ATM atm1 = context.getBean("atm", ATM.class);
        ATM atm2 = context.getBean("atm", ATM.class);
        log.info("atm1:{},atm2:{},(atm1 == atm2):{}", atm1,atm2,(atm1 == atm2));

    }

}

 

2.2、xxxAware接口

2.2.1、概述

        Spring中存在着大量的xxxAware接口实现类,用于在bean初始化完成之前做一些前置操作,程序员可以自己实现xxxAware接口,重写里边的方法修改bean的定义信息,进行扩展。

2.2.2、继承结构

2.2.3、案例

2.2.3.1、ATM

同上。

2.2.3.2、MySpringConfig

同上。

2.2.3.3、MyApplicationContextAware
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/25 18:51
 * @Description:
 */
@Component
public class MyApplicationContextAware implements ApplicationContextAware {
    public ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        AnnotationConfigApplicationContext context = (AnnotationConfigApplicationContext) applicationContext;
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("atm");
        beanDefinition.setScope("prototype");
    }

}
2.2.3.4、AopFullAnnotationMainApp

同上。

2.2.3、调用链路

2.2.4、注意事项

        通过观察 2.1.3.4和 2.2.3.4的执行结果,不能发现xxxAware接口中配置的扩展覆盖了BeanDefinitionRegistryPostProcessor中的配置,说明xxxAware的优先级更高,这个也很好理解,对于同样的一个bean,后边的配置肯定会覆盖掉前边的配置。

2.3、生命周期回调时进行扩展

2.3.1、概述

        bean的生命周期回调主要分为两种,一种是初始化进行调用,另外一种是销毁时进行调用,但是不管是初始化还是销毁,都对应着三种方式,即:

        a、@PostConstruct @PreDestroy
        b、实现接口 InitializingBean, DisposableBean的方式
        c、@Bean(initMethod = "init",destroyMethod = "destroy")的方式

2.4、初始化后实例化前进行扩展

2.4.1、bean创建完成的标识

        当循环完所有的DeanDefinition后,bean就创建完了。

2.4.2、大致流程

启动IOC容器 ===> refresh() ===>finishBeanFactoryInitialization(beanFactory)===>beanFactory.preInstantiateSingletons()

2.4.3、源码解析

#1、启动IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);

#2、refresh()
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
	this();
	register(componentClasses);
	refresh();
}

# 3、finishBeanFactoryInitialization(beanFactory)
@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);

		try {
			// Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);

			// Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);

			// Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);

			// Initialize message source for this context.
			initMessageSource();

			// Initialize event multicaster for this context.
			initApplicationEventMulticaster();

			// Initialize other special beans in specific context subclasses.
			onRefresh();

			// Check for listener beans and register them.
			registerListeners();

			// Instantiate all remaining (non-lazy-init) singletons.
			finishBeanFactoryInitialization(beanFactory);

			// Last step: publish corresponding event.
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();

			// Reset 'active' flag.
			cancelRefresh(ex);

			// Propagate exception to caller.
			throw ex;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}

# 4、beanFactory.preInstantiateSingletons();
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
	// Initialize conversion service for this context.
	if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
			beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
		beanFactory.setConversionService(
				beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
	}

	// Register a default embedded value resolver if no bean post-processor
	// (such as a PropertyPlaceholderConfigurer bean) registered any before:
	// at this point, primarily for resolution in annotation attribute values.
	if (!beanFactory.hasEmbeddedValueResolver()) {
		beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
	}

	// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
	String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
	for (String weaverAwareName : weaverAwareNames) {
		getBean(weaverAwareName);
	}

	// Stop using the temporary ClassLoader for type matching.
	beanFactory.setTempClassLoader(null);

	// Allow for caching all bean definition metadata, not expecting further changes.
	beanFactory.freezeConfiguration();

	// Instantiate all remaining (non-lazy-init) singletons.
	beanFactory.preInstantiateSingletons();
}

# 5、beanFactory.preInstantiateSingletons();
@Override
public void preInstantiateSingletons() throws BeansException {
	if (logger.isTraceEnabled()) {
		logger.trace("Pre-instantiating singletons in " + this);
	}

	// Iterate over a copy to allow for init methods which in turn register new bean definitions.
	// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

	// Trigger initialization of all non-lazy singleton beans...
	for (String beanName : beanNames) {
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			if (isFactoryBean(beanName)) {
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				if (bean instanceof FactoryBean) {
					final FactoryBean<?> factory = (FactoryBean<?>) bean;
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
										((SmartFactoryBean<?>) factory)::isEagerInit,
								getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
			}
			else {
				getBean(beanName);
			}
		}
	}

	// Trigger post-initialization callback for all applicable beans...
	for (String beanName : beanNames) {
		Object singletonInstance = getSingleton(beanName);
		if (singletonInstance instanceof SmartInitializingSingleton) {
			final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
			if (System.getSecurityManager() != null) {
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					smartSingleton.afterSingletonsInstantiated();
					return null;
				}, getAccessControlContext());
			}
			else {
				smartSingleton.afterSingletonsInstantiated();
			}
		}
	}
}

2.4.4、通过监听器扩展

2.4.4.1、Computer
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 11:52
 * @Description:
 */
@Getter
@Setter
@Accessors(chain = true)
@Component
public class Computer {

    /**
     * 电脑名称
     */
    private String name;

    /**
     * 品牌
     */
    private String brand;

}
2.4.4.2、 MyContextRefreshedEvent 
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 11:48
 * @Description: 监听器
 */
@Component
public class MyContextRefreshedEvent {

    @EventListener(ContextRefreshedEvent.class)
    public void onContextRefreshedEvent(ContextRefreshedEvent event) {
        System.out.println(event);

        // bean初始化完成后做扩展,扩展代码写在这里
        ConfigurableApplicationContext context = (ConfigurableApplicationContext) event.getApplicationContext();
        DefaultListableBeanFactory factory = (DefaultListableBeanFactory) context.getBeanFactory();
        BeanDefinition beanDefinition = factory.getBeanDefinition("computer");
        beanDefinition.setScope("prototype");
        factory.registerBeanDefinition("computer",beanDefinition);

        System.out.println("all singleton beans loaded,onContextRefreshedEvent execute success!");
    }

}
2.4.4.3、MySpringConfig(同上)
2.4.4.4、AopFullAnnotationMainApp 
@Slf4j
public class AopFullAnnotationMainApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);

        Computer computer1 = context.getBean(Computer.class);
        Computer computer2 = context.getBean(Computer.class);
        log.info("computer1:{},computer2:{},(computer1 == computer2 ?) : {}",computer1,computer2,(computer1 == computer2));
    }
}

2.4.5、通过接口扩展

2.4.5.1、Computer
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 11:52
 * @Description:
 */
@Getter
@Setter
@Accessors(chain = true)
@Component
@Slf4j
public class Computer {

    /**
     * 电脑名称
     */
    private String name;

    /**
     * 品牌
     */
    private String brand;

    public void init() {
        Computer computer = new Computer().setName("OptiPlex7010MT Plus13").setBrand("戴尔");
        log.info("Computer's init was invoked! computer:{}", JSON.toJSONString(computer));
    }

}
2.4.5.2、MySmartInitializingSingleton
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 13:04
 * @Description:
 */
@Component
public class MySmartInitializingSingleton implements SmartInitializingSingleton {

    @Resource
    private ApplicationContext applicationContext;

    @Override
    public void afterSingletonsInstantiated() {
        ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
        DefaultListableBeanFactory factory = (DefaultListableBeanFactory) context.getBeanFactory();
        BeanDefinition beanDefinition = factory.getBeanDefinition("computer");
        beanDefinition.setInitMethodName("init");
        beanDefinition.setScope("prototype");
        factory.registerBeanDefinition("computer",beanDefinition);

        System.out.println("all singleton beans loaded,afterSingletonsInstantiated execute success!");

    }
}
2.4.5.3、MySpringConfig(同上)
2.4.5.4、AopFullAnnotationMainApp(同上) 


http://www.kler.cn/a/149126.html

相关文章:

  • 【R78/G15 开发板测评】串口打印 DHT11 温湿度传感器、DS18B20 温度传感器数据,LabVIEW 上位机绘制演化曲线
  • 豆瓣均分9:不容错过的9本大模型入门宝藏书籍,非常详细收藏我这一篇就够了
  • 【含开题报告+文档+PPT+源码】基于Spring Boot智能综合交通出行管理平台的设计与实现
  • 时序数据库TimescaleDB安装部署以及常见使用
  • 实时渲染技术如何助力3D虚拟展厅?
  • 彻底理解ARXML中的PDU
  • vue3-10
  • 【C++】构造函数和析构函数第四部分(深拷贝和浅拷贝)--- 2023.11.25
  • Spring Boot 3 + Spring Security 6 最新版本修改 Json 登录后 RememberMe 功能问题失效的解决方案
  • NextJS开发:封装shadcn/ui中的AlertDialog确认对话框
  • windows系统mobaxterm远程执行linux上ssh命令
  • 中伟视界:AI智能分析盒子的人数统计AI算法通过什么算法模型可以解决重复统计的问题?
  • 【AI考证笔记】NO.1人工智能的基础概念
  • Mysql更新Blob存储的Josn数据
  • c++调用openssl对文件加解密
  • ubuntu配置免密登录vscode
  • 如何优化 Elasticsearch 查询性能
  • 华为P40无法链接adb的解决记录
  • 深度学习之六(自编码器--Autoencoder)
  • 面向植保任务的无人机集群系统及其应用研究
  • shell编程系列(4)-循环结构
  • Java第十二篇:连接安全版kafka(Kerberos认证)出现的问题解答
  • C++学习之路(十)C++ 用Qt5实现一个工具箱(增加一个时间戳转换功能)- 示例代码拆分讲解
  • Matlab 点云曲率计算(之二)
  • 浅谈现代化城市建设中智慧消防的研究与应用
  • Python与微信交互(互动)神器yyds