Spring 擴展點之 Bean工廠的後置處理器

BeanFactoryPostProcessor 的子類

你的贊與交流是我最大的動力,期待與你一塊兒共同進步!java

  在Spring 中提供了擴展點來供程序員擴展實現定製化的功能。在Spring中,容器初始化的時候,一樣也用到了相應的擴展點,來完成容器的初始化。這篇文章中將經過源碼來分析,Spring擴展的實現原理,與使用技巧。以及在Spring中對某一些類的特殊處理。好了,閒言少敘,在下先上圖爲敬!從下圖開始暈車之旅。。。程序員

斷點調試圖一: invokeBeanFactoryPostProcessors斷點調試圖web

private static void invokeBeanFactoryPostProcessors(  Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {   for (BeanFactoryPostProcessor postProcessor : postProcessors) {  /**  * 根據不一樣的 BeanFactoryPostProcessor 實現  * 去調用不一樣的 postProcessBeanFactory 方法  * ConfigurationClassPostProcessor 同時也是 BeanFactoryPostProcessor 的子類  */  postProcessor.postProcessBeanFactory(beanFactory);  } } 複製代碼

  在看具體的postProcessor.postProcessBeanFactory(beanFactory);代碼以前,咱們先看一下在Spring中這裏對應的BeanFactoryPostProcessor的子類實現,以下圖: BeanFactory後置處理器app

  從斷點調試圖一中能夠看出,這裏的 postProcessor 對應的子類爲 ConfigurationClassPostProcessor,下面的 postProcessBeanFactory() 方法對應子類的實現以下:less

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
 int factoryId = System.identityHashCode(beanFactory);  if (this.factoriesPostProcessed.contains(factoryId)) {  throw new IllegalStateException(  "postProcessBeanFactory already called on this post-processor against " + beanFactory);  }  this.factoriesPostProcessed.add(factoryId);  if (!this.registriesPostProcessed.contains(factoryId)) {  // BeanDefinitionRegistryPostProcessor hook apparently not supported...  // Simply call processConfigurationClasses lazily at this point then.  processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);  }  /**  * 配置類 產生 cglib 代理  */  enhanceConfigurationClasses(beanFactory);  beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); } 複製代碼

  上述方法中,主要的功能就是:①加強配置類;②向容器中添加 BeanPsotProcessor 的子類 ImportAwareBeanPostProcessor。 其中對於配置類的加強,是本文要介紹的,這個就和上一篇文章中說起Spring中對於配置類的 FullLite 模式的標記。編輯器

加強配置類

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
 Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();  for (String beanName : beanFactory.getBeanDefinitionNames()) {  BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);  /**  * 判斷這個類是否是全註解類,這個地方與前面 {@link Configuration} 註解的類的  * 處理有關  */  if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {  if (!(beanDef instanceof AbstractBeanDefinition)) {  throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +  beanName + "' since it is not stored in an AbstractBeanDefinition subclass");  }  else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {  logger.info("Cannot enhance @Configuration bean definition '" + beanName +  "' since its singleton instance has been created too early. The typical cause " +  "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +  "return type: Consider declaring such methods as 'static'.");  }  /** 若是是全註解類,就將其 put 到 configBeanDefs 中*/  configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);  }  }  if (configBeanDefs.isEmpty()) {  /**  * Map 爲空 表示沒有全註解類,則返回  */  // nothing to enhance -> return immediately  return;  }   ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();  for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {  AbstractBeanDefinition beanDef = entry.getValue();  // If a @Configuration class gets proxied, always proxy the target class  beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);  try {  // Set enhanced subclass of the user-specified bean class  /**  * 對全註解類 進行 cglib 代理  * config 類 -> cglib class -> BeanDefinition -> bean  */  Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);  if (configClass != null) {  Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);  if (configClass != enhancedClass) {  if (logger.isTraceEnabled()) {  logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +  "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));  }  beanDef.setBeanClass(enhancedClass);  }  }  }  catch (Throwable ex) {  throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);  }  } } 複製代碼

  接上篇文章咱們提到配置類的 FullLite兩種不一樣的模式。在這篇文章中,咱們能夠看到,Full模式,也就是全配置類,Spring 經過使用CGLIB動態代理的 方式對其進行了加強。而 Lite 模式的配置類,沒有經過代理的方式加強。咱們究其緣由發現,對於@Configuration 類的處理,是Spring 的後置處理器的典型應用。縱觀整個 Spring, 在器內部只有一個 ConfigurationClassPostProcessor 該類中處理了 BeanDefinitionRegistryPostProcessor 的方法 postProcessBeanDefinitionRegistry() 也處理了 postProcessBeanFactory的方法 postProcessBeanFactory()。經過該類,咱們應該也要知道,對於Spring 的擴展點 BeanFactoryPostProcessor 的處理。ide

  對於配置類,先經過 resolveBeanClass() 解析獲得類的Class對象,而後經過 enhancer.enhance(configClass, this.beanClassLoader ) 獲得加強後的類對象。最後再將這個加強後的類對象,設置回對應的 BeanDefinition中去。post

CGLIB 代理加強

public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
 /** 判斷是否被代理過*/  if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {  if (logger.isDebugEnabled()) {  logger.debug(String.format("Ignoring request to enhance %s as it has " +  "already been enhanced. This usually indicates that more than one " +  "ConfigurationClassPostProcessor has been registered (e.g. via " +  "<context:annotation-config>). This is harmless, but you may " +  "want check your configuration and remove one CCPP if possible",  configClass.getName()));  }  return configClass;  }  /** 沒有被代理 cglib 代理*/  Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));  if (logger.isTraceEnabled()) {  logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",  configClass.getName(), enhancedClass.getName()));  }  return enhancedClass; } 複製代碼

newEnhancer() 方法

/**  * Creates a new CGLIB {@link Enhancer} instance.  * 建立一個 CGLIB 實例  */ private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {  Enhancer enhancer = new Enhancer();  /** 加強父類 */  enhancer.setSuperclass(configSuperClass);  /** 加強接口,  * 便於判斷,表示一個類被加強了  * EnhancedConfiguration 實現了 BeanFactoryAware 接口  */  enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});  enhancer.setUseFactory(false);  /**  * BeanFactoryAwareGeneratorStrategy 是一個生成策略  * 主要爲生成的 cglib 類中添加成員變量 $beanFactory  * 同時基於接口 EnhancedConfiguration 的父接口 BeanFactoryAware 中的 setBeanFactory 方法,  * 設置此變量的值爲當前 context 中的 beanFactory,這樣一來 cglib 代理的對象就有了 beanFactory  * 有了 factory 就能得到對象了,不用經過 new 來獲取對象了  * 該BeanFactory 的做用是在 this 調用時攔截該調用,並直接在 beanFactory 中得到目標bean  *  */  enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);  enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));  enhancer.setCallbackFilter(CALLBACK_FILTER);  enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());  return enhancer; } 複製代碼

createClass()

/**  * 使用加強器生成超類的子類,  * 確保新的子類註冊了回調  * Uses enhancer to generate a subclass of superclass,  * ensuring that callbacks are registered for the new subclass.  */ private Class<?> createClass(Enhancer enhancer) {  Class<?> subclass = enhancer.createClass();  // Registering callbacks statically (as opposed to thread-local)  // is critical for usage in an OSGi environment (SPR-5932)...  Enhancer.registerStaticCallbacks(subclass, CALLBACKS);  return subclass; } 複製代碼

  其中 CALLBACKS 的定義以下:this

private static final Callback[] CALLBACKS = new Callback[] {
 /**  * 加強方法,主要控制bean的做用域,  * 不用每次都 new  */  // Bean 方法來攔截器  new BeanMethodInterceptor(),  // 攔截BeanFactoryAware 定義的方法 setBeanFactory  new BeanFactoryAwareMethodInterceptor(),  NoOp.INSTANCE }; 複製代碼

  對應的 new BeanMethodInterceptor() 攔截方法調用,new BeanFactoryAwareMethodInterceptor() 攔截 BeanFactoryAware 定義的方法 setBeanFactory。對應代碼實現以下url

攔截方法調用

private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {
  /**  * Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the  * existence of this bean object.  * @throws Throwable as a catch-all for any exception that may be thrown when invoking the  * super implementation of the proxied method i.e., the actual {@code @Bean} method  */  @Override  @Nullable  public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,  MethodProxy cglibMethodProxy) throws Throwable {  /**  * enhancedConfigInstance 代理  * 經過enhancedConfigInstance 中 CGLIB 生成的成員變量 &&beanFactory 獲取 beanFactory  */  ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);  String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);   // Determine whether this bean is a scoped-proxy  if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {  String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);  if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {  beanName = scopedBeanName;  }  }   // To handle the case of an inter-bean method reference, we must explicitly check the  // container for already cached instances.   // First, check to see if the requested bean is a FactoryBean. If so, create a subclass  // proxy that intercepts calls to getObject() and returns any cached bean instance.  // This ensures that the semantics of calling a FactoryBean from within @Bean methods  // is the same as that of referring to a FactoryBean within XML. See SPR-6602.  if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&  factoryContainsBean(beanFactory, beanName)) {  Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);  if (factoryBean instanceof ScopedProxyFactoryBean) {  // Scoped proxy factory beans are a special case and should not be further proxied  }  else {  // It is a candidate FactoryBean - go ahead with enhancement  return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);  }  }  /**  * 判斷執行的方法 和 調用的方法是否是同一個方法  */  if (isCurrentlyInvokedFactoryMethod(beanMethod)) {  // The factory is calling the bean method in order to instantiate and register the bean  // (i.e. via a getBean() call) -> invoke the super implementation of the method to actually  // create the bean instance.  if (logger.isInfoEnabled() &&  BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {  logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +  "assignable to Spring's BeanFactoryPostProcessor interface. This will " +  "result in a failure to process annotations such as @Autowired, " +  "@Resource and @PostConstruct within the method's declaring " +  "@Configuration class. Add the 'static' modifier to this method to avoid " +  "these container lifecycle issues; see @Bean javadoc for complete details.",  beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));  }  /**  * 執行的方法和調用的方法是同一個 執行父類的方法 建立對象  */  return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);  }  /**  * 執行的方法和調用的方法不是同一個,在 &&BeanFactory 中 get 一個 bean 出來  */  return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);  }   private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,  ConfigurableBeanFactory beanFactory, String beanName) {   /**  * 判斷對象是否正在建立  * 一個對象有三種狀態  * a. 沒有建立  * b. 正在建立  * c. 建立成功  */  boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);  try {  if (alreadyInCreation) {  beanFactory.setCurrentlyInCreation(beanName, false);  }  boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs);  if (useArgs && beanFactory.isSingleton(beanName)) {  // Stubbed null arguments just for reference purposes,  // expecting them to be autowired for regular singleton references?  // A safe assumption since @Bean singleton arguments cannot be optional...  for (Object arg : beanMethodArgs) {  if (arg == null) {  useArgs = false;  break;  }  }  }  /**  * 調用 beanFactory.getBean() 獲取對象  */  Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :  beanFactory.getBean(beanName));  if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {  // Detect package-protected NullBean instance through equals(null) check  if (beanInstance.equals(null)) {  if (logger.isDebugEnabled()) {  logger.debug(String.format("@Bean method %s.%s called as bean reference " +  "for type [%s] returned null bean; resolving to null value.",  beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),  beanMethod.getReturnType().getName()));  }  beanInstance = null;  }  else {  String msg = String.format("@Bean method %s.%s called as bean reference " +  "for type [%s] but overridden by non-compatible bean instance of type [%s].",  beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),  beanMethod.getReturnType().getName(), beanInstance.getClass().getName());  try {  BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);  msg += " Overriding bean of same name declared in: " + beanDefinition.getResourceDescription();  }  catch (NoSuchBeanDefinitionException ex) {  // Ignore - simply no detailed message then.  }  throw new IllegalStateException(msg);  }  }  Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();  if (currentlyInvoked != null) {  String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked);  beanFactory.registerDependentBean(beanName, outerBeanName);  }  return beanInstance;  }  finally {  if (alreadyInCreation) {  beanFactory.setCurrentlyInCreation(beanName, true);  }  }  } } 複製代碼

經過加強之後,配置類中使用@Bean註解的bean定義方法就再也不是普通的方法了,它們具備了以下跟bean做用域有關的能力,以單例bean爲例 :

  1. 它們首次被調用時,相應方法邏輯會被執行用於建立bean實例;
  2. 再次被調用時,不會再執行建立bean實例,而是根據bean名稱返回首次該方法被執行時建立的bean實例。

總結

  至此,在 refresh()方法中的,invokeBeanFactoryPostProcessors() 部分的代碼,已經分析完了。在這一步中,完成了對BeanFactoryPostProcessor的處理,其中有一個極爲重要的實現 ConfigurationPostProcessor 對應 Spring中配置類的處理。在這步處理完成的時候,咱們定義的Bean,都已經被註冊到了 BeanDefinitionMap中。

本文使用 mdnice 排版

相關文章
相關標籤/搜索