springboot启动bean加载处理器ConfigurationClassPostProcessor 四(@bean注解)

我之前已经介绍了ConfigurationClassPostProcessor 对另外几个注解的处理流程:

这期再来看下另外一个重要的注解@bean, 我们使用springboot后,去掉了xml的繁琐配置,日常开发中都是通过这个注解向spring容器中注入bean,当然我们要配合使用**@Configuration**使用,例如

@Configuration
//@Component 也可以用这个注解
public class TestBean {

    @Bean(name = "bean1")
    public ClassBean bean1(){
        return new ClassBean(bean2());
    }

    @Bean(name = "bean2")
    public ClassReBean bean2(){
        return new ClassReBean();
    }
}

就向代码注释中描述的,我们也可以配合**@Component**注解使用,那么这两个有什么区别呢?
在spring容器中bean可以有两种方式的定义:

  • full,配合@Configuration注解

  • lite,配合@Component注解

现在我们一起来下spring是怎么区分这两种方式,及区分这两种方式的目的是啥!!

现在先回到我们的处理类 ConfigurationClassPostProcessor,这个类中对@bean 的解析很简单,就是获取sourceclass中带有@bean注解的方法(包括processInterfaces(configClass, sourceClass)方法,解析父类的@bean注解),然后放到sourceclass中的beanMethods属性中。
在ConfigurationClassPostProcessor 中parse方法执行完成后,会返回所有满足条件的class集合,这个时候叫ConfigurationClass。这个时候对当前的集合进行循环做一个重要的事情,找出带有@Configuration 注解的类标记属性为full,其它的为lite

在这里插入图片描述
这时候ConfigurationClassPostProcessor 一个使命已经完成了(加载bean定义到spring容器中),他还有另外一个重要的重用,我们看下ConfigurationClassPostProcessor类关系
在这里插入图片描述
它实现了BeanFactoryPostProcessor,它里面有个重要的接口定义

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

	/**
	 * Modify the application context's internal bean definition registry after its
	 * standard initialization. All regular bean definitions will have been loaded,
	 * but no beans will have been instantiated yet. This allows for adding further
	 * bean definitions before the next post-processing phase kicks in.
	 * @param registry the bean definition registry used by the application context
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

ConfigurationClassPostProcessor 对这个接口的实现是:

@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(beanFactory);

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
		Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
		for (String beanName : beanFactory.getBeanDefinitionNames()) {
			BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
			Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
			MethodMetadata methodMetadata = null;
			if (beanDef instanceof AnnotatedBeanDefinition) {
				methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
			}
			if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
				// Configuration class (full or lite) or a configuration-derived @Bean method
				// -> resolve bean class at this point...
				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);
			}
		}
		if (configBeanDefs.isEmpty()) {
			// 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);
			// 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()));
				}
				//替换class类型
				beanDef.setBeanClass(enhancedClass);
			}
		}
	}

方法是重要,拆分出来就是两个循环:

  1. 找出所有类型是full的class
  2. 对full类型的class进行CGLIB代理并替换

所以spring容器中如果有带有@Configuration注解的类都会被代理,类型是full模式!,@Bean方法不能被private/final等进行修饰

看下ConfigurationClassPostProcessor 整体流程:
在这里插入图片描述

对替换的代理类,spring加两个重要的方法拦截器

  1. BeanMethodInterceptor,这个就是对@bean注解的方法进行拦截处理
  2. BeanFactoryAwareMethodInterceptor

重点来观察下BeanMethodInterceptor的拦截都干了啥!!

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 {

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

			return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
		}

再结合我们当前的例子:

@Configuration
//@Component
public class TestBean {

    @Bean(name = "bean1")
    public ClassBean bean1(){
        return new ClassBean(bean2());
    }

    @Bean(name = "bean2")
    public ClassReBean bean2(){
        return new ClassReBean();
    }
}

当方法bean1()执行后,这个时候isCurrentlyInvokedFactoryMethod(beanMethod)方法 (true),进入真正要执行的new ClassBean(bean2()),这个时候会执行bean2(),又会被当前方法拦截器拦截,这个时候isCurrentlyInvokedFactoryMethod(beanMethod)(false),这个时候就会走最后的resolveBeanReference方法,这个方法会创建bean(实际还走bean创建流程,进入当前的方法拦截器,isCurrentlyInvokedFactoryMethod==true,返回真正的bean2()对应的ClassReBean ),所以ClassReBean 不会创建两次,这个就叫full模式
这个创建过程有点绕,其实就是为了包装只创建一次!
在这里插入图片描述

当然如果使用@Component,也想要只创建一次的办法是加@Autowired注解

@Component
public class TestBean {

    @Autowired
    private ClassReBean classReBean;

    @Bean(name = "bean1")
    public ClassBean bean1(){
        return new ClassBean(classReBean);
    }

    @Bean(name = "bean2")
    public ClassReBean bean2(){
        return new ClassReBean();
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值