Spring注解源码解析(2):@Configuration和@Bean

Spring注解源码解析(2):@Configuration和@Bean

image-20220612224023830

ConfigurationClassPostProcessor中,工厂后处理器的原生接口BeanFactoryPostProcessor中的postProcessBeanFactory方法:

方法postProcessBeanDefinitionRegistry中的逻辑是类似的,都是通过容器获取到一个hash code。

然后通过集合factoriesPostProcessed,判断方法postProcessBeanFactory是否已经被执行过了,集合factoriesPostProcessed同样也是用来防止方法postProcessBeanFactory被重复执行的,在正式执行postProcessBeanFactory方法的核心逻辑之前,同样会判断下集合registriesPostProcessed中是否已经存在factoryId了。

@Override
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);
   }

   enhanceConfigurationClasses(beanFactory);
   beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

方法enhanceConfigurationClasses了,通过方法名称我们可以知道,方法enhanceConfigurationClasses是对配置类进行一些增强的处理,

可以看到,在方法enhanceConfigurationClasses中,首先遍历Spring容器中的所有BeanDefinition,并从BeanDefinition中获取属性“org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass”的值。

前面我们已经看到了,该属性的值为CONFIGURATION_CLASS_FULL,也就是“full”,所以configClassAttr不为空,然后,因为BeanDefinition的确是注解类型的,所以会将BeanDefinition强转为AnnotatedBeanDefinition,并通过方法getFactoryMethodMetadata,获取工厂方法相关的注解信息

因为属性值configClassAttr不为空,且beanDef是AbstractBeanDefinition的实例,所以,if分支逻辑成立,判断下BeanDefinition中的类是否存在,如果不存在此时就要通过类加载器加载一下。

然后,我们可以看到,如果发现属性值configClassAttr等于ConfigurationClassUtils.CONFIGURATION_CLASS_FULL,确实,前面我们已经看到了configClassAttr设置的就是该值,所以,接下来就会将符合增强条件的BeanDefinition添加到configBeanDefs中。

而且,我们可以发现如果configBeanDefs为空,直接就会return返回了。

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
   Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
  //遍历Spring容器中的所有BeanDefinition
   for (String beanName : beanFactory.getBeanDefinitionNames()) {
      BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
     //并从BeanDefinition中获取属性“*org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass*”的值
      Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
      MethodMetadata methodMetadata = null;
      if (beanDef instanceof AnnotatedBeanDefinition) {
        //获取工厂方法相关的注解信息
         methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
      }
     //属性值configClassAttr不为空,且beanDef是AbstractBeanDefinition的实例
      if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
        //判断下BeanDefinition中的类是否存在,如果不存在此时就要通过类加载器加载一下。
         AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
         if (!abd.hasBeanClass()) {
            try {
               abd.resolveBeanClass(this.beanClassLoader);
            }
            catch (Throwable ex) {
               throw new IllegalStateException(
                     "Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
            }
         }
      }
      if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
         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'.");
         }
         configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
      }
     //我们可以发现如果configBeanDefs为空,直接就会return返回了。
     	if (configBeanDefs.isEmpty()) {
			// nothing to enhance -> return immediately
			return;
		}
   }
对注解配置类进行增强操作

先创建了一个ConfigurationClassEnhancer类型的组件enhancer,通过类名我们可以知道,它应该就是对注解配置类进行增强的组件了。

接下来会遍历configBeanDefs中的每个元素也就是BeanDefinition,通过enhancer组件对注解配置类configClass进行增强,最后将增强好的类enhancedClass重新再设置到BeanDefinition中。


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);
   // Set enhanced subclass of the user-specified bean class
   Class<?> configClass = beanDef.getBeanClass();
   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);
   }
}

Spring底层对添加了注解@Configuration注解的类,使用的就是cglib的方式进行增强的。

最终会通过cglib动态代理方式创建一个动态代理出来,这也就是为什么我们案例中,在打印出来的对象信息中含有“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;
   }
   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;
}

private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(configSuperClass);
		enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
		enhancer.setUseFactory(false);
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
		enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
		enhancer.setCallbackFilter(CALLBACK_FILTER);
		enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
		return enhancer;
	}

在newEnhancer方法中,其中还为enhancer设置了一个策略类BeanFactoryAwareGeneratorStrategy,我们可以到这里类中看下:

可以看到,在策略类BeanFactoryAwareGeneratorStrategy中,在增强注解配置类的时候会在原来类的基础之上,添加一个BeanFactory类型的字段,字段名称为“$$beanFactory。”

private static class BeanFactoryAwareGeneratorStrategy extends
      ClassLoaderAwareGeneratorStrategy {

   public BeanFactoryAwareGeneratorStrategy(@Nullable ClassLoader classLoader) {
      super(classLoader);
   }

   @Override
   protected ClassGenerator transform(ClassGenerator cg) throws Exception {
      ClassEmitterTransformer transformer = new ClassEmitterTransformer() {
         @Override
         public void end_class() {
            declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null);
            super.end_class();
         }
      };
      return new TransformingClassGenerator(cg, transformer);
   }

}

这里还为增强的子类添加了一些回调接口,回调接口CALLBACKS,其实就是一些拦截器

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;
}

	private static final Callback[] CALLBACKS = new Callback[] {
			new BeanMethodInterceptor(),
			new BeanFactoryAwareMethodInterceptor(),
			NoOp.INSTANCE
	};
BeanFactoryAwareMethodInterceptor

注解配置类增强之后,会添加一个BeanFactory类型的字段,字段名称为“$$beanFactory”。

而我们看到在方法intercept中,果然这里就会检查一下增强后的注解配置类中,是否存在名称为“$$beanFactory”的字段,如果存在的话就会通过反射API为该字段赋值,这样的话,增强后的注解配置类就得到了Spring容器BeanFactory的一个引用了。

@Override
@Nullable
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
   Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
   Assert.state(field != null, "Unable to find generated BeanFactory field");
   field.set(obj, args[0]);

   // Does the actual (non-CGLIB) superclass implement BeanFactoryAware?
   // If so, call its setBeanFactory() method. If not, just exit.
   if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
      return proxy.invokeSuper(obj, args);
   }
   return null;
}
获取@Bean对应bean的名称

首先会调用getBeanFactory方法,从增强的注解配置类实例中获取容器BeanFactory,再获取@Bean对应bean的名称了

Spring默认会使用方法的名称作为bean的名称。如果我们在注解@Bean中指定了属性name的值,这个时候Spring优先会使用注解@Bean中name属性的值作为bean的名称。

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 {
//设置的字段$$beanFactory中获取容器BeanFactory。
      ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
     //获取@Bean对应bean的名称了
      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;
         }
      }

     	public static String determineBeanNameFor(Method beanMethod) {
		String beanName = beanNameCache.get(beanMethod);
		if (beanName == null) {
			// By default, the bean name is the name of the @Bean-annotated method
			beanName = beanMethod.getName();
			// Check to see if the user has explicitly set a custom bean name...
			AnnotationAttributes bean =
					AnnotatedElementUtils.findMergedAnnotationAttributes(beanMethod, Bean.class, false, false);
			if (bean != null) {
				String[] names = bean.getStringArray("name");
				if (names.length > 0) {
					beanName = names[0];
				}
			}
			beanNameCache.put(beanMethod, beanName);
		}
		return beanName;
	}
    }
增强FactoryBean类型的bean

接下来会通过方法factoryContainsBean,判断Spring容器中是否存在名称为beanName的bean,以及在beanName前添加前缀符号“&”后,看下对应的FactoryBean是否存在。

如果beanName前面加上符号“&”,就表示要获取该bean对应的FactoryBean。

可以看到,如果这两个条件都满足的话,此时就会从Spring容器中获取factoryBean,然后调用方法enhanceFactoryBean对FactoryBean进行进一步的增强。

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);
   }
}

enhanceFactoryBean()

方法enhanceFactoryBean中,首先判断factoryBean这个类中,类名和方法getObject是否被final关键字修饰,如果factoryBean对应的类或者是方法getObject中,有一个是被final关键字修饰的,则if分支成立。

接下来,会判断exposedType也就是@Bean标注方法的返回值类型、同时也是@Bean对应bean的类型,如果发现bean是接口类型的话,此时会调用方法createInterfaceProxyForFactoryBean创建动态代理,如果不是接口类型的话,直接返回factoryBean了

方法createInterfaceProxyForFactoryBean中,其实就是通过jdk动态代理来创建动态代理的,当动态代理执行方法时,如果执行方法的名称是“getObject”,此时就会从Spring容器中获取beanName对应的bean实例、如果动态代理执行方法名称不是“getObject”时,此时会执行RelectionUtils中的方法invokeMethod方法

private Object enhanceFactoryBean(final Object factoryBean, Class<?> exposedType,
      final ConfigurableBeanFactory beanFactory, final String beanName) {
      Class<?> clazz = factoryBean.getClass();
      boolean finalClass = Modifier.isFinal(clazz.getModifiers());
      boolean finalMethod = Modifier.isFinal(clazz.getMethod("getObject").getModifiers());
  //首先判断factoryBean这个类中,类名和方法getObject是否被final关键字修饰
      if (finalClass || finalMethod) {
         if (exposedType.isInterface()) {
            if (logger.isTraceEnabled()) {
               logger.trace("Creating interface proxy for FactoryBean '" + beanName + "' of type [" +
                     clazz.getName() + "] for use within another @Bean method because its " +
                     (finalClass ? "implementation class" : "getObject() method") +
                     " is final: Otherwise a getObject() call would not be routed to the factory.");
            }
           //发现bean是接口类型的话,此时会调用方法jdk动态代理
            return createInterfaceProxyForFactoryBean(factoryBean, exposedType, beanFactory, beanName);
         }
      }
   }
   return createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);
}

//方法createInterfaceProxyForFactoryBean中,其实就是通过jdk动态代理来创建动态代理的,当动态代理执行方法时,如果执行方法的名称是“getObject”,此时就会从Spring容器中获取beanName对应的bean实例
//如果动态代理执行方法名称不是“getObject”时,此时会执行RelectionUtils中的方法invokeMethod方
private Object createInterfaceProxyForFactoryBean(final Object factoryBean, Class<?> interfaceType,
				final ConfigurableBeanFactory beanFactory, final String beanName) {

			return Proxy.newProxyInstance(
					factoryBean.getClass().getClassLoader(), new Class<?>[] {interfaceType},
					(proxy, method, args) -> {
            //当动态代理执行方法时,如果执行方法的名称是“getObject”,此时就会从Spring容器中获取beanName对应的bean实例。
						if (method.getName().equals("getObject") && args == null) {
              //调用方法是FactoryBean的中getObject方法,通过beanName从容器中获取bean
							return beanFactory.getBean(beanName);
						}
            //如果动态代理执行方法名称不是“getObject”时,就会执行FactoryBean中的该方法了。
						return ReflectionUtils.invokeMethod(method, factoryBean, args);
					});
		}

同理,如果FactoryBean中的类名或方法名,都不是final关键字修饰的,则方法createCglibProxyForFactoryBean果然就是通过cglib的方式来创建动态代理。

可以看到,首先会创建一个增强的代理类fbClass,然后通过反射API,利用无参构造方法来实例化了一个代理对象fbProxy,最后再为动态代理设置了一个回调接口,也就是方法拦截器MethodInterceptor。

在方法拦截器中的逻辑,和前面的jdk动态代理是类似的,如果当前bean执行的方法名称为“getObject”,此时就会根据名称beanName从Spring容器中获取bean的实例,而如果当前bean执行其他方法,此时就会委托FactoryBean去执行该方法

获取普通类型的bean的增强

如果被注解@Bean标注的方法,同时还指定了工厂方法,且工厂方法就是当前的@Bean标注的方法,此时就会让当前方法的代理对象cglibMethodProxy去执行invokeSuper方法,调用工厂方法来实例化bean实例。

而如果当前被注解@Bean标注的方法,它的名称既不能在Spring容器中找到对应的FactoryBean,同时当前被注解@Bean标注的方法也没有指定工厂方法,此时就会执行方法resolveBeanReference

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);
}
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);

resolveBeanReference()

方法resolveBeanReference其实就是通过beanName,从Spring容器中获取bean的实例。

所以,我们可以知道在注解@Configuration的注解配置类下,不仅会为每个添加了注解@Bean的方法创建cglib动态代理,而且底层动态代理获取到的bean实例的方式,默认的方式还是从Spring容器中获取到的。


private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
      ConfigurableBeanFactory beanFactory, String beanName) {

   // The user (i.e. not the factory) is requesting this bean through a call to
   // the bean method, direct or indirect. The bean may have already been marked
   // as 'in creation' in certain autowiring scenarios; if so, temporarily set
   // the in-creation status to false in order to avoid an exception.
   
      Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
            beanFactory.getBean(beanName));
    
      Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
      if (currentlyInvoked != null) {
         String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked);
         beanFactory.registerDependentBean(beanName, outerBeanName);
      }
      return beanInstance;
   }
}

所以,我们可以知道在注解@Configuration的注解配置类下,不仅会为每个添加了注解@Bean的方法创建cglib动态代理,而且底层动态代理获取到的bean实例的方式,默认的方式还是从Spring容器中获取到的。

至于这些被注解@Bean标注的方法对应的bean实例,究竟是怎么初始化的呢?前面我们也看到了,添加了注解@Bean的每个方法,都会被封装为BeanDefinition并注册到Spring容器中,而Spring高级容器在初始化阶段,就会将这些非延迟初始化的bean实例给提前初始化好。

所以,在我们实际要用到这些通过注解@Bean指定的bean时,底层的动态代理就会直接从Spring容器中,获取bean的实例了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值