@Configuration 与 @Component 的区别
目录
1、基本概念
1.1、@Component
1.2、@Configuration
2、代理类
2.1、保证Bean的单例性
2.2、提供完整的Bean生命周期
2.3、实现配置类的特殊行为
2.4、容器一致性保障
3、代理流程
3.1 代理流程分类
1、Full 模式(默认):
2、Lite 模式:
3.2、启动流程
3.3、底层机制
1、代理创建时机
2、代理类的工作方式
3.4、如何验证代理行为
4、性能考量
5、最佳实践建议
前言
在Spring框架中,有许多常用的注解来定义类,以下常用的是@configuration和@component。
Web 应用程序采用了经典的三层分层结构的话,在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释,而 @Component 就是对那些比较中立的类进行注释。
如下:
<context:component-scan base-package=”com.*”>上面的这个是引入Component组件的例子,其中base-package表示为需要扫描的所有子包。
从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
以下就是两者的区别:
1、基本概念
1.1、@Component
@Component 源码片段:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {String value() default "";
}
-
Spring 核心注解,最基础的组件标记注解。
-
作用:标识一个类作为 Spring 组件(Bean),会被组件扫描自动检测并注册到容器
-
衍生注解:
-
@Service
(服务层) -
@Repository
(持久层) -
@Controller
(控制层)
-
-
典型场景:
-
业务逻辑组件
-
工具类组件
-
非配置型的通用组件
-
1.2、@Configuration
@Configuration 源码片段:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {@AliasFor(annotation = Component.class)String value() default "";boolean proxyBeanMethods() default true;
}
-
Spring 专门用于配置的增强型注解。
-
本质:是一种特殊的
@Component
(包含@Component
元注解) -
核心能力:
-
定义 Bean 的装配规则
-
支持跨 Bean 引用
-
集成条件化配置(如
@Conditional
)
-
-
典型场景:
-
应用主配置类
-
数据库/缓存等基础设施配置
-
第三方库集成配置
-
通过对比可以发现:
@Configuration
是@Component
的派生注解(带有@Component
元注解)。
@Configuration
特有的proxyBeanMethods
属性控制代理行为。
2、代理类
Spring为@Configuration
类生成CGLIB代理主要出于以下几个关键原因:
2.1、保证Bean的单例性
当@Configuration
类中的@Bean
方法相互调用时:
@Configuration
public class AppConfig {@Beanpublic A a() {return new A(b()); // 调用b()方法}@Bean public B b() {return new B();}
}
无代理情况(如使用@Component
):
-
每次调用
b()
都会创建新的B实例 -
A和B无法保持单例性
有代理情况:
-
代理会拦截方法调用
-
确保相同的
b()
总是返回同一个实例 -
维护Spring容器的单例契约
2.2、提供完整的Bean生命周期
代理类能够:
-
处理
@PostConstruct
/@PreDestroy
等生命周期回调 -
正确处理
BeanFactoryAware
等接口 -
管理Bean的依赖关系
2.3、实现配置类的特殊行为
代理使得@Configuration
特有功能:
支持
@Bean
方法间的调用完美配合
@Import
/@ImportResource
原生支持
@Profile
等条件化配置与
@Enable*
系列注解深度集成
2.4、容器一致性保障
如下图所示:
代码示例:
1、原始类:
@Configuration
public class DataConfig {@Beanpublic DataSource dataSource() {return new HikariDataSource(config());}@Beanpublic HikariConfig config() {return new HikariConfig();}
}
2、代理类伪代码:
public class DataConfig$$EnhancerBySpringCGLIB extends DataConfig {private BeanFactory beanFactory;@Overridepublic DataSource dataSource() {// 1. 检查Bean是否已存在if (beanFactory.containsBean("dataSource")) {return (DataSource) beanFactory.getBean("dataSource");}// 2. 通过父类方法创建新实例return super.dataSource(); // 会触发config()的代理逻辑}@Overridepublic HikariConfig config() {// 同样的单例保证逻辑if (beanFactory.containsBean("config")) {return (HikariConfig) beanFactory.getBean("config");}return super.config();}
}
3、代理流程
3.1 代理流程分类
1、Full 模式(默认):
需要完整代理。
2、Lite 模式:
轻量级配置。
使用 @Component
+ @Bean
或 @Configuration(proxyBeanMethods = false)
3.2、启动流程
ConfigurationClassPostProcessor
开始处理调用
ConfigurationClassUtils.checkConfigurationClassCandidate()
- 识别真正的
@Configuration
类(非lite模式)
CGLIB代理生成过程:
// ConfigurationClassEnhancer 内部实现
public Enhancer newEnhancer(Class<?> configSuperClass, 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;
}
-
回调拦截器:
BeanMethodInterceptor
:处理 @Bean
方法调用。
BeanFactoryAwareMethodInterceptor
:处理依赖注入。
3.3、底层机制
1、代理创建时机
-
在容器启动时,Spring检测到
@Configuration
类 -
通过
ConfigurationClassPostProcessor
处理配置类 -
使用CGLIB生成子类代理
2、代理类的工作方式
生成的代理类会:
-
首次调用
@Bean
方法时,将返回的Bean注册到容器 -
后续调用相同方法时,直接从容器返回已注册的Bean
-
拦截所有
@Bean
方法调用,确保通过容器获取Bean
3.4、如何验证代理行为
示例代码
@Configuration
public class ConfigA {@Beanpublic B b() {return new B();}
}@Component
public class ConfigB {@Bean public C c() {return new C();}
}
测试验证
@SpringBootTest
public class ConfigTest {@Autowiredprivate ApplicationContext context;@Testpublic void testProxy() {// @Configuration类会被代理System.out.println(context.getBean(ConfigA.class).getClass()); // 输出: class com.example.ConfigA$$EnhancerBySpringCGLIB$$xxx// @Component类不会被代理System.out.println(context.getBean(ConfigB.class).getClass());// 输出: class com.example.ConfigB}
}
4、性能考量
4.1 启动时间影响(测试数据)
4.2 方法调用开销(纳秒级测量)
-
代理创建开销:启动时会增加少量开销
-
运行时性能:方法调用有轻微间接开销
-
优化方案:对于不需要方法间调用的简单配置,可使用
@Component
+@Bean
5、最佳实践建议
1、使用@Configuration
当:
-
需要定义多个相互依赖的Bean
-
需要完整的Spring配置功能
-
使用
@Enable*
系列注解
2、使用@Component
+@Bean
当:
-
只有简单的Bean定义
-
不需要方法间调用
-
想避免代理开销
3、禁用代理(特殊场景):
@Configuration(proxyBeanMethods = false)
public class MyConfig {// 此时行为类似于@Component
}
总结
Spring为
@Configuration
生成代理是为了保证Bean的单例性和正确的依赖管理,这是Spring容器可靠性的重要基础设计。