前言
我们都知道@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了一个对象,然后返回。