Skip to content

Spring Boot 常用注解与条件装配

一、Spring Boot 核心注解

@SpringBootApplication

java
@SpringBootApplication
// 等价于:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationImportFilter.class)
})
public @interface SpringBootApplication {
    @AliasFor(annotation = ComponentScan.class)
    String[] exclude() default {};
    
    @AliasFor(annotation = ComponentScan.class)
    String[] scanBasePackages() default {};
}

@SpringBootConfiguration

java
// 等价于 @Configuration,标记这是启动配置类
@SpringBootConfiguration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
}

二、条件装配注解

@Conditional

java
// 基础条件注解,实现 Condition 接口
@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

// 使用
@Bean
@Conditional(MyCondition.class)
public MyBean myBean() {
    return new MyBean();
}

// 自定义条件
public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().containsProperty("my.feature.enabled");
    }
}

@ConditionalOnClass / @ConditionalOnMissingClass

java
// 类路径存在某个类时才加载
@Configuration
@ConditionalOnClass(name = "com.fasterxml.jackson.databind.ObjectMapper")
public class JacksonAutoConfiguration { }

// 类路径不存在某个类时才加载
@Configuration
@ConditionalOnMissingClass("com.fasterxml.jackson.xml.XMLMapper")
public class JsonOnlyAutoConfiguration { }

@ConditionalOnBean / @ConditionalOnMissingBean

java
// 存在某个 Bean 时才加载
@Configuration
@ConditionalOnBean(DataSource.class)
public class TransactionAutoConfiguration {
    @Bean
    public PlatformTransactionManager transactionManager(DataSource ds) {
        return new DataSourceTransactionManager(ds);
    }
}

// 不存在某个 Bean 时才加载
@Configuration
@ConditionalOnMissingBean(RedisTemplate.class)
public class RedisAutoConfiguration {
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        return new RedisTemplate<>();
    }
}

@ConditionalOnProperty

java
@Configuration
@ConditionalOnProperty(
    prefix = "spring.cache",
    name = "type",
    havingValue = "redis",
    matchIfMissing = false  // 默认为 false,不配置则不生效
)
public class RedisCacheAutoConfiguration { }

// 使用 name数组(满足任一即生效)
@ConditionalOnProperty(
    name = {"spring.cache.type", "my.cache.enabled"},
    havingValue = "true"
)

@ConditionalOnExpression

java
// SpEL 表达式条件
@Configuration
@ConditionalOnExpression("${my.enabled:true} && ${my.cache.enabled:false}")
public class MyAutoConfiguration { }

// 或更复杂的条件
@ConditionalOnExpression("#{ '${server.port}'.startsWith('80') }")

@ConditionalOnWebApplication / @ConditionalOnNotWebApplication

java
// Web 应用才加载
@Configuration
@ConditionalOnWebApplication(type = Type.REACTIVE)
public class WebFluxAutoConfiguration { }

// 非 Web 应用才加载
@Configuration
@ConditionalOnNotWebApplication
public class StandaloneAutoConfiguration { }

条件组合

java
@Configuration
@AutoConfiguration
// 多个条件必须同时满足
@ConditionalOnClass({RedisOperations.class, CacheManager.class})
@ConditionalOnBean(CacheManager.class)
@ConditionalOnProperty(
    prefix = "spring.cache",
    name = "type",
    havingValue = "redis"
)
public class RedisCacheAutoConfiguration { }

三、自动配置顺序

@AutoConfigureBefore / @AutoConfigureAfter

java
// 在某个自动配置类之前加载
@AutoConfiguration(before = DataSourceAutoConfiguration.class)
public class MyDataSourceAutoConfiguration { }

// 在某个自动配置类之后加载
@AutoConfiguration(after = JdbcTemplateAutoConfiguration.class)
public class MyTransactionAutoConfiguration { }

// 多个
@AutoConfiguration(after = {A.class, B.class}, before = C.class)

@AutoConfigureOrder

java
// 指定加载顺序(数字越小越先加载)
@AutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class FirstAutoConfiguration { }

@AutoConfiguration
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public class LastAutoConfiguration { }

四、@Configuration vs @Bean

@Configuration

java
// 完整配置类
@Configuration
public class AppConfig {
    
    @Bean
    public UserService userService() {
        return new UserService();
    }
    
    // 支持内部调用(代理)
    @Bean
    public OrderService orderService() {
        // 这里调用 userService() 会被代理,返回同一个 Bean
        return new OrderService(userService());
    }
}

@Configuration vs @Component

对比@Configuration@Component
本质特殊 @Component普通组件
内部 @Bean 调用代理保证单例每次调用创建新实例
性能略低(有代理开销)稍快

@Component 的陷阱

java
@Component
public class AppConfig {
    
    @Bean
    public UserService userService() {
        return new UserService();
    }
    
    @Bean
    public OrderService orderService() {
        // 危险!这里直接调用不会经过 Spring 代理
        return new OrderService(userService());  // 会创建新的 UserService!
    }
}

// 正确做法
@Configuration
public class AppConfig {
    
    @Bean
    public UserService userService() {
        return new UserService();
    }
    
    @Bean
    public OrderService orderService(UserService userService) {
        // 通过参数注入,受 Spring 管理
        return new OrderService(userService);
    }
}

五、@Import 与 @ImportResource

@Import

java
// 导入普通配置类
@Configuration
@Import({ConfigA.class, ConfigB.class})
public class MainConfig { }

// 导入自动配置类
@SpringBootApplication
@Import(AutoConfigurationA.class)
public class Application { }

// 导入普通类(会注册为 Bean)
@Import(UserService.class)
public class AppConfig { }

@ImportResource

java
// 导入 XML 配置
@Configuration
@ImportResource("classpath:beans.xml")
public class XmlConfig { }

// 导入 XML 并指定路径
@Configuration
@ImportResource({"classpath:beans.xml", "classpath:another-beans.xml"})
public class XmlConfig { }

XML 配置示例

xml
<!-- beans.xml -->
<beans xmlns="http://www.springframework.org/schema/beans">
    <bean id="userService" class="com.example.UserService">
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

六、Spring Boot 测试注解

@SpringBootTest

java
@SpringBootTest
class UserServiceTest {
    
    @Autowired
    private UserService userService;
    
    @Test
    void testSave() {
        userService.save(new User("张三"));
    }
}

Web 测试

java
@SpringBootTest
@AutoConfigureMockMvc  // 自动配置 MockMvc
class UserControllerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    void testGetUser() throws Exception {
        mockMvc.perform(get("/users/1"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("张三"));
    }
}

@TestConfiguration

java
@SpringBootTest
class UserServiceTest {
    
    // 测试专用配置,不会被扫描到
    @TestConfiguration
    static class TestConfig {
        @Bean
        public MockBean mockService() {
            return Mockito.mock(MockBean.class);
        }
    }
}

@CsvSource / @EnumSource

java
@Test
@CsvSource({
    "2024-01-01, 2024-01-01",
    "2024-01-02, 2024-01-02"
})
void testDate(String input, String expected) { }

@Test
void testEnum(Status status) {
    // ...
}

七、Spring Boot 启动相关注解

@Lazy

java
// Bean 懒加载
@Service
@Lazy  // 第一次使用时才创建
public class LazyService {
    public LazyService() {
        System.out.println("LazyService created");
    }
}

// @Bean 懒加载
@Bean
@Lazy
public UserService userService() {
    return new UserService();
}

@Primary

java
// 优先使用该 Bean
@Service
@Primary
public class PrimaryUserService extends UserService { }

// 或在 @Bean 中
@Bean
@Primary
public UserService userService() {
    return new PrimaryUserService();
}

@Profile

java
// 环境切换
@Service
@Profile("dev")
public class DevUserService extends UserService { }

@Service
@Profile("prod")
public class ProdUserService extends UserService { }

// 激活环境
// 1. @ActiveProfile
@SpringBootTest
@ActiveProfiles("dev")
class Test { }

// 2. 配置
spring:
  profiles:
    active: dev

@DependsOn

java
// 确保依赖的 Bean 先创建
@Service
@DependsOn({"dataSource", "transactionManager"})
public class UserService { }

// @Bean 方式
@Bean
@DependsOn("dataSource")
public UserService userService() {
    return new UserService();
}

八、组件扫描注解

@ComponentScan

java
@SpringBootApplication
@ComponentScan(
    basePackages = {"com.example", "com.other"},  // 扫描多个包
    excludeFilters = {
        @ComponentScan.Filter(
            type = FilterType.REGEX,
            pattern = "com\\.example\\.config\\..*"
        )
    }
)
public class Application { }

@Filter 类型

java
type = FilterType.ANNOTATION      // 按注解过滤
type = FilterType.ASSIGNABLE_TYPE // 按类型过滤
type = FilterType.REGEX           // 正则表达式
type = FilterType.CUSTOM          // 自定义过滤

@Indexed(Spring 5.0+)

java
// 启用组件索引,加速启动
@Indexed
public @interface Service { }

// 编译时生成索引文件
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-indexer</artifactId>
    <optional>true</optional>
</dependency>

九、Spring Boot Aware 接口

常用 Aware

java
@Service
public class UserService implements BeanNameAware,
                                    BeanFactoryAware,
                                    ApplicationContextAware,
                                    EnvironmentAware,
                                    ResourceLoaderAware {
    
    private String beanName;
    private BeanFactory beanFactory;
    private ApplicationContext applicationContext;
    private Environment environment;
    private ResourceLoader resourceLoader;
    
    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }
    
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
    
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}

通过 ApplicationContextAware 获取

java
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
    
    private static ApplicationContext ctx;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        ctx = applicationContext;
    }
    
    public static <T> T getBean(Class<T> clazz) {
        return ctx.getBean(clazz);
    }
}

十、面试高频问题

Q1: @ConditionalOnMissingBean 和 @ConditionalOnBean 区别?

  • @ConditionalOnMissingBean:容器中不存在该 Bean 时才生效
  • @ConditionalOnBean:容器中存在该 Bean 时才生效

Q2: @Configuration 为什么比 @Component 更好?

  • @Configuration 有 CGLIB 代理,保证内部 @Bean 调用也是单例
  • @Component 内部调用会每次创建新实例

Q3: @Profile 和 @ConditionalOnProperty 区别?

  • @Profile:环境切换(JVM 参数、配置文件)
  • @ConditionalOnProperty:配置属性值判断

Q4: @SpringBootApplication 做了什么?

  • @SpringBootConfiguration:标记为配置类
  • @EnableAutoConfiguration:启用自动配置
  • @ComponentScan:扫描组件

Q5: 如何让配置类不参与组件扫描?

java
// 方式1:excludeFilters
@ComponentScan(excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MyConfig.class))

// 方式2:不标注为 Spring 组件
@Configuration
public class MyConfig { }  // 用 @Import 手动导入

十一、下一章预告

下一章我们将学习 Spring MVC 开发

  • SpringMVC 处理流程
  • 请求映射与参数绑定
  • 响应处理与视图解析
  • 拦截器与过滤器

基于 MIT 许可发布