文章目录
- 简介
- 测试一
- 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 |
DisposableBean | destroy |
从源码中我们可以获知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;
}