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 处理流程
- 请求映射与参数绑定
- 响应处理与视图解析
- 拦截器与过滤器