我之前已经介绍了ConfigurationClassPostProcessor 对另外几个注解的处理流程:
- springboot启动bean加载处理器ConfigurationClassPostProcessor 一(@ComponentScan注解)
- springboot启动bean加载处理器ConfigurationClassPostProcessor 二(@PropertySource注解)
- springboot启动bean加载处理器ConfigurationClassPostProcessor 三(@Import注解)
这期再来看下另外一个重要的注解@bean, 我们使用springboot后,去掉了xml的繁琐配置,日常开发中都是通过这个注解向spring容器中注入bean,当然我们要配合使用**@Configuration**使用,例如
@Configuration
//@Component 也可以用这个注解
public class TestBean {
@Bean(name = "bean1")
public ClassBean bean1(){
return new ClassBean(bean2());
}
@Bean(name = "bean2")
public ClassReBean bean2(){
return new ClassReBean();
}
}
就向代码注释中描述的,我们也可以配合**@Component**注解使用,那么这两个有什么区别呢?
在spring容器中bean可以有两种方式的定义:
现在我们一起来下spring是怎么区分这两种方式,及区分这两种方式的目的是啥!!
现在先回到我们的处理类 ConfigurationClassPostProcessor,这个类中对@bean 的解析很简单,就是获取sourceclass中带有@bean注解的方法(包括processInterfaces(configClass, sourceClass)方法,解析父类的@bean注解),然后放到sourceclass中的beanMethods属性中。
在ConfigurationClassPostProcessor 中parse方法执行完成后,会返回所有满足条件的class集合,这个时候叫ConfigurationClass。这个时候对当前的集合进行循环做一个重要的事情,找出带有@Configuration 注解的类标记属性为full,其它的为lite。
这时候ConfigurationClassPostProcessor 一个使命已经完成了(加载bean定义到spring容器中),他还有另外一个重要的重用,我们看下ConfigurationClassPostProcessor类关系
它实现了BeanFactoryPostProcessor,它里面有个重要的接口定义
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean definition registry after its
* standard initialization. All regular bean definitions will have been loaded,
* but no beans will have been instantiated yet. This allows for adding further
* bean definitions before the next post-processing phase kicks in.
* @param registry the bean definition registry used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
ConfigurationClassPostProcessor 对这个接口的实现是:
@Override
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);
}
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
这里有个重要的方法:enhanceConfigurationClasses(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);
}
}
}
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()));
}
//替换class类型
beanDef.setBeanClass(enhancedClass);
}
}
}
方法是重要,拆分出来就是两个循环:
- 找出所有类型是full的class
- 对full类型的class进行CGLIB代理并替换
所以spring容器中如果有带有@Configuration注解的类都会被代理,类型是full模式!,@Bean方法不能被private/final等进行修饰
看下ConfigurationClassPostProcessor 整体流程:
对替换的代理类,spring加两个重要的方法拦截器
- BeanMethodInterceptor,这个就是对@bean注解的方法进行拦截处理
- BeanFactoryAwareMethodInterceptor
重点来观察下BeanMethodInterceptor的拦截都干了啥!!
private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {
/**
* Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
* existence of this bean object.
* @throws Throwable as a catch-all for any exception that may be thrown when invoking the
* super implementation of the proxied method i.e., the actual {@code @Bean} method
*/
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// Determine whether this bean is a scoped-proxy
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
// To handle the case of an inter-bean method reference, we must explicitly check the
// container for already cached instances.
// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
// proxy that intercepts calls to getObject() and returns any cached bean instance.
// This ensures that the semantics of calling a FactoryBean from within @Bean methods
// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
}
else {
// It is a candidate FactoryBean - go ahead with enhancement
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// The factory is calling the bean method in order to instantiate and register the bean
// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
// create the bean instance.
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);
}
再结合我们当前的例子:
@Configuration
//@Component
public class TestBean {
@Bean(name = "bean1")
public ClassBean bean1(){
return new ClassBean(bean2());
}
@Bean(name = "bean2")
public ClassReBean bean2(){
return new ClassReBean();
}
}
当方法bean1()执行后,这个时候isCurrentlyInvokedFactoryMethod(beanMethod)方法 (true),进入真正要执行的new ClassBean(bean2()),这个时候会执行bean2(),又会被当前方法拦截器拦截,这个时候isCurrentlyInvokedFactoryMethod(beanMethod)(false),这个时候就会走最后的resolveBeanReference方法,这个方法会创建bean(实际还走bean创建流程,进入当前的方法拦截器,isCurrentlyInvokedFactoryMethod==true,返回真正的bean2()对应的ClassReBean ),所以ClassReBean 不会创建两次,这个就叫full模式;
这个创建过程有点绕,其实就是为了包装只创建一次!
当然如果使用@Component,也想要只创建一次的办法是加@Autowired注解
@Component
public class TestBean {
@Autowired
private ClassReBean classReBean;
@Bean(name = "bean1")
public ClassBean bean1(){
return new ClassBean(classReBean);
}
@Bean(name = "bean2")
public ClassReBean bean2(){
return new ClassReBean();
}
}