0. 区别
直接讲一下区别:
在配置类的方法间调用时,如果类时@component标注的,每次调用获取的都是新的实体;而如果是@configuration标注的话,每次调用返回的是同一个实体Bean。其他方面都是相同,可以无差别使用(装配注入等)。看一下代码可能更加清晰一点:
哪是什么原因造成的呢?不卖关子,是因为@Configuration标注下的@Bean调用函数使用都是代理对象,获取的都是从IOC容器里获取的bean,因此都是同一个。而@Component标注下的@Bean下只是普通的函数方法调用。下面来看一下@configuration注册@Bean生成代理的过程。
1. @Bean的注册原理和过程
其实再看@Enable***注解的实现原理的时候有提到。是在ConfigurationClassPostProcessor类中解析的:
看一下注册的地方:
然后是生成代理的地方和生成代理拦截方法:
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<String, AbstractBeanDefinition>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) { // 只有带@Configuration注解的类才会被代理
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.isWarnEnabled() && beanFactory.containsSingleton(beanName)) {
logger.warn("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(); // 增强类,内有增强方法
…..(省略代码)
}
可以看到只有被@Configuration注解标识的类才会被增强。看一下增强类和设置的callBack。
BeanMethodInterceptor会根据方法名去IOC找到Bean并返回,具体代码就不贴了。
2. CGLIB内部方法调用:
看一下getObjectMethod字节码:
0 getstatic #2 <java/lang/System.out>
3 ldc #6 <getObjectMethod>
5 invokevirtual #4 <java/io/PrintStream.println>
8 aload_0
9 invokevirtual #7 <com/study/h/config/testConfigurationBean/enhancer/EnhancerModel.getObject>
12 areturn
可以看到getObjectMethod方法中的getObject()方法仍旧会通过代理对象调用getObject方法。