首页 > 其他分享 >Spring扩展点系列-InstantiationAwareBeanPostProcessor

Spring扩展点系列-InstantiationAwareBeanPostProcessor

时间:2024-09-02 17:52:37浏览次数:13  
标签:Spring beanName 扩展 Bean new null 方法 method InstantiationAwareBeanPostProcessor

文章目录

简介

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 {
	@Nullable
	default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

	default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		return true;
	}
	
	@Nullable
	default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
			throws BeansException {

		return null;
	}

public interface BeanPostProcessor {
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default 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/beans
 http://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;
    }

    @PostConstruct
    public 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对象已经存在了
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        log.info(">>postProcessBeforeInitialization");
        return bean;
    }

    /**
     * BeanPostProcessor接口中的方法
     * 在Bean的自定义初始化方法执行完成之后执行
     * Bean对象已经存在了
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("<<postProcessAfterInitialization");
        return bean;
    }

    /**
     * InstantiationAwareBeanPostProcessor中自定义的方法
     * 在方法实例化之前执行  Bean对象还没有
     */
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        log.info("--->postProcessBeforeInstantiation");
        return null;
    }

    /**
     * InstantiationAwareBeanPostProcessor中自定义的方法
     * 在方法实例化之后执行  Bean对象已经创建出来了
     */
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        log.info("<---postProcessAfterInstantiation");
        return true;
    }

    /**
     * InstantiationAwareBeanPostProcessor中自定义的方法
     * 可以用来修改Bean中属性的内容
     */
    @Override
    public 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 注解 ,就放入list
			ReflectionUtils.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;
}

总结

在这里插入图片描述

标签:Spring,beanName,扩展,Bean,new,null,方法,method,InstantiationAwareBeanPostProcessor
From: https://blog.csdn.net/qq_32590535/article/details/141720939

相关文章

  • springboot多媒体内容管理系统-计算机毕业设计源码08580
    摘 要随着人类向信息社会的不断迈进,风起云涌的信息时代正掀起一次新的革命,同时计算机网络技术高速发展,网络管理运用也变得越来越广泛。因此,建立一个多媒体内容管理系统(CMS)的设计与优化来管理多媒体内容信息,会使管理工作系统化、规范化,提高管理效率。本课题的研究对象是多媒......
  • springboot中小型酒店管理系统-计算机毕业设计源码02793
    摘要随着互联网和移动技术的快速发展,酒店行业也面临着巨大的变革和机遇。传统的酒店管理方式存在着信息不透明、预订流程繁琐等问题,无法满足现代消费者对便捷、高效、个性化服务的需求。因此,开发中小型酒店管理系统具有重要的意义。本文旨在设计和实现一种功能完善、易用且可......
  • Java服务端服务监控:Prometheus与Spring Boot Actuator的集成
    Java服务端服务监控:Prometheus与SpringBootActuator的集成大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代Java服务端开发中,服务监控是确保系统稳定性和性能的关键。Prometheus是一个开源的系统监控和警报工具,而SpringBootActuator提供了生......
  • Java服务端服务监控:Spring Boot Actuator的实践
    Java服务端服务监控:SpringBootActuator的实践大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在微服务架构中,服务监控是确保系统健康运行的关键。SpringBootActuator提供了一系列的监控和管理功能,使得开发者能够更好地监控和管理SpringBoot应用......
  • springboot 编程事务的封装
    一、创建事务管理工具类java复制importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importorg.springframework.transaction.PlatformTransactionManager;importorg.springframework.transaction.Transact......
  • LongWriter-6k 数据集开发利用 AgentWrite:一种在LLM中将输出长度扩展到超过10,000字,同
    大语言模型(LLMs)的领域已经取得了巨大的进展,特别是在扩展其记忆容量以处理越来越多的上下文方面。现在这些模型可以处理超过100,000个标记的输入,使得它们能够执行高度复杂的任务,例如生成长篇文本、翻译大型文档和总结大量数据。然而,尽管在处理能力方面取得了这些进展,在生成等长......
  • 基于SpringBoot+MySQL+SSM+Vue.js的学生选课系统
    获取见最下方名片获取见最下方名片获取见最下方名片演示视频基于SpringBoot+MySQL+SSM+Vue.js的学生选课系统(附论文)技术描述开发工具:Idea/Eclipse数据库:MySQLJar包仓库:Maven前端框架:Vue/ElementUI后端框架:Spring+SpringMVC+Mybatis+SpringBoot文字描......
  • Spring框架之IOC介绍
    Spring之IOC简介首先,官网中有这样一句话:SpringFrameworkimplementationoftheInversionofControl(IoC)principle.这句话翻译过来就是:Spring实现控制反转(IOC)原理,由此可以得出,InversionofControl(IOC)是一个名为控制反转的原理,而Spring实现了他。而实现这个原理或者说设......
  • SpringCloud Gateway鉴权
    参考:https://blog.csdn.net/weixin_43296313/article/details/121126811基于从前的项目:https://www.cnblogs.com/xsj1989/p/18350213在网关项目下创建全局过滤器packagecom.xcg.filters;importcom.auth0.jwt.interfaces.Claim;importcom.auth0.jwt.interfaces.DecodedJWT;......
  • Java服务端服务编排:Spring Boot与Spring Cloud的整合
    Java服务端服务编排:SpringBoot与SpringCloud的整合大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在微服务架构中,服务编排是一个关键的环节,它涉及到服务的协调、管理和监控。SpringBoot和SpringCloud是Java生态中广泛使用的框架,它们提供了强大的......