@Component和@Configuration原来有这个区别,长见识了

前言

我们都知道@Component和@Configuration都可以用来做配置类的注解。不过有什么区别不是很清楚,今天看到一篇文章说明了区别,所以我做了下测试


正文

先定义两个bean类

@Data
public class Teacher {

    private String id;

    private String age;

    private Person person;
}

@Data
public class Person {

    private String id;

    private String name;

}

在一个配置类进行注册bean

@Component
public class MyTestConfig {


    @Bean
    public Person person(){
        Person person=new Person();
        person.setId("1");
        person.setName("PERSON");
        return person;
    }

    @Bean
    public Teacher teacher(){
        Teacher teacher=new Teacher();
        teacher.setId("1");
        teacher.setAge("20");
        teacher.setPerson(person());
        return teacher;
    }
}

创建一个测试类,进行测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTest {

    @Autowired
    private Person person;

    @Autowired
    private Teacher teacher;

    @Test
    public void testMethod(){
        boolean result = teacher.getPerson() == person;
        System.out.println(result?"同一个对象":"不同一个对象");
    }
}

结果是:当我使用@Configuration做配置注解时,输出的是同一个对象,而使用@Component做配置注解时,输出为不同一个对象。

原因:当我使用@Configuration做配置注解,Spring会启用{@link ConfigurationClassPostProcessor}和其他注释相关的功能,处理{@code @Configuration}类的后置处理器。

以下就是ConfigurationClassPostProcessor的 postProcess,它会帮我们运行配置类,并帮我们用cglib增强的子类来替换原来注册的bean

/**
 * Prepare the Configuration classes for servicing bean requests at runtime准备在运行时服务bean请求的配置类
 * by replacing them with CGLIB-enhanced subclasses.用cglib增强的子类替换它们。
 */
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));
	}


enhanceConfigurationClasses方法源码

/ * *
*后处理一个查找配置类BeanDefinitions的BeanFactory;
*然后任何候选项都被{@link ConfigurationClassEnhancer}增强。
*候选状态由BeanDefinition属性元数据决定。
* /
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()));
				}
				beanDef.setBeanClass(enhancedClass);
			}
		}
	}

主要代理代码是下面这些,spring会遍历配置类中bean(for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet())),进行以下操作(即进行cglib代理)

// If a @Configuration class gets proxied, always proxy the target class,如果@Configuration类被代理,始终代理目标类,将属性设置
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class设置用户指定bean类的增强子类
Class<?> configClass = beanDef.getBeanClass();
//生成代理class
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,变成代理的class
beanDef.setBeanClass(enhancedClass);
}

结论:

当我们使用@Configuration作为配置时,Spring会为我们的bean生成代理对象,我们这个bean中使用的bean对象其实是已经被代理的对象,当我们调用Person方法时,是直接从BeanFactory中获取到的代理对象,而不是新new出来的对象。而使用Component注解,不会生成代理对象,所以我们调用Person方法,相当于是新new了一个对象,然后返回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值