10. spring-容器: BeanPostProcessor后置处理器-进阶

再谈bean生命周期

目前整个bean的生命周期,我们可以进行如下控制:

  • bean的实例化: 容器初始化时调用bean的构造方法,我们可以在构造方法中执行必要的逻辑
  • bean的初始化: 在初始化时,可以通过BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法进行拦截,执行自定义的逻辑;通过@PostConstruct注解、InitializingBean和init-method来指定bean初始化前后执行的方法,执行自定义的逻辑。
  • bean的销毁: 可以通过@PreDestroy注解,DisposableBean和destroy-method来指定bean在销毁前的执行逻辑。

以上,就是我们可以通过spring容器控制bean生命周期的方式。

解析几种重要的BeanPostProcessor

本章会详解几种spring自带的几种BeanPostProcessor,通过它们实现了对bean的功能定制。主要了解这几个:

  • ApplicationContextAwareProcessor
    它的作用是可以向组件中注入IOC容器

  • BeanValidationPostProcessor
    它的作用是对bean进行校验,当我们创建bean,并赋值后,可以通过它对bean进行校验,看是否合法。

  • CommonAnnotationBeanPostProcessor
    它的作用是用来处理@PostConstructor,@PreDestroy和@Resource等注解,这些注解是JSR-250注解,位于javax.annotation包中。

  • AutowiredAnnotationBeanPostProcessor
    它的作用是用于处理标注了@Autowired注解的变量或方法。

  • AnnotationAwareAspectJAutoProxyCreator
    它的作用是用于Spring AOP的动态代理。

这里以ApplicationContextAwareProcessor为示例,讲解下spring容器如何利用BeanPostProcessor实现bean功能增强的。其他几个后置处理器在相关主题时再做介绍。

ApplicationContextAwareProcessor

ApplicationContextAwareProcessor是比较简单易懂的Bean后置处理器。
当我们自己的Bean想持有容器对象时可在定义Bean 类的时候实现这么一个接口:ApplicationContextAware,接口定义如下:

package org.springframework.context;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;

/**
 * Interface to be implemented by any object that wishes to be notified
 * of the {@link ApplicationContext} that it runs in.
 *
 * <p>Implementing this interface makes sense for example when an object
 * requires access to a set of collaborating beans. Note that configuration
 * via bean references is preferable to implementing this interface just
 * for bean lookup purposes.
 *
 * <p>This interface can also be implemented if an object needs access to file
 * resources, i.e. wants to call {@code getResource}, wants to publish
 * an application event, or requires access to the MessageSource. However,
 * it is preferable to implement the more specific {@link ResourceLoaderAware},
 * {@link ApplicationEventPublisherAware} or {@link MessageSourceAware} interface
 * in such a specific scenario.
 *
 * <p>Note that file resource dependencies can also be exposed as bean properties
 * of type {@link org.springframework.core.io.Resource}, populated via Strings
 * with automatic type conversion by the bean factory. This removes the need
 * for implementing any callback interface just for the purpose of accessing
 * a specific file resource.
 *
 * <p>{@link org.springframework.context.support.ApplicationObjectSupport} is a
 * convenience base class for application objects, implementing this interface.
 *
 * <p>For a list of all bean lifecycle methods, see the
 * {@link org.springframework.beans.factory.BeanFactory BeanFactory javadocs}.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Chris Beams
 * @see ResourceLoaderAware
 * @see ApplicationEventPublisherAware
 * @see MessageSourceAware
 * @see org.springframework.context.support.ApplicationObjectSupport
 * @see org.springframework.beans.factory.BeanFactoryAware
 */
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;

}

接口本身挺简单,就一个方法setApplicationContext, spring容器会在目标bean初始化的某个阶段调用该方法,将自己注入。

举个示例:
先见一个容器持有工具类,它实现了ApplicationContextAware接口,期望容器初始化该bean时注入spring容器。

package win.elegentjs.spring.ioc.beanpost.appware;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * 持有容器类工具类,方便从容器中获取Bean对象
 */
public class SpringContextHolder implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextHolder.applicationContext = applicationContext;
    }

    /**
     * 从容器中获取Bean
     */
    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

}

定义java config类加载bean

package win.elegentjs.spring.ioc.beanpost.appware;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * java config配置类
 */
@Configuration
public class SpringContextHolderConfig  {

    /**
     * 定义Bean,在容器初始化bean的某个阶段注入容器对象
     */
    @Bean
    public SpringContextHolder springContextHolder() {
        return new SpringContextHolder();
    }
}

写示例,看看执行结果

package win.elegentjs.spring.ioc.beanpost.appware;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

@Slf4j
public class SpringContextHolderSample {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringContextHolderConfig.class);

        SpringContextHolderConfig config = SpringContextHolder.getBean(SpringContextHolderConfig.class);

        log.info("==> config: {}", config);
    }
}

// result:
2021-05-29 16:31:42.851 [main] INFO  w.e.s.i.beanpost.appware.SpringContextHolderSample-==> config: win.elegentjs.spring.ioc.beanpost.appware.SpringContextHolderConfig$$EnhancerBySpringCGLIB$$162c7fe1@1623b78d

可以看出能顺利的获取bean实例,而方法的内部实现是委托spring容器实现,说明spring容器成功注入了。

以下我们分析ApplicationContextAwareProcessor,看看spring容器到底是如何实现容器注入到bean中的。

分析ApplicationContextAwareProcessor类

先上源码:

package org.springframework.context.support;

import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.EmbeddedValueResolver;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.lang.Nullable;
import org.springframework.util.StringValueResolver;

/**
 * {@link BeanPostProcessor} implementation that supplies the {@code ApplicationContext},
 * {@link org.springframework.core.env.Environment Environment}, or
 * {@link StringValueResolver} for the {@code ApplicationContext} to beans that
 * implement the {@link EnvironmentAware}, {@link EmbeddedValueResolverAware},
 * {@link ResourceLoaderAware}, {@link ApplicationEventPublisherAware},
 * {@link MessageSourceAware}, and/or {@link ApplicationContextAware} interfaces.
 *
 * <p>Implemented interfaces are satisfied in the order in which they are
 * mentioned above.
 *
 * <p>Application contexts will automatically register this with their
 * underlying bean factory. Applications do not use this directly.
 *
 * @author Juergen Hoeller
 * @author Costin Leau
 * @author Chris Beams
 * @since 10.10.2003
 * @see org.springframework.context.EnvironmentAware
 * @see org.springframework.context.EmbeddedValueResolverAware
 * @see org.springframework.context.ResourceLoaderAware
 * @see org.springframework.context.ApplicationEventPublisherAware
 * @see org.springframework.context.MessageSourceAware
 * @see org.springframework.context.ApplicationContextAware
 * @see org.springframework.context.support.AbstractApplicationContext#refresh()
 */
class ApplicationContextAwareProcessor implements BeanPostProcessor {

	private final ConfigurableApplicationContext applicationContext;

	private final StringValueResolver embeddedValueResolver;


	/**
	 * Create a new ApplicationContextAwareProcessor for the given context.
	 */
	public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
		this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
	}


	@Override
	@Nullable
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
				bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
				bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
			return bean;
		}

		AccessControlContext acc = null;

		if (System.getSecurityManager() != null) {
			acc = this.applicationContext.getBeanFactory().getAccessControlContext();
		}

		if (acc != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareInterfaces(bean);
				return null;
			}, acc);
		}
		else {
			// 执行Aware接口对象的注入方法,bean就是容器初始化的bean
			invokeAwareInterfaces(bean);
		}

		return bean;
	}

	private void invokeAwareInterfaces(Object bean) {
		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);
		}
		// 判断如果bean实现了ApplicationContextAware接口,则调用setApplicationContext方法,传入容器对象
		if (bean instanceof ApplicationContextAware) {
			((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
		}
	}

}

可以看出ApplicationContextAwareProcessor的逻辑并不难,甚至挺简单,就是在bean实例化并设置了必要属性后,调用了ApplicationContextAwareProcessor的postProcessBeforeInitialization方法,在该方法中执行了setApplicationContext方法注入了容器。

小结

本节通过分析ApplicationContextAwareProcessor的源码了解了容器增强bean背后的秘密,即通过Bean后置处理器实现,干预的时机是两个:

  • Bean实例化,属性设值后,初始化方法执行前
  • Bean实例化,属性设值后,初始化方法执行后

通过这两个阶段,可以插入自己的逻辑,实现对Bean实例的增强,如:

  • 对bean属性做合法性校验
  • 检查是否有特殊的接口,如果有做相应的处理(如本示例注入spring容器本身)
  • 对注解的支持,如:@Autowired, @PostConstructor等
  • AOP,包装生成Bean代理对象,实现对象功能的增强。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值