@Configuration 和 @Component 到底有啥区别?
Posted 李巴巴
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了@Configuration 和 @Component 到底有啥区别?相关的知识,希望对你有一定的参考价值。
Spring 注解中 @Configuration 和 @Component 的区别总结为一句话就是:
@Configuration 中所有带 @Bean 注解的方法都会被动态代理(cglib),因此调用该方法返回的都是同一个实例。而 @Conponent 修饰的类不会被代理,每实例化一次就会创建一个新的对象。
在 @Configuration 注解的源代码中,使用了 @Component 注解:
从定义来看, @Configuration
注解本质上还是 @Component
,因此 <context:component-scan/>
或者 @ComponentScan
都能处理 @Configuration
注解的类。
下面我们通过一个例子来说明上述情况:
// 使用@Configuration和@Bean注解创建Room实例和People实例,并注入进spring容器
@Configuration
public class RoomPeopleConfig
@Bean
public Room room()
Room room = new Room();
room.setId(1);
room.setName("房间");
room.setPeople(people());// 在创建Room实例时,再调用一次People()创建一个People实例
return room;
@Bean
public People people()
People people = new People();
people.setId(1);
people.setName("小明");
return people;
// 下面是测试代码
@SpringBootTest
@ContextConfiguration(classes = Application.class)
public class ConfigurationTests
@Autowired
private Room room;
@Autowired
private People people;
@Test
public void test()
System.out.println(people == room.getPeople() ? "是同一个实例" : "不是同一个实例");
输出结果:
如果将 @Configuration 换成 @Component ,则输出:
从上面的结果可以发现使用 @Configuration 时在 people 和 spring 容器之中的是同一个对象,而使用 @Component 时是不同的对象。这就是因为 @Configuration 使用了 cglib 动态代理,返回的是同一个实例对象。
虽然 @Component 注解也会当做配置类,但是并不会为其生成 CGLIB 代理 Class,所以在生成 room 对象时和生成 people 对象时调用 people( ) 方法执行了两次 new 操作,所以是不同的对象。当使用 @Configuration 注解时,生成当前对象的子类 Class,并对方法拦截,第二次调用 people()方法时直接从 BeanFactory 之中获取对象,所以得到的是同一个对象。
其具体底层的实现原理可以去看一看这篇文章:configuration和component区别底层实现
@Configuration和@Component
@Configuration注解和@Component注解的区别
首先看下@Configuration这个注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
可以发现其实@Configuration注解本质上也是一个@Component注解.
@Configuration
@ComponentScan("com.aiqiong.admin")
public class AppConfig
@Component
public class A
public static void main(String[] args)
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
A a = (A) annotationConfigApplicationContext.getBean("a");
System.out.println("a = " + a);
运行结果
可以看到A此时是一个普通的对象
把A类上的注解换成@Configuration
@Configuration
public class A
运行结果:
可以看到加上Configuration注解之后,A变成了一个cglib的代理对象.
@Configuration注解生成cglib代理的作用
场景:
@Component
public class AppConfig
@Bean
public B b()
B b = new B();
b.setA(a());
return b;
@Bean
public A a()
return new A();
public class A
public A()
System.out.println("A....");
通过控制台的输出信息可以看到,其实A对象被创建了两次
换成@Configuration注解呢
@Configuration
public class AppConfig
@Bean
public B b()
B b = new B();
b.setA(a());
return b;
@Bean
public A a()
return new A();
此时A只创建一次.
所以@Configuration注解就是为了避免用户显式地去调用@Bean方法,造成破坏了@Bean方法的bean的单例,而@Configuration注解能够做到这样避免用户显式调用@Bean方法的原理就是通过创建对应的配置类cglib代理对象去实现的.
下面我们重点看一下@Configuraton注解底层是怎么通过cglib代理去实现@Bean方法不被用户显式调用.
@Configuration动态代理的实现原理
ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,而BeanDefinitionRegistryPostProcessor接口又是继承于BeanFactoryPostProcessor接口,众所周知BeanFactoryPostProcessor接口接口是一个BeanFactory的后置处理接口,也就是在BeanFactory初始化之后会调用该接口的postProcessBeanFactory方法,也就是上面的方法,其中里面会再调用enhanceConfigurationClasses方法,该方法就是生成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));
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory)
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
// 遍历当前spring容器中所有的bd名称,这个for循环的目的就是找到所有全配置类(加了@Configuration注解的配置类)
for (String beanName : beanFactory.getBeanDefinitionNames())
// 根据bd名称获取到bd
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
// 获取到CONFIGURATION_CLASS_ATTRIBUTE对应的属性值
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);
// 条件成立:说明这个bd对应的是一个全配置类
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))
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
// 条件成立:没有需要被增强的配置类
if (configBeanDefs.isEmpty())
// nothing to enhance -> return immediately
return;
// 如果这个类是一个全配置类,那么就使用cglib进行代理增强,目的是防止@Bean方法的手动重复调用造成单例的破坏
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();
// 给这个全配置类创建一个cglib增强后的Class对象
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass)
// 把cglib代理的Class对象设置到这个全配置类的bd中
beanDef.setBeanClass(enhancedClass);
上面代码其实分成了两大步骤:
第一步骤是用来找到扩展属性CONFIGURATION_CLASS_ATTRIBUTE对应的值等于CONFIGURATION_CLASS_FULL的BeanDefinition.
这个属性是在执行postProcessBeanFactory方法之前,spring会解析所有的配置类,然后解析成对应的BeanDefinition.
而BeanDefinition又分成了两种,一种是全配置类,一种是半配置类.
- 全配置类:加了@Configuration注解并且proxyBeanMethods属性等于true
- 半配置类:加了@Component,@ComponentScan,@Import,@ImportResource这几个注解,或者@Configuration注解并且proxyBeanMethods属性等于false
对于全配置类CONFIGURATION_CLASS_ATTRIBUTE值等于CONFIGURATION_CLASS_FULL
对于半配置类CONFIGURATION_CLASS_ATTRIBUTE值等于CONFIGURATION_CLASS_LITE
第二步是把上面找出来的BeanDefinition创建cglib代理.使用的就是ConfigurationClassEnhancer这个工具类.
创建代理对象详解
概述
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader)
if (EnhancedConfiguration.class.isAssignableFrom(configClass))
return configClass;
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
return enhancedClass;
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;
上面都比较简单,就是常规的创建代理对象需要的参数
private Class<?> createClass(Enhancer enhancer)
Class<?> subclass = enhancer.createClass();
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
return subclass;
最后调用enhancer的createClass方法,就可以得到一个增强后的Class对象,返回出去之后把这个增强的Class对象设置到BeanDefinition中就可以了
创建代理对象参数详解
(1) CallbackFilter
enhancer.setCallbackFilter(CALLBACK_FILTER);
作用:当执行目标方法的时候会被accept方法拦截,在accept方法中会根据传入的目标方法去返回一个Callback增强.
通过这个CallbackFilter就可以实现调用不同的方法使用不同的增强Callback.spring这里传入的是ConditionalCallbackFilter,这个CallbackFilter在创建的时候需要传入一个Callback数组.
这个数组会被accept方法过滤,返回一个具体的Callback返回.
private static class ConditionalCallbackFilter implements CallbackFilter
private final Callback[] callbacks;
private final Class<?>[] callbackTypes;
public ConditionalCallbackFilter(Callback[] callbacks)
this.callbacks = callbacks;
this.callbackTypes = new Class<?>[callbacks.length];
for (int i = 0; i < callbacks.length; i++)
this.callbackTypes[i] = callbacks[i].getClass();
//
@Override
public int accept(Method method)
for (int i = 0; i < this.callbacks.length; i++)
Callback callback = this.callbacks[i];
if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method))
return i;
throw new IllegalStateException("No callback available for method " + method.getName());
public Class<?>[] getCallbackTypes()
return this.callbackTypes;
上面提到一个Callback数组,那么这个数组中的元素是啥呢?
private static final Callback[] CALLBACKS = new Callback[]
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
// 不进行增强
NoOp.INSTANCE
;
根据对应isMatch方法来看一下另外两个都是什么时候回被调用
1、BeanMethodInterceptor
public boolean isMatch(Method candidateMethod)
// 不是Object对象、不是setBeanFactory方法,并且方法上要有@Bean注解
return (candidateMethod.getDeclaringClass() != Object.class &&
!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
2、BeanFactoryAwareMethodInterceptor
public boolean isMatch(Method candidateMethod)
return isSetBeanFactory(candidateMethod);
public static boolean isSetBeanFactory(Method candidateMethod)
// 是setBeanFactory方法
return (candidateMethod.getName().equals("setBeanFactory") &&
candidateMethod.getParameterCount() == 1 &&
BeanFactory.class == candidateMethod.getParameterTypes()[0] &&
BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
那么最后我们返回的是哪一个增强器呢?
(2) EnhancedConfiguration
enhancer.setInterfaces(new Class<?>[] EnhancedConfiguration.class);
public interface EnhancedConfiguration extends BeanFactoryAware
所以自然地cglib代理对象中自然就会有这个BeanFactoryAware接口的setBeanFactory方法了.
所以可以回答上面一个问题了,创建代理的时候使用的增强器是BeanFactoryAwareMethodInterceptor
那么setBeanFactory方法是在什么时候被调用的呢?
这就要回顾之前讲到的Bean的生命周期了.如果一个bean实现了BeanFactoryAware接口的话,在实例化的过程中会回调BeanFactoryAware接口的setBeanFactory方法,然后在spring回调setBeanFactory方法的时候,BeanFactoryAwareMethodInterceptor这个Callback就能拦截到,执行intercept方法中的增强逻辑.
在intercept方法中做的其实就是把回调回来的BeanFactory对象赋值给属性名称为beanFactory的属性.
beanFactory这个属性是哪里的?在哪里设置的呢?
(3) beanFactory
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
setStrategy属性可以设置生成代理类的class文件字节码策略,这里具体设置的策略是BeanFactoryAwareGeneratorStrategy.
private static class BeanFactoryAwareGeneratorStrategy extends
ClassLoaderAwareGeneratorStrategy
public BeanFactoryAwareGeneratorStrategy(@Nullable ClassLoader classLoader)
super(classLoader);
@Override
protected ClassGenerator transform(ClassGenerator cg) throws Exception
ClassEmitterTransformer transformer = new ClassEmitterTransformer()
@Override
public void end_class()
// 给生成的代理类添加一个属性
// 属性名为 beanFactory
// 访问域是public
// 属性类型是BeanFactory
declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null);
super.end_class();
;
return new TransformingClassGenerator(cg, transformer);
private static final String BEAN_FACTORY_FIELD = "$$beanFactory";
总结下BeanFactoryAwareMethodInterceptor这个Callback的作用了,它的作用就是在spring回调BeanFactoryAware接口的seetBeanFactory方法的时候,把回调的BeanFactory对象赋值给
beanFactory属性,而这个属性的由来就是cglib在创建代理类的时候添加进来的.
下面讲讲另外一种增强器
BeanMethodInterceptor
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable
// 获取到$$beanFactory属性的值,也就是BeanFactory对象
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;
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName))
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof Sco以上是关于@Configuration 和 @Component 到底有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章
@configuration和@component之间的区别
Spring--@configuration 和 @Bean