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

Spring扩展点系列-InstantiationAwareBeanPostProcessor

文章目录

    • 简介
    • 测试一
      • 1、配置文件Bean注册
      • 2、单元测试方法
      • 3、测试类
      • 4、输出结果
      • 结论
    • 测试二
      • 1、测试类
      • 2、输出结果
      • 结论
    • 源码解析
      • postProcessProperties
        • CommonAnnotationBeanPostProcessor
        • AnnotationInjectedBeanPostProcessor
    • 总结

简介

spring容器中Bean的生命周期内所有可扩展的点的调用顺序
扩展接口 实现接口
ApplicationContextlnitializer initialize
AbstractApplicationContext refreshe
BeanDefinitionRegistryPostProcessor postProcessBeanDefinitionRegistry
BeanDefinitionRegistryPostProcessor postProcessBeanFactory
BeanFactoryPostProcessor postProcessBeanFactory
instantiationAwareBeanPostProcessor postProcessBeforelnstantiation
SmartlnstantiationAwareBeanPostProcessor determineCandidateConstructors
MergedBeanDefinitionPostProcessor postProcessMergedBeanDefinition
InstantiationAwareBeanPostProcessor postProcessAfterlnstantiation
SmartInstantiationAwareBeanPostProcessor getEarlyBeanReference
BeanFactoryAware postProcessPropertyValues
ApplicationContextAwareProcessor invokeAwarelnterfaces
BeanNameAware setBeanName
InstantiationAwareBeanPostProcessor postProcessBeforelnstantiation
@PostConstruct
InitializingBean afterPropertiesSet
FactoryBean getobject
SmartlnitializingSingleton afterSingletonslnstantiated
CommandLineRunner run
DisposableBeandestroy

从源码中我们可以获知InstantiationAwareBeanPostProcessor接口除了具有父接口中的两个方法以外还自己额外定义了三个方法。所以该接口一共定义了5个方法,这5个方法的作用分别是

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {@Nullabledefault Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {return null;}default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {return true;}@Nullabledefault PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)throws BeansException {return null;}
public interface BeanPostProcessor {@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}
}
方法描述
postProcessBeforeInitialization方法将在Bean实例的afterPropertiesSet方法或者自定义的init方法被调用前调用,此时Bean属性已经被赋值。方法返回原始Bean实例或者包装后的Bean实例,如果返回null,则后续的后置处理方法不再被调用。
postProcessAfterInitialization方法将在Bean实例的afterPropertiesSet方法或者自定义的init方法被调用后调用,此时Bean属性已经被赋值。方法返回原始Bean实例或者包装后的Bean实例,如果返回null,则后续的后置处理方法不再被调用。
postProcessBeforeInstantiation方法作用为:在Bean实例化前调用该方法,返回值可以为代理后的Bean,以此代替Bean默认的实例化过程。返回值不为null时,后续只会调用BeanPostProcessor的 postProcessAfterInitialization方法,而不会调用别的后续后置处理方法(如postProcessAfterInitialization、postProcessBeforeInstantiation等方法);返回值也可以为null,这时候Bean将按默认方式初始化。
postProcessAfterInstantiation方法作用为:当Bean通过构造器或者工厂方法被实例化后,当属性还未被赋值前,该方法会被调用,一般用于自定义属性赋值。方法返回值为布尔类型,返回true时,表示Bean属性需要被赋值;返回false表示跳过Bean属性赋值,并且InstantiationAwareBeanPostProcessor的postProcessProperties方法不会被调用。

Instantiation为实例化的意思,Initialization为初始化的意思。在Spring Bean生命周期中,实例化指的是创建Bean的过程,初始化指的是Bean创建后,对其属性进行赋值(populate bean)、后置处理等操作的过程,所以Instantiation执行时机先于Initialization。

测试一

简单测试一下,看看每个方法的执行顺序

1、配置文件Bean注册

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="cn.mycode.chen.myunit.config.springextend.TestAutowired" id="testAutowired" init-method="init"><property name="testName" value="若曼底登陆"></property></bean><!-- 注册InstantiationAwareBeanPostProcessor对象 --><bean class="cn.mycode.chen.myunit.config.springextend.ExtendInstantiationAwareBeanPostProcessor"></bean>
</beans>

2、单元测试方法

@Test
public void test2() {ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");TestAutowired testAutowired = ac.getBean(TestAutowired.class);System.out.println(testAutowired);// 关闭销毁ac.registerShutdownHook();
}

3、测试类

一个业务类TestAutowired

@Slf4j
@Component
public class TestAutowired {private String testName ;public TestAutowired(){log.info("TestAutowired构造方法实例化------run");}public void setTestName(String testName) {log.info("设置属性:"+testName);this.testName = testName;}@PostConstructpublic void init() {log.info("init bean执行 ");this.testName = "若曼底登陆";}public void start() {log.info("TestAutowired init-method run" );}
}

InstantiationAwareBeanPostProcessor的实现测试类

@Slf4j
@Configuration
public class ExtendInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {/*** BeanPostProcessor接口中的方法* 在Bean的自定义初始化方法之前执行* Bean对象已经存在了*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// TODO Auto-generated method stublog.info(">>postProcessBeforeInitialization");return bean;}/*** BeanPostProcessor接口中的方法* 在Bean的自定义初始化方法执行完成之后执行* Bean对象已经存在了*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {log.info("<<postProcessAfterInitialization");return bean;}/*** InstantiationAwareBeanPostProcessor中自定义的方法* 在方法实例化之前执行  Bean对象还没有*/@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {log.info("--->postProcessBeforeInstantiation");return null;}/*** InstantiationAwareBeanPostProcessor中自定义的方法* 在方法实例化之后执行  Bean对象已经创建出来了*/@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {log.info("<---postProcessAfterInstantiation");return true;}/*** InstantiationAwareBeanPostProcessor中自定义的方法* 可以用来修改Bean中属性的内容*/@Overridepublic PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean,String beanName) throws BeansException {log.info("<---postProcessPropertyValues--->");return pvs;}
}

4、输出结果

执行测试方法,输出如下结果

springextend.ExtendInstantiationAwareBeanPostProcessor - --->postProcessBeforeInstantiation
springextend.TestAutowired - TestAutowired构造方法实例化------run
springextend.ExtendInstantiationAwareBeanPostProcessor - <---postProcessAfterInstantiation
springextend.ExtendInstantiationAwareBeanPostProcessor - <---postProcessPropertyValues--->
springextend.TestAutowired - 设置属性:若曼底登陆
ExtendInstantiationAwareBeanPostProcessor - >>postProcessBeforeInitialization
springextend.TestAutowired - init bean执行 
springextend.ExtendInstantiationAwareBeanPostProcessor - <<postProcessAfterInitialization

结论

通过打印结果可以看到五个方法全部都执行,并且清楚它们的执行顺序

测试二

基于测试一的代码,更改postProcessBeforeInstantiation方法内部返回

1、测试类

postProcessBeforeInstantiation方法代码

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {log.info("--->postProcessBeforeInstantiation");return new TestAutowired();
}

2、输出结果

从打印结果看,该方法返回的结果如果为null,后面的方法都正常执行了,但是如果postProcessBeforeInstantiation方法返回实例对象后跳过了对象的初始化操作,直接执行了postProcessAfterInitialization(该方法在自定义初始化方法执行完成之后执行),跳过了postProcessAfterInstantiation,postProcessPropertyValues以及自定义的初始化方法

springextend.ExtendInstantiationAwareBeanPostProcessor - --->postProcessBeforeInstantiation
springextend.TestAutowired - TestAutowired构造方法实例化------run
springextend.ExtendInstantiationAwareBeanPostProcessor - <<postProcessAfterInitialization

结论

postProcessBeforeInstantiation方法是最先执行的方法,它在目标对象实例化之前调用,该方法的返回值类型是Object,我们可以返回任何类型的值。由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用;否则按照正常的流程走

源码解析

postProcessProperties

  • CommonAnnotationBeanPostProcessor : 注册带有 @Resource 注解的属性
  • AutowiredAnnotationBeanPostProcessor : 处理带有 @Value、@Autowired、@Inject、@Lookup 注解的属性
CommonAnnotationBeanPostProcessor
static {//将需要解析的注解 都先加载进来  @Resource必须加载,@WebServiceRef和@EJB按条件加载resourceAnnotationTypes.add(Resource.class);webServiceRefClass = loadAnnotationType("javax.xml.ws.WebServiceRef");if (webServiceRefClass != null) {resourceAnnotationTypes.add(webServiceRefClass);}ejbClass = loadAnnotationType("javax.ejb.EJB");if (ejbClass != null) {resourceAnnotationTypes.add(ejbClass);}}

postProcessProperties方法的逻辑就是找到对应的带有注解的元数据Metadata ,然后注入进去

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);}catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);}return pvs;
}

findResourceMetadata方法主要就是查询元数据信息

private InjectionMetadata findResourceMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {// 首先优先用beanName 作为缓存的Key,没有beanName回退到类名作为缓存键String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());// 首先快速检查并发映射(从缓存里面获取,是否已经存在)InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);// needsRefresh判断是否为null ,为null就重新创建, 这里是一个双重检查if (InjectionMetadata.needsRefresh(metadata, clazz)) {synchronized (this.injectionMetadataCache) {metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {// 如果不为null ,先清除一下if (metadata != null) {metadata.clear(pvs);}// 这里开始创建,并放入缓存,详细在下面分析metadata = buildResourceMetadata(clazz);this.injectionMetadataCache.put(cacheKey, metadata);}}}return metadata;}

buildResourceMetadata类的大概逻辑主要是对字段,方法遍历,看是否带有 @Resource,@WebServiceRef,@EJB 注解 ,如果有就对 其进行解析, 然后再将结果放入一个list

private InjectionMetadata buildResourceMetadata(Class<?> clazz) {if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {return InjectionMetadata.EMPTY;}List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;do {final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();ReflectionUtils.doWithLocalFields(targetClass, field -> {if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");}currElements.add(new WebServiceRefElement(field, field, null));}else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@EJB annotation is not supported on static fields");}currElements.add(new EjbRefElement(field, field, null));}else if (field.isAnnotationPresent(Resource.class)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static fields");}if (!this.ignoredResourceTypes.contains(field.getType().getName())) {currElements.add(new ResourceElement(field, field, null));}}});//如果方法上面有 @Resource,@WebServiceRef,@EJB 注解 ,就放入listReflectionUtils.doWithLocalMethods(targetClass, method -> {Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {return;}if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");}if (method.getParameterCount() != 1) {throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);}PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));}else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@EJB annotation is not supported on static methods");}if (method.getParameterCount() != 1) {throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);}PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new EjbRefElement(method, bridgedMethod, pd));}else if (bridgedMethod.isAnnotationPresent(Resource.class)) {if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static methods");}Class<?>[] paramTypes = method.getParameterTypes();if (paramTypes.length != 1) {throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);}if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new ResourceElement(method, bridgedMethod, pd));}}}});elements.addAll(0, currElements);targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);return InjectionMetadata.forElements(elements, clazz);}

inject方法获取里面的 InjectedElement 列表 ,然后开始遍历注入

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {for (InjectedElement element : elementsToIterate) {element.inject(target, beanName, pvs);}}
}
AnnotationInjectedBeanPostProcessor

AnnotationInjectedBeanPostProcessor大体的逻辑也是一样,但是针对的注解主要是@Value,@Autowired, @Inject

@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);} catch (BeanCreationException ex) {throw ex;} catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getName()+ " dependencies is failed", ex);}return pvs;
}

总结

在这里插入图片描述


http://www.mrgr.cn/news/17893.html

相关文章:

  • 原码 / 反码 / 补码的介绍及认知
  • Python测试开发基础(三)---random模块
  • SQLi-LABS靶场56-60通过攻略
  • 【网络基础】探索 NAT 技术:IP 转换、NAPT、NAT穿越及代理服务器
  • Windows 11家庭中文版中管理员阻止运行应用程序的问题
  • C++第四十四弹---Lambda表达式的妙用:高效解决编程中的匿名函数问题
  • 去中心化身份验证:Web3时代数字身份的革新
  • Python测试开发基础(一)
  • Ascend显卡创建虚拟vgpu实例
  • 安防监控视频打手机检测算法核心技术打手机检测算法源码、模型简介
  • C++多线程
  • 云原生主键模型:高效、弹性,省钱又省心
  • Oracle rac模式下undo表空间爆满的解决
  • 全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用
  • C++ 编译三环节
  • centos8 install .net8
  • 前端vue中怎么判断接口请求返回的时长
  • 页面滚动到指定位置——记录div滚动高度,并下次自动滚动到该位置
  • Shopee、Lazada等跨境平台如何获取优质的评价?
  • 09-02 周一 Ubuntu上使用docker-compose部署elasticsearch和kibana服务