【源码】Spring —— @Configuration 3 FULL 配置类的原理

85 阅读5分钟

【源码】Spring —— @Configuration 3 FULL 配置类的原理

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

之前的章节解读了 配置类 的解析时机与过程,同时提到了 FULLLITE 两种模式的 配置类,他们的区别在于,前者会代为管理 @Bean 之间的依赖关系,换句话说:对于 bean 方法的调用会转而去 BeanFactory 中获取对应的 bean 实例,看个代码示例:

@Configuration
public class ProxyConfigurationConfig {

    class Son {
        public Son() {
            System.out.println("son");
        }
    }

    class Parent {
        Son son;

        public Parent(Son son) {
            this.son = son;
            System.out.println("parent");
        }

        public Son getSon() {
            return son;
        }

        public void setSon(Son son) {
            this.son = son;
        }
    }

    @Bean
    public Son son() {

        return new Son();
    }

    @Bean
    public Parent parent() {

        return new Parent(son());
    }
}

结果
可以看到,尽管 parent 方法调用了 son 方法,但 Son 实例确实只初始化了一次,也就是说,son 方法获取实例不再是抽象创建,而是从 BeanFactory 获取

当然,这不是猜测,下文给出相关源码解读

版本

springframework:5.2.x

ConfigurationClassPostProcessor#postProcessBeanFactory

之前的章节已解读,在 容器 的启动生命周期中,会依次调用 ConfigurationClassPostProcessorprocessConfigBeanDefinitionspostProcessBeanFactory 方法,前者正是我们前面的章节重点解读的 配置类 的解析,后者就是本章节的重点,配置类 的代理

PS:这种动态改变行为的实现,基本就是 代理 没跑了

	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)) {
			processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
		}

		// 代理处理
		enhanceConfigurationClasses(beanFactory);

		// 给容器注册一个 ImportAwareBeanPostProcessor
		beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
	}

代理的实现就是 enhanceConfigurationClasses 方法

ConfigurationClassPostProcessor#enhanceConfigurationClasses

	public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
		
		// ... 筛选出所有的 FULL 配置类

		// 遍历进行 CGLIB 代理,由 ConfigurationClassEnhancer#enhance 方法实现
		ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
		for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
			AbstractBeanDefinition beanDef = entry.getValue();
			beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
			Class<?> configClass = beanDef.getBeanClass();
			Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
			if (configClass != enhancedClass) {
				/**
				 * 这里替换了 bd 的 beanClass 属性,因此
				 * 因此之后创建的 bean 就是一个增强过的代理对象
				 * 同时,这个代理对象可以参与后续 bean 的生命周期,比如:属性赋值
				 */
				beanDef.setBeanClass(enhancedClass);
			}
		}
	}
  • 筛选出所有的 FULL 配置类
  • 遍历进行代理,此处使用 CGLIB 代理,因为之前提到 配置类 会同样解析父类的 @Bean 方法,也就是说,配置类允许 继承 行为
  • 代理逻辑由 ConfigurationClassEnhancer#enhance 方法实现

ConfigurationClassEnhancer#enhance

	public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
		
		// ...
		
		// 创建代理
		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 --------------------

	private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(configSuperClass);

		// 设置的接口 EnhancedConfiguration 继承了 BeanFactoryAware
		// 因此代理类可以持有 BeanFactory 对象
		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;
	}

	-------------------- createClass --------------------

	private Class<?> createClass(Enhancer enhancer) {
		Class<?> subclass = enhancer.createClass();
		
		// 拦截器
		Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
		return subclass;
	}

	------------------------------------------------

	// 注册的三(两)个拦截器
	private static final Callback[] CALLBACKS = new Callback[] {
			new BeanMethodInterceptor(),
			new BeanFactoryAwareMethodInterceptor(),
			NoOp.INSTANCE
	};

Enhancer 创建代理对象,注册了两个核心拦截器 BeanMethodInterceptor BeanFactoryAwareMethodInterceptor,都是 ConfigurationClassEnhancer 的内部类(高内聚的体现)

BeanFactoryAwareMethodInterceptor

	private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor, ConditionalCallback {

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

			if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
				return proxy.invokeSuper(obj, args);
			}
			return null;
		}

		@Override
		public boolean isMatch(Method candidateMethod) {
			return isSetBeanFactory(candidateMethod);
		}

		// 拦截 setBeanFactory 方法
		public static boolean isSetBeanFactory(Method candidateMethod) {
			return (candidateMethod.getName().equals("setBeanFactory") &&
					candidateMethod.getParameterCount() == 1 &&
					BeanFactory.class == candidateMethod.getParameterTypes()[0] &&
					BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
		}
	}
  • 因为间接实现了 BeanFactoryAware 接口,因此 setBeanFactory 持有 BeanFactory 对象,此处把该对象设置到 $$beanFactory 字段中
  • setBeanFactory 方法原逻辑保持

BeanMethodInterceptor

核心的拦截器,逻辑也比较复杂,主要作用就是通过拦截 @Bean 方法,将方法指向 容器 中获取对应的 bean 实例

BeanMethodInterceptor#isMatch

	// 拦截 @Bean 方法
	public boolean isMatch(Method candidateMethod) {
		return (candidateMethod.getDeclaringClass() != Object.class &&
				!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
				BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
	}

主要是拦截 @Bean 注解标注的方法

BeanMethodInterceptor#intercept

	public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
					MethodProxy cglibMethodProxy) throws Throwable {

			// 从之前设置的 $$beanFactory 字段获取 beanFactory
			ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);

			// 解析 beanName,一般就是方法名
			String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

			// Scope 相关
			if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
				String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
				if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
					beanName = scopedBeanName;
				}
			}

			/**
			 * 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) {
				
				else {
					return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
				}
			}

			/**
			 * 检查给定的方法是否与容器当前调用的工厂方法相对应
			 * @Bean
			 * public Son son() {
			 * 	    return new Son();
			 * }
			 *
			 * @Bean
			 * public Parent parent() {
			 *     return new Parent(son());
			 * }
			 * 具体的说,形如以上的示例:
			 * 在创建 Son 的 bean 实例时调用 son() 方法,即表明
			 * 		给定的方法与容器当前调用的工厂方法相对应,则
			 * 		不作处理,对应于方法 cglibMethodProxy.invokeSuper
			 * 在创建 Parent 的 bean 实例时调用到 son() 方法,
			 * 		则给定的方法不与容器当前调用的工厂方法相对应,
			 * 		需要代理指向 beanFactory#getBean 方法,
			 * 		对应于 resolveBeanReference 方法
			 */
			if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
		
				/**
				 * 这个地方的场景是这样的:
				 * BeanFactoryPostProcessor 的 bean 实例的创建理论上是要先于 配置类 bean 的
				 * 但是如果在配置类中创建 BeanFactoryPostProcessor 类型的 bean
				 * 则在创建该 bean 之前会先去创建对应 配置类 的 bean,导致 @Autowired 等注解无法处理
				 * 因此此处会给出日志信息,同时给出解决方法:将 BeanFactoryPostProcessor 的 bean
				 * 		声明为 static 类型
				 */
				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);
		}

该方法便是代理处理的核心逻辑了,注释中给出了详细的解读,这里做个简单地总结:

  • 取出我们上一个 拦截器 设置的 beanFactory 用于支持后续操作
  • 对于 FactoryBean,我们将其 getObject 方法代理到 beanFactory#getBean
  • 对于 @Bean 方法的调用:
    1)如果给定的方法与容器当前调用的工厂方法相对应,则不作处理,比如:创建 Son 的实例时调用 son() 方法是不用处理的,而创建 Parent 的实例时调用 son() 方法是要被处理成从容器获取的
    2)反之,将其代理到 beanFactory#getBean

总结

至此,对 FULL 配置里的代理做了基本的解读,再往后的细节便不再解读了,如果想了解,文末会给出对应的链接以供参考

借助这波操作,配置类 内对其他 @Bean 方法的调用便指向了从 容器 中获取,在这很多业务场景下是必须的