Spring扩展点系列-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
DisposableBean destroy

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

java 复制代码
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;
	}
java 复制代码
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 复制代码
<?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、单元测试方法

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

3、测试类

一个业务类TestAutowired

java 复制代码
@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的实现测试类

java 复制代码
@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、输出结果

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

powershell 复制代码
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方法代码

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

2、输出结果

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

powershell 复制代码
springextend.ExtendInstantiationAwareBeanPostProcessor - --->postProcessBeforeInstantiation
springextend.TestAutowired - TestAutowired构造方法实例化------run
springextend.ExtendInstantiationAwareBeanPostProcessor - <<postProcessAfterInitialization

结论

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

源码解析

postProcessProperties

  • CommonAnnotationBeanPostProcessor : 注册带有 @Resource 注解的属性
  • AutowiredAnnotationBeanPostProcessor : 处理带有 @Value、@Autowired、@Inject、@Lookup 注解的属性
CommonAnnotationBeanPostProcessor
java 复制代码
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 ,然后注入进去

java 复制代码
@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方法主要就是查询元数据信息

java 复制代码
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

java 复制代码
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 列表 ,然后开始遍历注入

java 复制代码
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

java 复制代码
@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;
}

总结

相关推荐
Damon小智1 分钟前
SpringBoot权限认证-Sa-Token的使用与详解
java·spring boot·spring cloud·微服务·sa-token
祁思妙想8 分钟前
《JavaEE进阶》----16.<Mybatis简介、操作步骤、相关配置>
java·java-ee·mybatis
程序猿大波17 分钟前
基于Java、SpringBoot、Vue的加油站管理系统设计
java·vue.js·spring boot
Aa1345176502526 分钟前
c# sqlhelper类
java·数据库·c#
流浪的大萝卜27 分钟前
开发一个电商API接口的步骤!!!
java·大数据·前端·数据仓库·后端·爬虫·python
汇匠源1 小时前
Java在零工市场中的应用:构建灵活高效的劳动力平台
java·大数据·开发语言·团队开发
莳光.1 小时前
xml中SQL执行错误(使用另外一张表的两个字段,组装SQL的where查询条件)
java·mybatis
Onlooker1291 小时前
Spring3-IoC1-IoC容器、基于xml管理bean
spring
铁打的阿秀1 小时前
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException
java
Artemis丶月1 小时前
Spring Event 业务解耦神器(泛型喔!)
java·后端·spring