系列十六、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!");
}
}