Spring 学习二、Bean生命周期相关注解

时间:2022-07-26
本文章向大家介绍Spring 学习二、Bean生命周期相关注解,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

目录

  • 1. 原始xml配置bean的初始化方法和销毁方法
  • 2.使用注解配置Bean的init方法和destroy方法
  • 3.实现InitializingBean 和 DisposableBean接口
  • 4.JSR-250 @PostConstruct and @PreDestroy
  • 5.BeanPostProcessor
  • 6.BeanPostProcessor 原理
    • 6.1ApplicationContextAwareProcessor
    • 6.2BeanValidationPostProcessor
    • 6.3InstantiationAwareBeanPostProcessor
  • 7 总结

1. 原始xml配置bean的初始化方法和销毁方法

<bean id="person" 
  		class="com.meritit.ysjian.spring3learning.beanlife.Person"
		init-method="myInit" destroy-method="myDestory" scope="singleton"
		p:id="1001" p:age="125" p:birthday="2013/03/05" />

2.使用注解配置Bean的init方法和destroy方法

/**
     * 指定Bean的初始化方法和销毁方法
     *
     * @return
     */
@Bean(initMethod = "init",destroyMethod = "destroy")
Student windows(){
        return new Student(00001L, "Windows");
}

具体的init和destroy方法

    public void init(){
        System.out.println("Student Bean 调用初始化方法.");
    }


    public void destroy(){
        System.out.println("Student Bean 调用销毁方法.");
    }

Bean的销毁方法正常不会被调用只有当容器关闭的时候才会调用

如果Bean的作用域是prototype 销毁方法会被Spring容器调用,但是如果是多例方法销毁方法就不会被调用

3.实现InitializingBean 和 DisposableBean接口

官方文档

To interact with the container’s management of the bean lifecycle, you can implement the Spring InitializingBean and DisposableBean interfaces. The container calls afterPropertiesSet() for the former and destroy() for the latter to let the bean perform certain actions upon initialization and destruction of your beans.

为了和容器管理的bean的声明周期进行交互,你可以实现Spring的InitializingBean和DisposableBean 接口。

容器为前者调用afterPropertiesSet(),为后者调用destroy(),以便bean在初始化和销毁bean时执行某些操作。

public class JeepCar implements InitializingBean,DisposableBean {


    public void JeepCar(){
        System.out.println("Jeep Car Construct ...");
    }


    /**
     * 在Bean销毁时调用的方法
     * @throws Exception
     */
    public void destroy() throws Exception {

        System.out.println("执行销毁方法....");

    }

    /**
     * 在Bean 执行完构造方法之后执行的方法
     * @throws Exception
     */
    public void afterPropertiesSet() throws Exception {
        System.out.println("执行初始化构造方法");
    }
}

控制台打印输出的内容

执行初始化构造方法
Spring Contation Initializer Ok ... 
容器构造完成
执行销毁方法....

这种方法官方不推荐使用和Spring的耦合度太大。

4.JSR-250 @PostConstruct and @PreDestroy

这种方法是官方推荐的

@Component
public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
        System.out.println("populates the movie cache upon initialization...");
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
        System.out.println("clear the movie cache upon destruction...");
    }
}

日志打印

populates the movie cache upon initialization...
执行初始化构造方法
Spring Contation Initializer Ok ... 
容器构造完成
执行销毁方法....
clear the movie cache upon destruction...

5.BeanPostProcessor

BeanPostProcessor 主要执行的时机是 调用初始化方法前后

public interface BeanPostProcessor {

	/**
	 * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). 
	  应用这个方法在给出新的bean实例后,在调用任何初始化方法回调函数之前,比如说 实现 Initializing的接口
	 
	 
	 The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * <p>The default implementation returns the given {@code bean} as-is.
	 * @param bean the new bean instance
	 * @param beanName the name of the bean
	 * @return the bean instance to use, either the original or a wrapped one;
	 * if {@code null}, no subsequent BeanPostProcessors will be invoked
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 */
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	/**
	 * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
	 * instance and the objects created by the FactoryBean (as of Spring 2.0). The
	 * post-processor can decide whether to apply to either the FactoryBean or created
	 * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
	 * <p>This callback will also be invoked after a short-circuiting triggered by a
	 * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
	 * in contrast to all other BeanPostProcessor callbacks.
	 * <p>The default implementation returns the given {@code bean} as-is.
	 * @param bean the new bean instance
	 * @param beanName the name of the bean
	 * @return the bean instance to use, either the original or a wrapped one;
	 * if {@code null}, no subsequent BeanPostProcessors will be invoked
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 * @see org.springframework.beans.factory.FactoryBean
	 */
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

有两个方法

postProcessBeforeInitialization: 初始化方法执行之前调用该方法

postProcessAfterInitialization: 初始化方法执行之后调用该方法

Java 代码实现

@Component
public class MyBeanPostProcessor implements BeanPostProcessor{


    /**
     *
     * @param bean 刚实例化的Bean
     * @param beanName  在容器中的名称
     * @return
     * @throws BeansException
     */
    @Nullable
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("postProcessBeforeInitialization => "+beanName+" => "+bean);

        return bean;
    }

    /**
     *
     * @param bean  刚实例化的Bean
     * @param beanName 在容器中的名称
     * @return
     * @throws BeansException
     */
    @Nullable
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("postProcessAfterInitialization => "+beanName+" => "+bean);
        return bean;
    }
}

控制台输出

postProcessBeforeInitialization => studentBeanConfiguration => com.zuoyan.config.StudentBeanConfiguration@2d127a61
postProcessAfterInitialization => studentBeanConfiguration => com.zuoyan.config.StudentBeanConfiguration@2d127a61
postProcessBeforeInitialization => cachingMovieLister => com.zuoyan.bean.CachingMovieLister@32a068d1
populates the movie cache upon initialization...
postProcessAfterInitialization => cachingMovieLister => com.zuoyan.bean.CachingMovieLister@32a068d1
postProcessBeforeInitialization => jeepCar => com.zuoyan.bean.JeepCar@71a794e5
执行初始化构造方法
postProcessAfterInitialization => jeepCar => com.zuoyan.bean.JeepCar@71a794e5
Spring Contation Initializer Ok ... 
容器构造完成
执行销毁方法....
clear the movie cache upon destruction...

6.BeanPostProcessor 原理

在Spring org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean的方法中

// Initialize the bean instance.
Object exposedObject = bean;
try {
    // 首先设置bean的属性值 
	populateBean(beanName, mbd, instanceWrapper);
    // 然后在调用初始化方法
	exposedObject = initializeBean(beanName, exposedObject, mbd);
}

首先执行populateBean 设置对象的属性,然后执行初始化类

下面是 initialzeBean方法中的代码

if (mbd == null || !mbd.isSynthetic()) {
  			// 首先在初始化之前执行
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
          	// 调用初始化方法
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
             
          	// 初始化方法之后执行
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

初始化之前执行的方法 applyBeanPostProcessorsBeforeInitialization方法

然后在调用初始化方法

最后调用初始化方法之后的方法

下面是applyBeanPostProcessorsBeforeInitialization 具体的代码实现

@Override
	public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
      	// 循环遍历执行所有 BeanPostProcessor
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessBeforeInitialization(result, beanName);
             // 如果碰见其中一个为null 就不往下执行了
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

常用的BeanPostProcessor 还有如下

6.1ApplicationContextAwareProcessor

通过实现这个 ApplicationContextAware 接口,我们可以在自己的实体类中获取ApplicationContext 容器

public interface ApplicationContextAware extends Aware {

   /**
    * Set the ApplicationContext that this object runs in.
    * Normally this call will be used to initialize the object.
    * <p>Invoked after population of normal bean properties but before an init callback such
    * as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
    * or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
    * {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
    * {@link MessageSourceAware}, if applicable.
    * @param applicationContext the ApplicationContext object to be used by this object
    * @throws ApplicationContextException in case of context initialization errors
    * @throws BeansException if thrown by application context methods
    * @see org.springframework.beans.factory.BeanInitializationException
    */
   void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

比如说我定义一个Animal类,然后想获取applicationContext类,就可以使用下面的方法

@Component
public class Animal implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        this.applicationContext = applicationContext;
    }
}

但是底层实现的原理是什么呢,我们可以Debug调试跟踪下,可以看到方法栈调用如下。

查看详细的实现代码

可以看见正在调用 ApplicationContextAwareProcessor 的 postProcessBeforeInitialization方法,进入该方法中继续进行查看

@Override
	@Nullable
	public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
		AccessControlContext acc = null;
		
         // 判断Bean 是否有安全管理器 和有没有实现下面的这些接口,如果有实现就给 acc 初始化
		if (System.getSecurityManager() != null &&
				(bean instanceof EnvironmentAware || 
                  bean instanceof EmbeddedValueResolverAware ||
			     bean instanceof ResourceLoaderAware || 
                  bean instanceof ApplicationEventPublisherAware ||
			     bean instanceof MessageSourceAware ||
                  // 可以看到这行判断是否该接口
                  bean instanceof ApplicationContextAware)
           ) 
        {
			acc = this.applicationContext.getBeanFactory().getAccessControlContext();
		}
          
      
		if (acc != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareInterfaces(bean);
				return null;
			}, acc);
		}
		else {
             // 调用这段代码
			invokeAwareInterfaces(bean);
		}

		return bean;
	}

可以看到下面调用的方法传入的bean是 Animal

接着向下调用, 具体代码实现

private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof EnvironmentAware) {
				((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
			}
			if (bean instanceof EmbeddedValueResolverAware) {
				((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
			}
			if (bean instanceof ResourceLoaderAware) {
				((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
			}
			if (bean instanceof ApplicationEventPublisherAware) {
				((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
			}
			if (bean instanceof MessageSourceAware) {
				((MessageSourceAware) bean).setMessageSource(this.applicationContext);
			}
          
             // 判断是否实现接口,
			if (bean instanceof ApplicationContextAware) {
				((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
			}
		}
	}

如果实现相应的接口,进行方法的回调 ,也就是我们自己实现 ApplicationContextAware 接口的方法

6.2BeanValidationPostProcessor

这个类是对对象的校验,比如说SpringMVC 中Controller 提交过来的参数进行校验

/**
 * Simple {@link BeanPostProcessor} that checks JSR-303 constraint annotations
 * in Spring-managed beans, throwing an initialization exception in case of
 * constraint violations right before calling the bean's init method (if any).
 *
 * @author Juergen Hoeller
 * @since 3.0
 */
public class BeanValidationPostProcessor implements BeanPostProcessor, InitializingBean {

	@Nullable
	private Validator validator;

	private boolean afterInitialization = false;


	/**
	 * Set the JSR-303 Validator to delegate to for validating beans.
	 * <p>Default is the default ValidatorFactory's default Validator.
	 */
	public void setValidator(Validator validator) {
		this.validator = validator;
	}

	/**
	 * Set the JSR-303 ValidatorFactory to delegate to for validating beans,
	 * using its default Validator.
	 * <p>Default is the default ValidatorFactory's default Validator.
	 * @see javax.validation.ValidatorFactory#getValidator()
	 */
	public void setValidatorFactory(ValidatorFactory validatorFactory) {
		this.validator = validatorFactory.getValidator();
	}

	/**
	 * Choose whether to perform validation after bean initialization
	 * (i.e. after init methods) instead of before (which is the default).
	 * <p>Default is "false" (before initialization). Switch this to "true"
	 * (after initialization) if you would like to give init methods a chance
	 * to populate constrained fields before they get validated.
	 */
	public void setAfterInitialization(boolean afterInitialization) {
		this.afterInitialization = afterInitialization;
	}

	@Override
	public void afterPropertiesSet() {
		if (this.validator == null) {
			this.validator = Validation.buildDefaultValidatorFactory().getValidator();
		}
	}


	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if (!this.afterInitialization) {
			doValidate(bean);
		}
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (this.afterInitialization) {
			doValidate(bean);
		}
		return bean;
	}


	/**
	 * Perform validation of the given bean.
	 * @param bean the bean instance to validate
	 * @see javax.validation.Validator#validate
	 */
	protected void doValidate(Object bean) {
		Assert.state(this.validator != null, "No Validator set");
		Object objectToValidate = AopProxyUtils.getSingletonTarget(bean);
		if (objectToValidate == null) {
			objectToValidate = bean;
		}
		Set<ConstraintViolation<Object>> result = this.validator.validate(objectToValidate);

		if (!result.isEmpty()) {
			StringBuilder sb = new StringBuilder("Bean state is invalid: ");
			for (Iterator<ConstraintViolation<Object>> it = result.iterator(); it.hasNext();) {
				ConstraintViolation<Object> violation = it.next();
				sb.append(violation.getPropertyPath()).append(" - ").append(violation.getMessage());
				if (it.hasNext()) {
					sb.append("; ");
				}
			}
			throw new BeanInitializationException(sb.toString());
		}
	}

}

6.3InstantiationAwareBeanPostProcessor

这个类是实现 @PostConstruct@PreDestroy 这两个注解的具体实现

打上断点,查看方法调用栈,会调用初始化方法

但是在执行这个方法之前会获取 注解标注的方法 findLifecycleMetadata()

private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
		if (this.lifecycleMetadataCache == null) {
			// Happens after deserialization, during destruction...
			return buildLifecycleMetadata(clazz);
		}
		// Quick check on the concurrent map first, with minimal locking.
		LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
		if (metadata == null) {
			synchronized (this.lifecycleMetadataCache) {
              	
				metadata = this.lifecycleMetadataCache.get(clazz);
				if (metadata == null) {
					metadata = buildLifecycleMetadata(clazz);
					this.lifecycleMetadataCache.put(clazz, metadata);
				}
				return metadata;
			}
		}
		return metadata;
	}

this.lifecycleMetadataCache 是一个 Map 对象,用来存放容器中的Bean的声明周期元数据

private final transient Map<Class<?>, LifecycleMetadata> lifecycleMetadataCache = new ConcurrentHashMap<>(256);

LifecycleElement类

private static class LifecycleElement {
		// 具体的方法
		private final Method method;
		// 方法的名称标识符
		private final String identifier;
}

LifecycleMetadata 类数据类型

private class LifecycleMetadata {
         // 目标类
		private final Class<?> targetClass;
         // 初始化方法集合
		private final Collection<LifecycleElement> initMethods;
         
        //  Bean 销毁方法集合
		private final Collection<LifecycleElement> destroyMethods;
		
  		// 检查初始化方法集合
		@Nullable
		private volatile Set<LifecycleElement> checkedInitMethods;
		
  		// 检查销毁方法集合
		@Nullable
		private volatile Set<LifecycleElement> checkedDestroyMethods;

  		
		public LifecycleMetadata(Class<?> targetClass, Collection<LifecycleElement> initMethods,
				Collection<LifecycleElement> destroyMethods) {

			this.targetClass = targetClass;
			this.initMethods = initMethods;
			this.destroyMethods = destroyMethods;
		}
         // .....
}

到此为止在初始化方法之前获取到了所有的bean生命周期元注解,接下来开始执行初始化方法。

初始化方法调用代码

public void invokeInitMethods(Object target, String beanName) throws Throwable {
			Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;
			Collection<LifecycleElement> initMethodsToIterate =
					(checkedInitMethods != null ? checkedInitMethods : this.initMethods);
  			//如果初始化方法不为空,就迭代执行初始化方法
			if (!initMethodsToIterate.isEmpty()) {
				for (LifecycleElement element : initMethodsToIterate) {
					if (logger.isTraceEnabled()) {
						logger.trace("Invoking init method on bean '" + beanName + "': " + element.getMethod());
					}
                  
					element.invoke(target);
				}
			}
	}

继续追踪element.invoke(target);方法

public void invoke(Object target) throws Throwable {
			ReflectionUtils.makeAccessible(this.method);
  			// this 就是LifecycleElement 
			this.method.invoke(target, (Object[]) null);
}

可以看到就是通过反射方法调用对象相应的方法

7 总结

Spring底层对BeanPostProcessor的使用可以体现在 Bean 对象的赋值、注入其他组件、Bean声明周期注解功能,@ Autowired 自动注入注解功能,@Async。

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2gxc0pq0c740g