本文大纲
一、@Configuration和@Bean 给容器中注册组件
1.1.@Conditional 按照条件注册bean
二、@ComponentScan 自动扫描组件
三、@Import 给容器中快速导入一个组件
3.1.@Import 使用 class 导入
3.2.@Import 使用 ImportSelector 全类路径名
3.3.@Import 使用 ImportBeanDefinitionRegistrar 导入
(1)导入ImportSelect的实现类
(2)导入ImportBeandeinitionRegistrart
(3)ImportSelector、ImportBeanDefinitionRegistrar 区别
四、@EnableXXX与@Import的使用
4.1.导入普通类
4.2.导入配置类
4.3.导入ImportSelector
4.4.导入ImportBeanDefinitionRegistrar
Spring容器提供了多种方式来注册和管理组件(bean),下面我将详细介绍每种方式,并提供相应的代码示例。
这是最基础的方式,通过Java配置类来显式声明bean。
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserServiceImpl();
}
// 按照条件注册bean
@Bean
@Conditional(WindowsCondition.class)
public WindowsService windowsService() {
return new WindowsServiceImpl();
}
@Bean
@Conditional(LinuxCondition.class)
public LinuxService linuxService() {
return new LinuxServiceImpl();
}
}
@Conditional
注解允许根据特定条件决定是否注册bean。
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String osName = context.getEnvironment().getProperty("os.name");
return osName != null && osName.contains("Windows");
}
}
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String osName = context.getEnvironment().getProperty("os.name");
return osName != null && osName.contains("Linux");
}
}
通过组件扫描自动注册带有特定注解的类。
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}
被扫描的组件需要标注以下注解之一:
@Component
@Service
@Repository
@Controller
@Service
public class UserService {
// ...
}
@Repository
public class UserRepository {
// ...
}
@Import
注解可以快速导入一个或多个组件到Spring容器中。
@Configuration
@Import({UserService.class, OrderService.class})
public class AppConfig {
}
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
"com.example.service.UserService",
"com.example.service.OrderService"
};
}
}
@Configuration
@Import(MyImportSelector.class)
public class AppConfig {
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(UserService.class);
registry.registerBeanDefinition("userService", beanDefinition);
}
}
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class AppConfig {
}
特性 | ImportSelector | ImportBeanDefinitionRegistrar |
---|---|---|
使用方式 | 返回全类名数组 | 直接注册BeanDefinition |
灵活性 | 较低 | 更高,可以自定义bean定义 |
适用场景 | 简单导入多个类 | 需要更精细控制bean注册过程 |
实现复杂度 | 简单 | 较复杂 |
@EnableXXX
是Spring中常见的模式,通常基于@Import
实现。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(UserService.class)
public @interface EnableUserService {
}
@Configuration
@EnableUserService
public class AppConfig {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(UserConfig.class)
public @interface EnableUserConfig {
}
@Configuration
@EnableUserConfig
public class AppConfig {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(UserServiceSelector.class)
public @interface EnableUserServiceSelector {
}
@Configuration
@EnableUserServiceSelector
public class AppConfig {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(UserServiceRegistrar.class)
public @interface EnableUserServiceRegistrar {
}
@Configuration
@EnableUserServiceRegistrar
public class AppConfig {
}
注册方式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
@Bean | 显式声明第三方库的bean | 明确、直观 | 每个bean都需要单独声明 |
@ComponentScan | 自动注册自己编写的组件 | 自动化、方便 | 对第三方库不适用 |
@Import(Class) | 快速导入少量组件 | 简单直接 | 不适合大量组件 |
@Import(ImportSelector) | 根据条件动态选择导入的组件 | 灵活、可编程 | 实现稍复杂 |
@Import(ImportBeanDefinitionRegistrar) | 需要精细控制bean注册过程 | 最灵活、控制力最强 | 实现最复杂 |
@EnableXXX | 模块化配置、功能开关 | 语义明确、便于理解和使用 | 需要额外定义注解 |
在实际开发中,通常会组合使用这些方式:
@ComponentScan
管理自己的组件@Bean
注册第三方库的组件@Import
和@EnableXXX
实现模块化配置@Conditional
实现条件化配置这种组合方式既保持了灵活性,又能让代码结构清晰易懂。