写点什么

Spring 源码 -@Configuration 注解解析

用户头像
魔曦
关注
发布于: 2021 年 01 月 17 日
Spring源码-@Configuration注解解析

这个注解在项目开发中出现在配置类上面,配置类有无这个注解都没有问题,那么此注解的作用是神马呢?

现象

#Demo#

@ComponentScan("com.spring.test.ch04")@Configurationpublic class Ch04Config {

@Bean public UserService userService(){ UserService userService = new UserService(); System.out.println("创建UserService "+ userService); return userService; }
@Bean public OrderService orderService(){ UserService userService = userService(); System.out.println("创建OrderService "+ userService); return new OrderService(userService); }}
public class OrderService {
public OrderService(UserService userService) { System.out.println("orderService 构造方法"); }}
public class UserService {}
public class Ch04Test {
public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); ac.register(Ch04Config.class); ac.refresh(); System.out.println(ac.getBean("orderService")); }}
复制代码


  • 现象 1:观察 ac#beanFactory#beanDefinitionMap、ac#beanFactory#singletonObjects 中 Ch04Config 对象

  • 配置类无 @Configuration 注解



  • 配置类有 @Configuration 注解


从以上现象可以得知有 @Configuration 注解的时候配置类的在 spring 的容器中存储的是一个代理对象。

  • 现象 2:demo 的配置类中通过 @Bean 定义了两个对象 UerService、OrderService,OrderService 提供一个有参数的构造方法,创建对象的时候需要提供一个参数;

  • 配置类无 @Configuration 注解


  • 配置类有 @Configuration 注解

从以上现象可以得知

  • 有 @Configuration 注解时创建 OrderService 时入参 UserService 对象是一个对象

  • 无 @Configuration 注解时创建 OrderService 时入参 UserService 对象是新创建的一个对象,也就是说 UserService 实例化了两次

分析

有 @Configuration 注解情况下 Spring 是如何避免 UserService 两次实例化的呢?要分析根因就的死磕源码。

  1. 首先 spring 根据配置类生成 BeanDefinition 的时候,判断类是有 @Configuration 注解且 proxyBeanMethods 为 true(该属性默认是 true)的情况,在 BeanDefinition 中存入一个属性 CONFIGURATION_CLASS_ATTRIBUTE 的值是 full,否则是 lite;

ConfigurationClassPostProcessor

#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)

#processConfigBeanDefinitions(BeanDefinitionRegistry registry)

#checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory)

// 获取Configuration注解的属性信息,配置类分两种,被@Configuration标记的配置类为full,其他的配置类为lite,full的配置类会生成代理对象		Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());		if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);		}		// 注意,并不是没有Configuration注解当前BeanDefinition就不是一个配置类		// 注意isConfigurationCandidate方法,会检查是否存在@Component, @ComponentScan,@Import,@ImportResource,@Bean注解		else if (config != null || isConfigurationCandidate(metadata)) {			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);		}		else {			// 如果没有Configuration注解信息,则返回false,表示不是一个配置类			return false;		}
复制代码


ConfigurationClassPostProcessor

#postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

#enhanceConfigurationClasses(ConfigurableListableBeanFactory 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);					}				}			}
// 被@Configuration标注的配置类就是full 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); } }
// 加了@Configuration注解的配置类的BeanDefinition if (configBeanDefs.isEmpty()) { // nothing to enhance -> return immediately return; }
// 生成AppConfig的代理对象 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())); } // 将增强类设置给beanDefinition,后续基于BeanDefinition产生的bean就是增加类的对象了 beanDef.setBeanClass(enhancedClass); // AppConfig--->Bean 代理对象 } } }
/** * Loads the specified class and generates a CGLIB subclass of it equipped with * container-aware callbacks capable of respecting scoping and other bean semantics. * @return the enhanced subclass */ 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; }
/** * Creates a new CGLIB {@link Enhancer} instance. */ 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; }
复制代码

cglib 代理主要是对我们的方法进行拦截增强,当我们执行 Ch04Config 中的方法的时候会去执行 cglib 代理类中的代理方法,主要就是 callBacks 中的方法

ConfigurationClassEnhancer

#BeanMethodInterceptor

#intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs

MethodProxy cglibMethodProxy)


/**		 * Check whether the given method corresponds to the container's currently invoked		 * factory method. Compares method name and parameter types only in order to work		 * around a potential problem with covariant return types (currently only known		 * to happen on Groovy classes).		 */		private boolean isCurrentlyInvokedFactoryMethod(Method method) {			Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();			return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&					Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));		}
复制代码

比如当前正在进行实例化的是 orderService()方法所对应的 bean,而在 orderService()方法中会调用 userService()方法,isCurrentlyInvokedFactoryMethod 这个时候下面这个判断等于 false,而等于 false,就不会真正去执行 userService()方法了,就会直接去 beanFactory 中去获取 bean。

总结

配置类加上 @Configuration 注解主要是给类加上了 cglib 代理。在执行配置类的方法时,会执行 cglib 代理类中的方法,其中有一个非常重要的判断,当我们的执行方法和我们的调用方法是同一个方法时,会执行父类的方法 new(cglib 代理基于继承);当执行方法和调用方法不是同一个方法时会调用 beanFactory.getBean 获取。

用户头像

魔曦

关注

我思故我在! 2018.01.15 加入

凡事有交代,件件有着落,事事有回音。

评论

发布
暂无评论
Spring源码-@Configuration注解解析