文章目录
1. 条件注解基础概念
1.1 什么是条件注解
条件注解是SpringBoot提供的一套强大的机制,允许我们根据特定条件来决定是否创建Bean、加载配置类或执行某些操作。它是SpringBoot自动配置的核心基础,让应用能够根据环境、依赖、配置等因素智能地决定加载哪些组件。
核心特点:
- 智能化:根据条件自动决定是否加载
- 灵活性:支持多种判断条件
- 可扩展:可以自定义条件逻辑
- 高性能:在应用启动时进行条件判断,运行时不影响性能
1.2 为什么需要条件注解
在传统的Spring应用中,我们需要手动配置大量的Bean和组件。SpringBoot通过条件注解实现了"约定优于配置"的理念:
// 传统Spring配置(繁琐)
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
// 需要手动判断环境、配置等
if (hasH2Driver() && isTestEnvironment()) {
return createH2DataSource();
} else if (hasMySQLDriver() && hasValidMySQLConfig()) {
return createMySQLDataSource();
}
throw new IllegalStateException("No valid datasource configuration found");
}
private boolean hasH2Driver() {
try {
Class.forName("org.h2.Driver");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
// 更多复杂的判断逻辑...
}
// SpringBoot条件注解方式(简洁)
@Configuration
public class DataSourceAutoConfig {
@Bean
@ConditionalOnClass(name = "org.h2.Driver")
@ConditionalOnProperty(name = "spring.datasource.url", havingValue = "jdbc:h2:")
public DataSource h2DataSource() {
return new H2DataSource();
}
@Bean
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
@ConditionalOnMissingBean(DataSource.class)
public DataSource mysqlDataSource() {
return new MySQLDataSource();
}
}
1.3 条件注解的工作原理
条件注解基于Spring的@Conditional
注解实现,工作流程如下:
// 基础Conditional注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
// Condition接口
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
// SpringBoot条件注解的实现示例
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}
执行时机:
- Spring容器启动时
- 解析配置类时
- 创建Bean定义时
- 在Bean实例化之前进行条件判断
判断顺序:
- 首先处理
@ConditionalOnClass
和@ConditionalOnMissingClass
- 然后处理
@ConditionalOnBean
和@ConditionalOnMissingBean
- 最后处理其他条件注解
2. SpringBoot自动配置原理
2.1 自动配置的核心机制
SpringBoot的自动配置通过@EnableAutoConfiguration
注解启动,它会扫描META-INF/spring.factories
文件中定义的自动配置类:
// 主启动类
@SpringBootApplication // 包含了@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// @SpringBootApplication的组成
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration // 关键注解
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {
// ...
}
2.2 spring.factories文件示例
# META-INF/spring.factories 文件内容示例
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration
2.3 典型的自动配置类结构
// Redis自动配置类示例
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class) // 必须有Redis相关类
@EnableConfigurationProperties(RedisProperties.class) // 启用配置属性
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate") // 没有自定义时才创建
@ConditionalOnSingleCandidate(RedisConnectionFactory.class) // 有且仅有一个连接工厂
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean // 没有自定义时才创建
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
// Redis配置属性类
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String password;
private int port = 6379;
private boolean ssl;
private Duration timeout;
private Duration connectTimeout;
private String clientName;
private ClientType clientType;
// 连接池配置
private Pool pool;
// Sentinel配置
private Sentinel sentinel;
// Cluster配置
private Cluster cluster;
// Jedis配置
private Jedis jedis = new Jedis();
// Lettuce配置
private Lettuce lettuce = new Lettuce();
// getter和setter方法...
}
3. 基础条件注解详解
3.1 @Conditional - 所有条件注解的基础
@Conditional
是所有条件注解的基础,它需要指定一个实现了Condition
接口的类:
// 自定义条件类
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取环境信息
Environment environment = context.getEnvironment();
String osName = environment.getProperty("os.name");
// 判断是否为Linux系统
return osName != null && osName.toLowerCase().contains("linux");
}
}
// 使用自定义条件
@Configuration
public class SystemConfig {
@Bean
@Conditional(LinuxCondition.class)
public FileSystemService linuxFileSystemService() {
return new LinuxFileSystemService();
}
@Bean
@Conditional(WindowsCondition.class)
public FileSystemService windowsFileSystemService() {
return new WindowsFileSystemService();
}
}
// Windows条件判断
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String osName = context.getEnvironment().getProperty("os.name");
return osName != null && osName.toLowerCase().contains("windows");
}
}
3.2 ConditionContext详解
ConditionContext
提供了丰富的上下文信息:
public class DetailedConditionExample implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 1. 获取Bean定义注册器
BeanDefinitionRegistry registry = context.getRegistry();
System.out.println("已注册的Bean数量: " + registry.getBeanDefinitionCount());
// 2. 获取Bean工厂
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beanFactory != null) {
System.out.println("Bean工厂类型: " + beanFactory.getClass().getSimpleName());
}
// 3. 获取环境信息
Environment environment = context.getEnvironment();
// 获取激活的Profile
String[] activeProfiles = environment.getActiveProfiles();
System.out.println("激活的Profile: " + Arrays.toString(activeProfiles));
// 获取系统属性
String javaVersion = environment.getProperty("java.version");
String osName = environment.getProperty("os.name");
System.out.println("Java版本: " + javaVersion);
System.out.println("操作系统: " + osName);
// 获取应用配置
String appName = environment.getProperty("spring.application.name");
String serverPort = environment.getProperty("server.port", "8080");
System.out.println("应用名称: " + appName);
System.out.println("服务端口: " + serverPort);
// 4. 获取资源加载器
ResourceLoader resourceLoader = context.getResourceLoader();
// 检查资源是否存在
Resource resource = resourceLoader.getResource("classpath:custom-config.properties");
boolean resourceExists = resource.exists();
System.out.println("自定义配置文件存在: " + resourceExists);
// 5. 获取类加载器
ClassLoader classLoader = context.getClassLoader();
// 检查特定类是否存在
try {
Class<?> clazz = classLoader.loadClass("com.example.OptionalComponent");
System.out.println("可选组件存在: " + clazz.getName());
return true;
} catch (ClassNotFoundException e) {
System.out.println("可选组件不存在");
return false;
}
}
}
3.3 AnnotatedTypeMetadata详解
AnnotatedTypeMetadata
提供了注解元数据信息:
public class AnnotationMetadataCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 1. 检查是否包含特定注解
boolean hasService = metadata.isAnnotated(Service.class.getName());
boolean hasComponent = metadata.isAnnotated(Component.class.getName());
System.out.println("包含@Service注解: " + hasService);
System.out.println("包含@Component注解: " + hasComponent);
// 2. 获取注解属性
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnProperty.class.getName());
if (attributes != null) {
String prefix = (String) attributes.get("prefix");
String name = (String) attributes.get("name");
String havingValue = (String) attributes.get("havingValue");
System.out.println("属性前缀: " + prefix);
System.out.println("属性名称: " + name);
System.out.println("期望值: " + havingValue);
// 验证属性值
String fullPropertyName = prefix.isEmpty() ? name : prefix + "." + name;
String actualValue = context.getEnvironment().getProperty(fullPropertyName);
return havingValue.equals(actualValue);
}
// 3. 获取所有注解
Set<String> annotationTypes = metadata.getAnnotationTypes();
System.out.println("所有注解: " + annotationTypes);
// 4. 检查元注解
boolean hasConditional = metadata.isAnnotated(Conditional.class.getName());
System.out.println("包含@Conditional注解: " + hasConditional);
return true;
}
}
// 使用示例
@Component
@ConditionalOnProperty(prefix = "app", name = "feature.enabled", havingValue = "true")
@Conditional(AnnotationMetadataCondition.class)
public class FeatureComponent {
@PostConstruct
public void init() {
System.out.println("功能组件已初始化");
}
}
4. 类存在性条件注解
4.1 @ConditionalOnClass - 当类存在时
当指定的类在类路径中存在时,条件才成立:
// 基础用法
@Configuration
public class DatabaseConfig {
// 当H2数据库驱动存在时,创建H2数据源
@Bean
@ConditionalOnClass(name = "org.h2.Driver")
public DataSource h2DataSource() {
return DataSourceBuilder.create()
.driverClassName("org.h2.Driver")
.url("jdbc:h2:mem:testdb")
.username("sa")
.password("")
.build();
}
// 当MySQL驱动存在时,创建MySQL数据源
@Bean
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
public DataSource mysqlDataSource(@Value("${spring.datasource.url}") String url,
@Value("${spring.datasource.username}") String username,
@Value("${spring.datasource.password}") String password) {
return DataSourceBuilder.create()
.driverClassName("com.mysql.cj.jdbc.Driver")
.url(url)
.username(username)
.password(password)
.build();
}
}
// 使用Class对象(推荐方式,编译时检查)
@Configuration
public class CacheConfig {
@Bean
@ConditionalOnClass(RedisTemplate.class) // 类型安全的方式
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.build();
}
@Bean
@ConditionalOnClass(name = "com.github.benmanes.caffeine.cache.Caffeine")
@ConditionalOnMissingBean(CacheManager.class) // 当没有其他缓存管理器时
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(10)));
return cacheManager;
}
}
4.2 @ConditionalOnMissingClass - 当类不存在时
当指定的类在类路径中不存在时,条件才成立:
@Configuration
public class FallbackConfig {
// 当Redis不可用时,使用内存缓存
@Bean
@ConditionalOnMissingClass("org.springframework.data.redis.core.RedisTemplate")
public CacheManager simpleCacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(
new ConcurrentMapCache("users"),
new ConcurrentMapCache("products"),
new ConcurrentMapCache("orders")
));
return cacheManager;
}
// 当没有专业的消息队列时,使用简单的内存队列
@Bean
@ConditionalOnMissingClass({
"org.springframework.amqp.rabbit.core.RabbitTemplate",
"org.apache.kafka.clients.producer.KafkaProducer"
})
public MessageQueue simpleMessageQueue() {
return new SimpleInMemoryMessageQueue();
}
}
// 简单的内存消息队列实现
@Component
public class SimpleInMemoryMessageQueue implements MessageQueue {
private final Map<String, BlockingQueue<Object>> queues = new ConcurrentHashMap<>();
private final ExecutorService executor = Executors.newCachedThreadPool();
@Override
public void send(String topic, Object message) {
queues.computeIfAbsent(topic, k -> new LinkedBlockingQueue<>()).offer(message);
System.out.println("消息已发送到内存队列 [" + topic + "]: " + message);
}
@Override
public void subscribe(String topic, MessageHandler handler) {
BlockingQueue<Object> queue = queues.computeIfAbsent(topic, k -> new LinkedBlockingQueue<>());
executor.submit(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
Object message = queue.take();
handler.handle(message);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
}
}
4.3 多类条件组合
@Configuration
public class AdvancedClassConditionConfig {
// 同时需要多个类存在
@Bean
@ConditionalOnClass({
RedisTemplate.class,
Jackson2JsonRedisSerializer.class,
ObjectMapper.class
})
public RedisTemplate<String, Object> jsonRedisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 使用Jackson序列化
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LazyResolutionKey.NON_FINAL, ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
// 复杂的类存在性判断
@Bean
@ConditionalOnClass(name = {
"org.springframework.security.web.SecurityFilterChain",
"org.springframework.security.config.annotation.web.builders.HttpSecurity"
})
@ConditionalOnMissingClass("org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider")
public SecurityFilterChain basicSecurityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.build();
}
}
5. Bean存在性条件注解
5.1 @ConditionalOnBean - 当Bean存在时
当容器中存在指定的Bean时,条件才成立:
@Configuration
public class BeanConditionConfig {
// 基础Bean
@Bean
@ConditionalOnProperty(name = "app.datasource.enabled", havingValue = "true")
public DataSource dataSource() {
return DataSourceBuilder.create()
.url("jdbc:h2:mem:testdb")
.username("sa")
.password("")
.build();
}
// 当DataSource存在时,创建JdbcTemplate
@Bean
@ConditionalOnBean(DataSource.class)
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
// 当JdbcTemplate存在时,创建用户服务
@Bean
@ConditionalOnBean(JdbcTemplate.class)
public UserService jdbcUserService(JdbcTemplate jdbcTemplate) {
return new JdbcUserService(jdbcTemplate);
}
// 根据Bean名称判断
@Bean
@ConditionalOnBean(name = "customUserService")
public UserController userController(@Qualifier("customUserService") UserService userService) {
return new UserController(userService);
}
// 多个Bean条件(需要所有Bean都存在)
@Bean
@ConditionalOnBean({DataSource.class, RedisTemplate.class})
public CacheableUserService cacheableUserService(DataSource dataSource, RedisTemplate<String, Object> redisTemplate) {
return new CacheableUserService(dataSource, redisTemplate);
}
}
// 用户服务实现
@Service("jdbcUserService")
public class JdbcUserService implements UserService {
private final JdbcTemplate jdbcTemplate;
public JdbcUserService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public List<User> findAll() {
return jdbcTemplate.query("SELECT * FROM users", (rs, rowNum) -> {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
return user;
});
}
@Override
public User findById(Long id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new Object[]{id},
(rs, rowNum) -> {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
return user;
}
);
}
@Override
public User save(User user) {
if (user.getId() == null) {
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(
"INSERT INTO users (name, email) VALUES (?, ?)",
Statement.RETURN_GENERATED_KEYS
);
ps.setString(1, user.getName());
ps.setString(2, user.getEmail());
return ps;
}, keyHolder);
user.setId(keyHolder.getKey().longValue());
} else {
jdbcTemplate.update(
"UPDATE users SET name = ?, email = ? WHERE id = ?",
user.getName(), user.getEmail(), user.getId()
);
}
return user;
}
}
5.2 @ConditionalOnMissingBean - 当Bean不存在时
当容器中不存在指定的Bean时,条件才成立。这是实现默认配置的常用方式:
@Configuration
public class DefaultBeanConfig {
// 默认的用户服务(当没有其他实现时)
@Bean
@ConditionalOnMissingBean(UserService.class)
public UserService defaultUserService() {
return new DefaultUserService();
}
// 默认的缓存管理器
@Bean
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager defaultCacheManager() {
return new ConcurrentMapCacheManager("users", "products", "orders");
}
// 根据名称判断Bean是否缺失
@Bean("primaryDataSource")
@ConditionalOnMissingBean(name = "primaryDataSource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create()
.url("jdbc:h2:mem:primary")
.username("sa")
.password("")
.build();
}
}
// 默认用户服务实现
@Service
public class DefaultUserService implements UserService {
private final Map<Long, User> users = new ConcurrentHashMap<>();
private final AtomicLong idGenerator = new AtomicLong(1);
@PostConstruct
public void init() {
// 初始化一些测试数据
save(new User(null, "张三", "zhangsan@example.com"));
save(new User(null, "李四", "lisi@example.com"));
save(new User(null, "王五", "wangwu@example.com"));
System.out.println("默认用户服务已初始化,包含测试数据");
}
@Override
public List<User> findAll() {
return new ArrayList<>(users.values());
}
@Override
public User findById(Long id) {
User user = users.get(id);
if (user == null) {
throw new RuntimeException("用户不存在: " + id);
}
return user;
}
@Override
public User save(User user) {
if (user.getId() == null) {
user.setId(idGenerator.getAndIncrement());
}
users.put(user.getId(), user);
System.out.println("用户已保存: " + user);
return user;
}
}
6. 属性条件注解
6.1 @ConditionalOnProperty - 基于配置属性的条件
这是使用最频繁的条件注解之一,根据配置文件中的属性值来决定是否创建Bean:
@Configuration
public class PropertyConditionConfig {
// 基础用法:检查属性是否存在
@Bean
@ConditionalOnProperty(name = "app.feature.enabled")
public FeatureService basicFeatureService() {
return new BasicFeatureService();
}
// 检查属性值是否匹配
@Bean
@ConditionalOnProperty(name = "app.cache.type", havingValue = "redis")
public CacheManager redisCacheManager() {
// Redis缓存管理器配置
return new RedisCacheManager.RedisCacheManagerBuilder(redisConnectionFactory())
.cacheDefaults(cacheConfiguration())
.build();
}
@Bean
@ConditionalOnProperty(name = "app.cache.type", havingValue = "caffeine")
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(10)));
return cacheManager;
}
// 使用前缀简化配置
@Bean
@ConditionalOnProperty(prefix = "app.security", name = "enabled", havingValue = "true")
public SecurityConfig securityConfig() {
return new SecurityConfig();
}
// 默认值处理
@Bean
@ConditionalOnProperty(
prefix = "app.monitoring",
name = "enabled",
havingValue = "true",
matchIfMissing = true // 如果属性不存在,默认为true
)
public MonitoringService monitoringService() {
return new MonitoringService();
}
}
// 配置文件示例 (application.yml)
/*
app:
feature:
enabled: true
cache:
type: redis # 可选值: redis, caffeine, simple
security:
enabled: true
monitoring:
enabled: true # 或者不配置,使用默认值
*/
// 监控服务
@Component
public class MonitoringService {
@Value("${app.monitoring.interval:60}")
private int monitoringInterval;
@Value("${app.monitoring.metrics:cpu,memory,disk}")
private String[] metrics;
@PostConstruct
public void init() {
System.out.println("监控服务已启动");
System.out.println("监控间隔: " + monitoringInterval + " 秒");
System.out.println("监控指标: " + Arrays.toString(metrics));
}
@Scheduled(fixedRateString = "${app.monitoring.interval:60}000")
public void collectMetrics() {
for (String metric : metrics) {
switch (metric.toLowerCase()) {
case "cpu":
double cpuUsage = getCpuUsage();
System.out.println("CPU使用率: " + String.format("%.2f%%", cpuUsage));
break;
case "memory":
long memoryUsage = getMemoryUsage();
System.out.println("内存使用: " + formatBytes(memoryUsage));
break;
case "disk":
long diskUsage = getDiskUsage();
System.out.println("磁盘使用: " + formatBytes(diskUsage));
break;
}
}
}
private double getCpuUsage() {
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
return osBean.getProcessCpuLoad() * 100;
}
private long getMemoryUsage() {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
return memoryBean.getHeapMemoryUsage().getUsed();
}
private long getDiskUsage() {
File root = new File("/");
return root.getTotalSpace() - root.getFreeSpace();
}
private String formatBytes(long bytes) {
String[] units = {"B", "KB", "MB", "GB"};
int unitIndex = 0;
double size = bytes;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return String.format("%.2f %s", size, units[unitIndex]);
}
}
7. 资源条件注解
7.1 @ConditionalOnResource - 当资源存在时
当指定的资源文件存在时,条件才成立:
@Configuration
public class ResourceConditionConfig {
// 当配置文件存在时,加载自定义配置
@Bean
@ConditionalOnResource(resources = "classpath:custom-config.properties")
public CustomConfigurationService customConfigService() {
return new CustomConfigurationService();
}
// 当模板文件存在时,启用模板服务
@Bean
@ConditionalOnResource(resources = "classpath:templates/email-template.html")
public EmailTemplateService emailTemplateService() {
return new EmailTemplateService();
}
// 检查多个资源文件
@Bean
@ConditionalOnResource(resources = {
"classpath:static/css/app.css",
"classpath:static/js/app.js",
"classpath:static/images/logo.png"
})
public StaticResourceService staticResourceService() {
return new StaticResourceService();
}
// 根据外部配置文件决定加载组件
@Bean
@ConditionalOnResource(resources = "file:./config/external-config.xml")
public ExternalConfigProcessor externalConfigProcessor() {
return new ExternalConfigProcessor();
}
}
// 自定义配置服务
@Component
public class CustomConfigurationService {
private Properties customProperties;
@PostConstruct
public void loadCustomConfig() {
try {
customProperties = new Properties();
InputStream inputStream = getClass().getClassLoader()
.getResourceAsStream("custom-config.properties");
if (inputStream != null) {
customProperties.load(inputStream);
System.out.println("自定义配置已加载:");
customProperties.forEach((key, value) ->
System.out.println(" " + key + " = " + value));
}
} catch (IOException e) {
System.err.println("加载自定义配置失败: " + e.getMessage());
}
}
public String getProperty(String key) {
return customProperties.getProperty(key);
}
public String getProperty(String key, String defaultValue) {
return customProperties.getProperty(key, defaultValue);
}
public Set<String> getAllKeys() {
return customProperties.stringPropertyNames();
}
}
// 邮件模板服务
@Component
public class EmailTemplateService {
private String emailTemplate;
@PostConstruct
public void loadTemplate() {
try {
Resource resource = new ClassPathResource("templates/email-template.html");
emailTemplate = Files.readString(Paths.get(resource.getURI()));
System.out.println("邮件模板已加载,长度: " + emailTemplate.length());
} catch (Exception e) {
System.err.println("加载邮件模板失败: " + e.getMessage());
}
}
public String generateEmail(String recipientName, String content) {
return emailTemplate
.replace("{{recipientName}}", recipientName)
.replace("{{content}}", content)
.replace("{{timestamp}}", LocalDateTime.now().toString());
}
public void sendEmail(String to, String subject, String recipientName, String content) {
String emailBody = generateEmail(recipientName, content);
System.out.println("发送邮件到: " + to);
System.out.println("主题: " + subject);
System.out.println("内容: " + emailBody);
// 实际的邮件发送逻辑...
}
}
// 静态资源服务
@Component
public class StaticResourceService {
private final List<String> availableResources = new ArrayList<>();
@PostConstruct
public void scanResources() {
String[] resourcePaths = {
"static/css/app.css",
"static/js/app.js",
"static/images/logo.png"
};
for (String path : resourcePaths) {
Resource resource = new ClassPathResource(path);
if (resource.exists()) {
availableResources.add(path);
System.out.println("静态资源已发现: " + path);
}
}
System.out.println("总共发现 " + availableResources.size() + " 个静态资源");
}
public List<String> getAvailableResources() {
return new ArrayList<>(availableResources);
}
public boolean isResourceAvailable(String resourcePath) {
return availableResources.contains(resourcePath);
}
}
7.2 多种资源类型的条件判断
@Configuration
public class AdvancedResourceConfig {
// 数据库初始化脚本
@Bean
@ConditionalOnResource(resources = "classpath:db/migration/*.sql")
public DatabaseMigrationService migrationService() {
return new DatabaseMigrationService();
}
// 国际化资源文件
@Bean
@ConditionalOnResource(resources = {
"classpath:messages_zh_CN.properties",
"classpath:messages_en_US.properties"
})
public InternationalizationService i18nService() {
return new InternationalizationService();
}
// SSL证书文件
@Bean
@ConditionalOnResource(resources = "classpath:ssl/keystore.jks")
public SSLConfigurationService sslService() {
return new SSLConfigurationService();
}
// API文档资源
@Bean
@ConditionalOnResource(resources = "classpath:static/swagger-ui/index.html")
public SwaggerUIService swaggerUIService() {
return new SwaggerUIService();
}
}
// 数据库迁移服务
@Component
public class DatabaseMigrationService {
@Autowired
private DataSource dataSource;
@PostConstruct
public void runMigrations() {
try {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:db/migration/*.sql");
System.out.println("发现 " + resources.length + " 个数据库迁移脚本");
for (Resource resource : resources) {
String filename = resource.getFilename();
if (filename != null && filename.endsWith(".sql")) {
String sql = Files.readString(Paths.get(resource.getURI()));
executeMigrationScript(filename, sql);
}
}
} catch (Exception e) {
System.err.println("数据库迁移失败: " + e.getMessage());
}
}
private void executeMigrationScript(String filename, String sql) {
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
System.out.println("执行迁移脚本: " + filename);
statement.executeUpdate(sql);
System.out.println("迁移脚本执行成功: " + filename);
} catch (SQLException e) {
System.err.println("执行迁移脚本失败 [" + filename + "]: " + e.getMessage());
}
}
}
// 国际化服务
@Component
public class InternationalizationService {
private final Map<Locale, Properties> messages = new HashMap<>();
@PostConstruct
public void loadMessages() {
loadMessageBundle(Locale.SIMPLIFIED_CHINESE, "messages_zh_CN.properties");
loadMessageBundle(Locale.US, "messages_en_US.properties");
}
private void loadMessageBundle(Locale locale, String filename) {
try {
Properties props = new Properties();
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(filename);
if (inputStream != null) {
props.load(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
messages.put(locale, props);
System.out.println("已加载国际化资源: " + filename + " (" + props.size() + " 条消息)");
}
} catch (IOException e) {
System.err.println("加载国际化资源失败 [" + filename + "]: " + e.getMessage());
}
}
public String getMessage(String key, Locale locale) {
Properties props = messages.get(locale);
if (props != null) {
return props.getProperty(key);
}
// 降级到默认语言
props = messages.get(Locale.US);
return props != null ? props.getProperty(key, key) : key;
}
public String getMessage(String key, Locale locale, Object... args) {
String message = getMessage(key, locale);
return MessageFormat.format(message, args);
}
}
8. 表达式条件注解
8.1 @ConditionalOnExpression - SpEL表达式条件
使用Spring表达式语言(SpEL)进行复杂的条件判断:
@Configuration
public class ExpressionConditionConfig {
// 简单的属性表达式
@Bean
@ConditionalOnExpression("${app.feature.advanced:false}")
public AdvancedFeatureService advancedFeatureService() {
return new AdvancedFeatureService();
}
// 多个条件的组合
@Bean
@ConditionalOnExpression("${app.cache.enabled:true} and '${app.cache.type:redis}'.equals('redis')")
public RedisCacheService redisCacheService() {
return new RedisCacheService();
}
// 数值比较
@Bean
@ConditionalOnExpression("${server.port:8080} != 8080")
public CustomPortService customPortService() {
return new CustomPortService();
}
// 复杂的逻辑表达式
@Bean
@ConditionalOnExpression(
"${app.environment.name:dev}.matches('prod|staging') and " +
"${app.features.monitoring:false} and " +
"${app.features.alerting:false}"
)
public ProductionMonitoringService productionMonitoringService() {
return new ProductionMonitoringService();
}
// 基于JVM参数的条件
@Bean
@ConditionalOnExpression("#{systemProperties['java.version'].startsWith('11') or systemProperties['java.version'].startsWith('17')}")
public ModernJavaFeatureService modernJavaFeatureService() {
return new ModernJavaFeatureService();
}
// 基于环境变量的条件
@Bean
@ConditionalOnExpression("#{environment.getProperty('PATH') != null}")
public SystemEnvironmentService systemEnvironmentService() {
return new SystemEnvironmentService();
}
// 时间相关的条件
@Bean
@ConditionalOnExpression("#{T(java.time.LocalTime).now().getHour() >= 9 and T(java.time.LocalTime).now().getHour() <= 17}")
public BusinessHoursService businessHoursService() {
return new BusinessHoursService();
}
}
// 高级功能服务
@Component
public class AdvancedFeatureService {
@Value("${app.feature.ai.enabled:false}")
private boolean aiEnabled;
@Value("${app.feature.ml.enabled:false}")
private boolean mlEnabled;
@Value("${app.feature.analytics.enabled:false}")
private boolean analyticsEnabled;
@PostConstruct
public void init() {
System.out.println("高级功能服务已启动");
System.out.println("AI功能: " + (aiEnabled ? "启用" : "禁用"));
System.out.println("机器学习: " + (mlEnabled ? "启用" : "禁用"));
System.out.println("数据分析: " + (analyticsEnabled ? "启用" : "禁用"));
}
public Map<String, Boolean> getFeatureStatus() {
Map<String, Boolean> status = new HashMap<>();
status.put("ai", aiEnabled);
status.put("ml", mlEnabled);
status.put("analytics", analyticsEnabled);
return status;
}
public void processAdvancedRequest(String requestType, Object data) {
switch (requestType.toLowerCase()) {
case "ai":
if (aiEnabled) {
processAIRequest(data);
} else {
throw new UnsupportedOperationException("AI功能未启用");
}
break;
case "ml":
if (mlEnabled) {
processMLRequest(data);
} else {
throw new UnsupportedOperationException("机器学习功能未启用");
}
break;
case "analytics":
if (analyticsEnabled) {
processAnalyticsRequest(data);
} else {
throw new UnsupportedOperationException("数据分析功能未启用");
}
break;
default:
throw new IllegalArgumentException("不支持的请求类型: " + requestType);
}
}
private void processAIRequest(Object data) {
System.out.println("处理AI请求: " + data);
// AI处理逻辑...
}
private void processMLRequest(Object data) {
System.out.println("处理机器学习请求: " + data);
// 机器学习处理逻辑...
}
private void processAnalyticsRequest(Object data) {
System.out.println("处理数据分析请求: " + data);
// 数据分析处理逻辑...
}
}
// 生产环境监控服务
@Component
public class ProductionMonitoringService {
@Value("${app.monitoring.alert.threshold.cpu:80}")
private double cpuThreshold;
@Value("${app.monitoring.alert.threshold.memory:85}")
private double memoryThreshold;
@Value("${app.monitoring.alert.threshold.disk:90}")
private double diskThreshold;
@Autowired
private NotificationService notificationService;
@PostConstruct
public void init() {
System.out.println("生产环境监控服务已启动");
System.out.println("CPU告警阈值: " + cpuThreshold + "%");
System.out.println("内存告警阈值: " + memoryThreshold + "%");
System.out.println("磁盘告警阈值: " + diskThreshold + "%");
}
@Scheduled(fixedRate = 30000) // 每30秒检查一次
public void monitorSystemHealth() {
SystemHealthMetrics metrics = collectSystemMetrics();
checkAndAlert("CPU", metrics.getCpuUsage(), cpuThreshold);
checkAndAlert("内存", metrics.getMemoryUsage(), memoryThreshold);
checkAndAlert("磁盘", metrics.getDiskUsage(), diskThreshold);
}
private void checkAndAlert(String metricName, double actualValue, double threshold) {
if (actualValue > threshold) {
String alertMessage = String.format(
"⚠️ %s使用率告警: %.2f%% (阈值: %.2f%%)",
metricName, actualValue, threshold
);
System.out.println(alertMessage);
notificationService.sendAlert(alertMessage);
}
}
private SystemHealthMetrics collectSystemMetrics() {
// 收集系统指标的实现...
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
double cpuUsage = osBean.getProcessCpuLoad() * 100;
double memoryUsage = (double) memoryBean.getHeapMemoryUsage().getUsed() /
memoryBean.getHeapMemoryUsage().getMax() * 100;
File disk = new File("/");
double diskUsage = (double)(disk.getTotalSpace() - disk.getFreeSpace()) /
disk.getTotalSpace() * 100;
return new SystemHealthMetrics(cpuUsage, memoryUsage, diskUsage);
}
}
// 系统健康指标类
public class SystemHealthMetrics {
private final double cpuUsage;
private final double memoryUsage;
private final double diskUsage;
public SystemHealthMetrics(double cpuUsage, double memoryUsage, double diskUsage) {
this.cpuUsage = cpuUsage;
this.memoryUsage = memoryUsage;
this.diskUsage = diskUsage;
}
// getter方法...
public double getCpuUsage() { return cpuUsage; }
public double getMemoryUsage() { return memoryUsage; }
public double getDiskUsage() { return diskUsage; }
}
8.2 复杂的SpEL表达式示例
@Configuration
public class ComplexExpressionConfig {
// 基于多个配置属性的复杂判断
@Bean
@ConditionalOnExpression(
"#{environment.getProperty('spring.profiles.active', 'dev').contains('prod') " +
"and T(java.lang.Boolean).parseBoolean(environment.getProperty('app.security.strict', 'false')) " +
"and environment.getProperty('app.version', '1.0').matches('\\d+\\.\\d+')}"
)
public StrictSecurityService strictSecurityService() {
return new StrictSecurityService();
}
// 基于系统资源的条件
@Bean
@ConditionalOnExpression(
"#{T(java.lang.Runtime).getRuntime().availableProcessors() >= 4 " +
"and T(java.lang.Runtime).getRuntime().maxMemory() >= 2L * 1024 * 1024 * 1024}"
)
public HighPerformanceService highPerformanceService() {
return new HighPerformanceService();
}
// 基于外部服务可用性的条件
@Bean
@ConditionalOnExpression(
"#{@networkService.isServiceAvailable('https://api.example.com/health') " +
"and @networkService.isServiceAvailable('https://cache.example.com/ping')}"
)
public ExternalIntegrationService externalIntegrationService() {
return new ExternalIntegrationService();
}
// 基于业务规则的条件
@Bean
@ConditionalOnExpression(
"#{T(java.time.LocalDate).now().getDayOfWeek().getValue() <= 5 " + // 工作日
"and T(java.time.LocalTime).now().isAfter(T(java.time.LocalTime).of(8, 0)) " + // 8点之后
"and T(java.time.LocalTime).now().isBefore(T(java.time.LocalTime).of(18, 0))}" // 18点之前
)
public BusinessHoursOnlyService businessHoursOnlyService() {
return new BusinessHoursOnlyService();
}
}
// 网络服务(用于检查外部服务可用性)
@Component("networkService")
public class NetworkService {
private final RestTemplate restTemplate;
public NetworkService() {
this.restTemplate = new RestTemplate();
}
@PostConstruct
public void init() {
System.out.println("外部API服务已启动,API密钥已配置");
}
public boolean isServiceAvailable(String url) {
try {
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
return response.getStatusCode().is2xxSuccessful();
} catch (Exception e) {
System.out.println("服务不可用: " + url + " - " + e.getMessage());
return false;
}
}
public boolean isPortOpen(String host, int port) {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(host, port), 3000);
return true;
} catch (IOException e) {
return false;
}
}
}
// 严格安全服务
@Component
public class StrictSecurityService {
@PostConstruct
public void init() {
System.out.println("严格安全模式已启用");
enableStrictPolicies();
}
private void enableStrictPolicies() {
// 启用严格的安全策略
System.out.println("- 启用强密码策略");
System.out.println("- 启用两因素认证");
System.out.println("- 启用访问日志记录");
System.out.println("- 启用异常行为检测");
}
public boolean validateAccess(String userId, String resource, String action) {
// 严格的访问控制逻辑
System.out.println("严格验证访问权限: " + userId + " -> " + resource + " (" + action + ")");
return true;
}
}
// 高性能服务
@Component
public class HighPerformanceService {
private final int availableProcessors;
private final long maxMemory;
private final ExecutorService executorService;
public HighPerformanceService() {
this.availableProcessors = Runtime.getRuntime().availableProcessors();
this.maxMemory = Runtime.getRuntime().maxMemory();
// 根据系统资源创建线程池
this.executorService = Executors.newFixedThreadPool(availableProcessors * 2);
System.out.println("高性能服务已启动:");
System.out.println("- 可用处理器: " + availableProcessors);
System.out.println("- 最大内存: " + formatBytes(maxMemory));
System.out.println("- 线程池大小: " + (availableProcessors * 2));
}
public CompletableFuture<String> processAsync(String data) {
return CompletableFuture.supplyAsync(() -> {
// 模拟高性能处理
try {
Thread.sleep(100); // 模拟处理时间
return "高性能处理结果: " + data.toUpperCase();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}, executorService);
}
private String formatBytes(long bytes) {
return String.format("%.2f GB", bytes / (1024.0 * 1024.0 * 1024.0));
}
@PreDestroy
public void shutdown() {
executorService.shutdown();
}
}
9. Web环境条件注解
9.1 @ConditionalOnWebApplication - Web应用条件
根据应用类型决定是否创建Bean:
@Configuration
public class WebApplicationConfig {
// 只在Web环境中创建
@Bean
@ConditionalOnWebApplication
public WebSecurityConfig webSecurityConfig() {
return new WebSecurityConfig();
}
// 只在Servlet Web环境中创建
@Bean
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public ServletWebServerFactoryCustomizer servletCustomizer() {
return new ServletWebServerFactoryCustomizer();
}
// 只在响应式Web环境中创建
@Bean
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public ReactiveWebServerFactoryCustomizer reactiveCustomizer() {
return new ReactiveWebServerFactoryCustomizer();
}
// 任何类型的Web环境
@Bean
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.ANY)
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
// Web安全配置
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**", "/api/health", "/actuator/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.permitAll()
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.permitAll()
)
.csrf(csrf -> csrf.disable());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin"))
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}
// Servlet定制器
@Component
public class ServletWebServerFactoryCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
System.out.println("定制Servlet Web服务器");
// 设置端口
factory.setPort(8080);
// 设置上下文路径
factory.setContextPath("/api");
// 设置会话超时
factory.getSession().setTimeout(Duration.ofMinutes(30));
// 添加错误页面
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404");
ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500");
factory.addErrorPages(error404Page, error500Page);
// 设置SSL(如果需要)
// Ssl ssl = new Ssl();
// ssl.setKeyStore("classpath:keystore.jks");
// ssl.setKeyStorePassword("password");
// factory.setSsl(ssl);
System.out.println("Servlet Web服务器配置完成");
}
}
// 响应式定制器
@Component
public class ReactiveWebServerFactoryCustomizer
implements WebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory> {
@Override
public void customize(ConfigurableReactiveWebServerFactory factory) {
System.out.println("定制响应式Web服务器");
// 设置端口
factory.setPort(8081);
// 其他响应式特定的配置...
System.out.println("响应式Web服务器配置完成");
}
}
9.2 @ConditionalOnNotWebApplication - 非Web应用条件
@Configuration
public class NonWebApplicationConfig {
// 只在非Web环境中创建(如命令行应用)
@Bean
@ConditionalOnNotWebApplication
public CommandLineProcessor commandLineProcessor() {
return new CommandLineProcessor();
}
// 批处理任务调度器(非Web环境)
@Bean
@ConditionalOnNotWebApplication
public TaskScheduler batchTaskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("batch-task-");
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(30);
return scheduler;
}
// 文件监控服务(非Web环境)
@Bean
@ConditionalOnNotWebApplication
public FileMonitorService fileMonitorService() {
return new FileMonitorService();
}
}
// 命令行处理器
@Component
public class CommandLineProcessor implements CommandLineRunner {
@Value("${app.input.directory:./input}")
private String inputDirectory;
@Value("${app.output.directory:./output}")
private String outputDirectory;
@Override
public void run(String... args) throws Exception {
System.out.println("命令行应用启动");
System.out.println("输入目录: " + inputDirectory);
System.out.println("输出目录: " + outputDirectory);
// 处理命令行参数
if (args.length > 0) {
System.out.println("命令行参数:");
for (int i = 0; i < args.length; i++) {
System.out.println(" 参数" + (i + 1) + ": " + args[i]);
}
processCommand(args);
} else {
System.out.println("没有提供命令行参数,启动交互模式");
startInteractiveMode();
}
}
private void processCommand(String[] args) {
String command = args[0].toLowerCase();
switch (command) {
case "process":
if (args.length > 1) {
processFile(args[1]);
} else {
System.out.println("请指定要处理的文件");
}
break;
case "batch":
processBatch();
break;
case "status":
showStatus();
break;
default:
System.out.println("未知命令: " + command);
showHelp();
}
}
private void startInteractiveMode() {
Scanner scanner = new Scanner(System.in);
System.out.println("交互模式已启动。输入 'help' 查看可用命令,输入 'exit' 退出。");
while (true) {
System.out.print("> ");
String input = scanner.nextLine().trim();
if ("exit".equalsIgnoreCase(input)) {
System.out.println("再见!");
break;
}
if ("help".equalsIgnoreCase(input)) {
showHelp();
continue;
}
processCommand(input.split("\\s+"));
}
}
private void processFile(String filename) {
System.out.println("处理文件: " + filename);
// 文件处理逻辑...
}
private void processBatch() {
System.out.println("开始批处理...");
// 批处理逻辑...
}
private void showStatus() {
System.out.println("应用状态: 运行中");
System.out.println("内存使用: " + getMemoryUsage());
System.out.println("处理的文件数: " + getProcessedFileCount());
}
private void showHelp() {
System.out.println("可用命令:");
System.out.println(" process <filename> - 处理指定文件");
System.out.println(" batch - 批处理所有文件");
System.out.println(" status - 显示应用状态");
System.out.println(" help - 显示此帮助信息");
System.out.println(" exit - 退出应用");
}
private String getMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
long used = runtime.totalMemory() - runtime.freeMemory();
long max = runtime.maxMemory();
return String.format("%.2f%% (%s / %s)",
(double) used / max * 100,
formatBytes(used),
formatBytes(max));
}
private long getProcessedFileCount() {
// 返回已处理的文件数量
return 0; // 示例值
}
private String formatBytes(long bytes) {
String[] units = {"B", "KB", "MB", "GB"};
int unitIndex = 0;
double size = bytes;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return String.format("%.2f %s", size, units[unitIndex]);
}
}
// 文件监控服务
@Component
public class FileMonitorService {
@Value("${app.monitor.directory:./watch}")
private String monitorDirectory;
private WatchService watchService;
private boolean monitoring = false;
@PostConstruct
public void startMonitoring() {
try {
Path path = Paths.get(monitorDirectory);
Files.createDirectories(path);
watchService = FileSystems.getDefault().newWatchService();
path.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
monitoring = true;
// 在后台线程中监控文件变化
CompletableFuture.runAsync(this::monitorFiles);
System.out.println("文件监控服务已启动,监控目录: " + monitorDirectory);
} catch (IOException e) {
System.err.println("启动文件监控失败: " + e.getMessage());
}
}
private void monitorFiles() {
while (monitoring) {
try {
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
Path filename = (Path) event.context();
System.out.println("文件事件: " + kind + " - " + filename);
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
handleFileCreated(filename);
} else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
handleFileModified(filename);
} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
handleFileDeleted(filename);
}
}
key.reset();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
private void handleFileCreated(Path filename) {
System.out.println("新文件创建: " + filename);
// 处理新文件创建的逻辑...
}
private void handleFileModified(Path filename) {
System.out.println("文件修改: " + filename);
// 处理文件修改的逻辑...
}
private void handleFileDeleted(Path filename) {
System.out.println("文件删除: " + filename);
// 处理文件删除的逻辑...
}
@PreDestroy
public void stopMonitoring() {
monitoring = false;
try {
if (watchService != null) {
watchService.close();
}
} catch (IOException e) {
System.err.println("关闭文件监控失败: " + e.getMessage());
}
}
}
10. 自定义条件注解
10.1 创建自定义条件类
实现Condition
接口创建自定义条件逻辑:
// 操作系统条件
public class OperatingSystemCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取注解属性
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnOS.class.getName());
if (attributes == null) {
return false;
}
OS[] targetOS = (OS[]) attributes.get("value");
String osName = context.getEnvironment().getProperty("os.name", "").toLowerCase();
for (OS os : targetOS) {
switch (os) {
case WINDOWS:
if (osName.contains("windows")) return true;
break;
case LINUX:
if (osName.contains("linux")) return true;
break;
case MAC:
if (osName.contains("mac") || osName.contains("darwin")) return true;
break;
case UNIX:
if (osName.contains("unix") || osName.contains("linux") ||
osName.contains("mac") || osName.contains("darwin")) return true;
break;
}
}
return false;
}
}
// 操作系统枚举
public enum OS {
WINDOWS, LINUX, MAC, UNIX
}
// 自定义操作系统条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OperatingSystemCondition.class)
public @interface ConditionalOnOS {
OS[] value();
}
// 使用自定义条件注解
@Configuration
public class OSSpecificConfig {
@Bean
@ConditionalOnOS({OS.WINDOWS})
public FileSystemService windowsFileSystemService() {
return new WindowsFileSystemService();
}
@Bean
@ConditionalOnOS({OS.LINUX, OS.MAC})
public FileSystemService unixFileSystemService() {
return new UnixFileSystemService();
}
@Bean
@ConditionalOnOS({OS.MAC})
public NotificationService macNotificationService() {
return new MacNotificationService();
}
}
// Windows文件系统服务
@Component
public class WindowsFileSystemService implements FileSystemService {
@PostConstruct
public void init() {
System.out.println("Windows文件系统服务已启动");
System.out.println("- 支持NTFS文件系统");
System.out.println("- 支持Windows路径分隔符");
System.out.println("- 支持Windows文件属性");
}
@Override
public String getPathSeparator() {
return "\\";
}
@Override
public boolean createDirectory(String path) {
// Windows特定的目录创建逻辑
System.out.println("使用Windows API创建目录: " + path);
return new File(path).mkdirs();
}
@Override
public List<String> listFiles(String directory) {
File dir = new File(directory);
if (dir.exists() && dir.isDirectory()) {
String[] files = dir.list();
return files != null ? Arrays.asList(files) : new ArrayList<>();
}
return new ArrayList<>();
}
@Override
public long getFileSize(String filePath) {
return new File(filePath).length();
}
public void setFileAttributes(String filePath, boolean hidden, boolean readOnly) {
File file = new File(filePath);
if (file.exists()) {
// Windows特定的文件属性设置
if (hidden) {
System.out.println("设置文件为隐藏: " + filePath);
// 实际实现需要调用Windows API
}
if (readOnly) {
file.setReadOnly();
System.out.println("设置文件为只读: " + filePath);
}
}
}
}
// Unix文件系统服务
@Component
public class UnixFileSystemService implements FileSystemService {
@PostConstruct
public void init() {
System.out.println("Unix文件系统服务已启动");
System.out.println("- 支持ext4/HFS+等文件系统");
System.out.println("- 支持Unix路径分隔符");
System.out.println("- 支持Unix文件权限");
}
@Override
public String getPathSeparator() {
return "/";
}
@Override
public boolean createDirectory(String path) {
// Unix特定的目录创建逻辑
System.out.println("使用Unix系统调用创建目录: " + path);
return new File(path).mkdirs();
}
@Override
public List<String> listFiles(String directory) {
File dir = new File(directory);
if (dir.exists() && dir.isDirectory()) {
String[] files = dir.list();
return files != null ? Arrays.asList(files) : new ArrayList<>();
}
return new ArrayList<>();
}
@Override
public long getFileSize(String filePath) {
return new File(filePath).length();
}
public void setFilePermissions(String filePath, String permissions) {
// Unix特定的权限设置
System.out.println("设置文件权限: " + filePath + " -> " + permissions);
// 实际实现需要调用chmod命令或系统API
try {
ProcessBuilder pb = new ProcessBuilder("chmod", permissions, filePath);
Process process = pb.start();
int exitCode = process.waitFor();
if (exitCode == 0) {
System.out.println("权限设置成功");
} else {
System.out.println("权限设置失败");
}
} catch (Exception e) {
System.err.println("设置权限失败: " + e.getMessage());
}
}
}
10.2 复杂的自定义条件
// 时间范围条件
public class TimeRangeCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnTimeRange.class.getName());
if (attributes == null) {
return false;
}
String startTime = (String) attributes.get("startTime");
String endTime = (String) attributes.get("endTime");
String timezone = (String) attributes.get("timezone");
try {
ZoneId zoneId = ZoneId.of(timezone);
LocalTime start = LocalTime.parse(startTime);
LocalTime end = LocalTime.parse(endTime);
LocalTime now = LocalTime.now(zoneId);
if (start.isBefore(end)) {
// 同一天内的时间范围
return now.isAfter(start) && now.isBefore(end);
} else {
// 跨午夜的时间范围
return now.isAfter(start) || now.isBefore(end);
}
} catch (Exception e) {
System.err.println("时间范围条件解析失败: " + e.getMessage());
return false;
}
}
}
// 时间范围条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(TimeRangeCondition.class)
public @interface ConditionalOnTimeRange {
String startTime();
String endTime();
String timezone() default "UTC";
}
// 环境变量条件
public class EnvironmentVariableCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnEnvironmentVariable.class.getName());
if (attributes == null) {
return false;
}
String name = (String) attributes.get("name");
String value = (String) attributes.get("value");
boolean ignoreCase = (Boolean) attributes.get("ignoreCase");
String envValue = System.getenv(name);
if (value.isEmpty()) {
// 只检查环境变量是否存在
return envValue != null && !envValue.isEmpty();
}
if (envValue == null) {
return false;
}
return ignoreCase ? envValue.equalsIgnoreCase(value) : envValue.equals(value);
}
}
// 环境变量条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(EnvironmentVariableCondition.class)
public @interface ConditionalOnEnvironmentVariable {
String name();
String value() default "";
boolean ignoreCase() default false;
}
// 使用复杂自定义条件
@Configuration
public class AdvancedConditionConfig {
// 只在工作时间启用的服务
@Bean
@ConditionalOnTimeRange(startTime = "09:00", endTime = "17:00", timezone = "Asia/Shanghai")
public BusinessHoursService businessHoursService() {
return new BusinessHoursService();
}
// 只在夜间启用的批处理服务
@Bean
@ConditionalOnTimeRange(startTime = "23:00", endTime = "06:00", timezone = "Asia/Shanghai")
public NightBatchService nightBatchService() {
return new NightBatchService();
}
// 基于环境变量的服务
@Bean
@ConditionalOnEnvironmentVariable(name = "DEPLOYMENT_ENV", value = "production", ignoreCase = true)
public ProductionLoggingService productionLoggingService() {
return new ProductionLoggingService();
}
// 检查环境变量是否存在
@Bean
@ConditionalOnEnvironmentVariable(name = "API_KEY")
public ExternalApiService externalApiService() {
return new ExternalApiService();
}
}
// 业务时间服务
@Component
public class BusinessHoursService {
@PostConstruct
public void init() {
System.out.println("业务时间服务已启动 (工作时间: 09:00-17:00)");
}
@Scheduled(fixedRate = 60000) // 每分钟检查一次
public void checkBusinessHours() {
LocalTime now = LocalTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println("当前时间: " + now + " - 业务时间内");
// 业务时间内的定期任务
processBusinessRequests();
}
private void processBusinessRequests() {
System.out.println("处理业务请求...");
// 业务请求处理逻辑
}
}
// 夜间批处理服务
@Component
public class NightBatchService {
@PostConstruct
public void init() {
System.out.println("夜间批处理服务已启动 (处理时间: 23:00-06:00)");
}
@Scheduled(cron = "0 0 23 * * ?") // 每天23点开始
public void startNightBatch() {
System.out.println("开始夜间批处理任务");
try {
// 数据备份
performDataBackup();
// 数据清理
performDataCleanup();
// 报表生成
generateDailyReports();
// 系统维护
performSystemMaintenance();
System.out.println("夜间批处理任务完成");
} catch (Exception e) {
System.err.println("夜间批处理任务失败: " + e.getMessage());
}
}
private void performDataBackup() {
System.out.println("执行数据备份...");
// 数据备份逻辑
}
private void performDataCleanup() {
System.out.println("执行数据清理...");
// 数据清理逻辑
}
private void generateDailyReports() {
System.out.println("生成日报表...");
// 报表生成逻辑
}
private void performSystemMaintenance() {
System.out.println("执行系统维护...");
// 系统维护逻辑
}
}
// 外部API服务
@Component
public class ExternalApiService {
@Value("${API_KEY:}")
private String apiKey;
private final RestTemplate restTemplate;
public ExternalApiService() {
this.restTemplate = new RestTemplate();
}
@PostConstruct
public void init() {
if (apiKey.isEmpty()) {
apiKey = System.getenv("API_KEY");
}
System.out.println("外部API服务已启动,API密钥已配置");
}
public Map<String, Object> callExternalApi(String endpoint, Map<String, Object> params) {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(apiKey);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(params, headers);
ResponseEntity<Map> response = restTemplate.postForEntity(
"https://api.example.com/" + endpoint, entity, Map.class);
return response.getBody();
} catch (Exception e) {
System.err.println("调用外部API失败: " + e.getMessage());
return Map.of("error", e.getMessage());
}
}
}
11. 条件注解组合使用
11.1 多条件组合
@Configuration
public class CombinedConditionsConfig {
// 组合多个条件:类存在 + 属性配置 + Bean不存在
@Bean
@ConditionalOnClass(RedisTemplate.class)
@ConditionalOnProperty(prefix = "spring.redis", name = "host")
@ConditionalOnMissingBean(name = "customRedisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 设置序列化器
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
// 复杂的多条件组合
@Bean
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(name = "org.springframework.security.web.SecurityFilterChain")
@ConditionalOnProperty(name = "app.security.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnMissingBean(SecurityFilterChain.class)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**", "/actuator/health").permitAll()
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.build();
}
// 基于环境和功能的组合条件
@Bean
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "prod")
@ConditionalOnProperty(prefix = "app.monitoring", name = "enabled", havingValue = "true")
@ConditionalOnClass(name = "io.micrometer.core.instrument.MeterRegistry")
@ConditionalOnBean(MeterRegistry.class)
public ProductionMetricsService productionMetricsService(MeterRegistry meterRegistry) {
return new ProductionMetricsService(meterRegistry);
}
}
// 生产环境指标服务
@Component
public class ProductionMetricsService {
private final MeterRegistry meterRegistry;
private final Counter requestCounter;
private final Timer responseTimer;
private final Gauge systemLoadGauge;
public ProductionMetricsService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
// 初始化指标
this.requestCounter = Counter.builder("http.requests.total")
.description("Total HTTP requests")
.register(meterRegistry);
this.responseTimer = Timer.builder("http.response.time")
.description("HTTP response time")
.register(meterRegistry);
this.systemLoadGauge = Gauge.builder("system.load.average")
.description("System load average")
.register(meterRegistry, this, ProductionMetricsService::getSystemLoad);
}
@PostConstruct
public void init() {
System.out.println("生产环境指标服务已启动");
startMetricsCollection();
}
private void startMetricsCollection() {
// 定期收集自定义指标
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(this::collectCustomMetrics, 0, 30, TimeUnit.SECONDS);
}
private void collectCustomMetrics() {
// 应用特定的指标收集
collectDatabaseMetrics();
collectCacheMetrics();
collectBusinessMetrics();
}
private void collectDatabaseMetrics() {
// 数据库连接池指标
Timer.Sample sample = Timer.start(meterRegistry);
// 模拟数据库查询时间
sample.stop(Timer.builder("database.query.time")
.description("Database query execution time")
.register(meterRegistry));
}
private void collectCacheMetrics() {
// 缓存命中率指标
meterRegistry.gauge("cache.hit.ratio", Math.random() * 100);
}
private void collectBusinessMetrics() {
// 业务指标
meterRegistry.gauge("active.users", getActiveUserCount());
meterRegistry.gauge("pending.orders", getPendingOrderCount());
}
private double getSystemLoad() {
return ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
}
private int getActiveUserCount() {
// 模拟活跃用户数
return (int) (Math.random() * 1000);
}
private int getPendingOrderCount() {
// 模拟待处理订单数
return (int) (Math.random() * 50);
}
public void recordRequest(String endpoint) {
requestCounter.increment(Tags.of("endpoint", endpoint));
}
public Timer.Sample startTimer() {
return Timer.start(meterRegistry);
}
public void stopTimer(Timer.Sample sample, String endpoint) {
sample.stop(responseTimer.tag("endpoint", endpoint));
}
}
11.2 条件优先级和顺序
@Configuration
public class ConditionalPriorityConfig {
// 第一优先级:特定环境的Redis配置
@Bean("redisTemplate")
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "prod")
@ConditionalOnProperty(prefix = "app.redis.cluster", name = "enabled", havingValue = "true")
@ConditionalOnClass(RedisTemplate.class)
public RedisTemplate<String, Object> clusterRedisTemplate() {
System.out.println("创建集群Redis模板 (生产环境)");
// 集群Redis配置
return createClusterRedisTemplate();
}
// 第二优先级:单机Redis配置
@Bean("redisTemplate")
@ConditionalOnProperty(prefix = "spring.redis", name = "host")
@ConditionalOnClass(RedisTemplate.class)
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<String, Object> standaloneRedisTemplate() {
System.out.println("创建单机Redis模板");
// 单机Redis配置
return createStandaloneRedisTemplate();
}
// 第三优先级:内存缓存(Redis不可用时的降级方案)
@Bean
@ConditionalOnMissingClass("org.springframework.data.redis.core.RedisTemplate")
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager fallbackCacheManager() {
System.out.println("创建内存缓存管理器 (Redis不可用)");
return new ConcurrentMapCacheManager("default", "users", "products");
}
private RedisTemplate<String, Object> createClusterRedisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 集群连接工厂配置
JedisClusterConfiguration clusterConfig = new JedisClusterConfiguration();
// 添加集群节点...
JedisConnectionFactory factory = new JedisConnectionFactory(clusterConfig);
template.setConnectionFactory(factory);
configureRedisTemplate(template);
return template;
}
private RedisTemplate<String, Object> createStandaloneRedisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 单机连接工厂配置
JedisConnectionFactory factory = new JedisConnectionFactory();
template.setConnectionFactory(factory);
configureRedisTemplate(template);
return template;
}
private void configureRedisTemplate(RedisTemplate<String, Object> template) {
// 通用Redis模板配置
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
}
}
12. 实际应用场景
12.1 多环境配置管理
// 开发环境配置
@Configuration
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
public class DevelopmentConfig {
@Bean
public DataSource devDataSource() {
return DataSourceBuilder.create()
.driverClassName("org.h2.Driver")
.url("jdbc:h2:mem:devdb;DB_CLOSE_DELAY=-1")
.username("sa")
.password("")
.build();
}
@Bean
@ConditionalOnMissingBean(PasswordEncoder.class)
public PasswordEncoder devPasswordEncoder() {
// 开发环境使用简单的密码编码器
return NoOpPasswordEncoder.getInstance();
}
@Bean
public LoggingService devLoggingService() {
return new ConsoleLoggingService();
}
}
// 测试环境配置
@Configuration
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "test")
public class TestConfig {
@Bean
@Primary
public UserService mockUserService() {
return Mockito.mock(UserService.class);
}
@Bean
@Primary
public EmailService mockEmailService() {
return new MockEmailService();
}
@Bean
public TestDataInitializer testDataInitializer() {
return new TestDataInitializer();
}
}
// 生产环境配置
@Configuration
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "prod")
public class ProductionConfig {
@Bean
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
public DataSource prodDataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(environment.getProperty("spring.datasource.url"));
config.setUsername(environment.getProperty("spring.datasource.username"));
config.setPassword(environment.getProperty("spring.datasource.password"));
// 生产环境连接池优化
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
return new HikariDataSource(config);
}
@Bean
public PasswordEncoder prodPasswordEncoder() {
return new BCryptPasswordEncoder(12); // 更强的加密强度
}
@Bean
public LoggingService prodLoggingService() {
return new DatabaseLoggingService();
}
@Bean
@ConditionalOnProperty(prefix = "app.monitoring", name = "enabled", havingValue = "true")
public HealthCheckService healthCheckService() {
return new HealthCheckService();
}
}
// 控制台日志服务(开发环境)
@Component
public class ConsoleLoggingService implements LoggingService {
@Override
public void log(LogLevel level, String message, Object... args) {
String formattedMessage = MessageFormat.format(message, args);
System.out.printf("[%s] %s: %s%n",
LocalDateTime.now(), level, formattedMessage);
}
@Override
public void logError(String message, Throwable throwable) {
System.err.printf("[%s] ERROR: %s%n", LocalDateTime.now(), message);
throwable.printStackTrace();
}
}
// 数据库日志服务(生产环境)
@Component
public class DatabaseLoggingService implements LoggingService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void log(LogLevel level, String message, Object... args) {
String formattedMessage = MessageFormat.format(message, args);
jdbcTemplate.update(
"INSERT INTO application_logs (level, message, created_at) VALUES (?, ?, ?)",
level.toString(), formattedMessage, Timestamp.valueOf(LocalDateTime.now())
);
}
@Override
public void logError(String message, Throwable throwable) {
String stackTrace = Arrays.stream(throwable.getStackTrace())
.map(StackTraceElement::toString)
.collect(Collectors.joining("\n"));
jdbcTemplate.update(
"INSERT INTO error_logs (message, stack_trace, created_at) VALUES (?, ?, ?)",
message, stackTrace, Timestamp.valueOf(LocalDateTime.now())
);
}
}
12.2 功能开关和A/B测试
@Configuration
public class FeatureToggleConfig {
// 新功能开关
@Bean
@ConditionalOnProperty(name = "app.features.new-ui", havingValue = "true")
public UIService newUIService() {
return new NewUIService();
}
@Bean
@ConditionalOnProperty(name = "app.features.new-ui", havingValue = "false", matchIfMissing = true)
public UIService legacyUIService() {
return new LegacyUIService();
}
// A/B测试配置
@Bean
@ConditionalOnProperty(name = "app.ab-test.algorithm", havingValue = "v2")
public RecommendationService experimentalRecommendationService() {
return new ExperimentalRecommendationService();
}
@Bean
@ConditionalOnProperty(name = "app.ab-test.algorithm", havingValue = "v1", matchIfMissing = true)
public RecommendationService standardRecommendationService() {
return new StandardRecommendationService();
}
// 渐进式功能发布
@Bean
@ConditionalOnExpression("#{environment.getProperty('app.features.beta-users', '').contains(authentication.name)}")
public BetaFeatureService betaFeatureService() {
return new BetaFeatureService();
}
}
// 新UI服务
@Component
public class NewUIService implements UIService {
@PostConstruct
public void init() {
System.out.println("新UI服务已启用 - 使用现代化界面");
}
@Override
public String renderPage(String pageName, Map<String, Object> model) {
return "<!DOCTYPE html><html>" +
"<head><title>New UI - " + pageName + "</title>" +
"<link rel='stylesheet' href='/css/modern-theme.css'></head>" +
"<body class='modern-layout'>" +
renderModernContent(pageName, model) +
"</body></html>";
}
private String renderModernContent(String pageName, Map<String, Object> model) {
StringBuilder content = new StringBuilder();
content.append("<nav class='modern-nav'>").append(renderNavigation()).append("</nav>");
content.append("<main class='modern-main'>").append(renderMainContent(pageName, model)).append("</main>");
content.append("<footer class='modern-footer'>").append(renderFooter()).append("</footer>");
return content.toString();
}
private String renderNavigation() {
return "<ul><li><a href='/dashboard'>Dashboard</a></li>" +
"<li><a href='/profile'>Profile</a></li>" +
"<li><a href='/settings'>Settings</a></li></ul>";
}
private String renderMainContent(String pageName, Map<String, Object> model) {
return "<h1>Welcome to " + pageName + "</h1>" +
"<p>This is the new modern interface.</p>";
}
private String renderFooter() {
return "<p>© 2024 Modern App. All rights reserved.</p>";
}
}
// 旧UI服务
@Component
public class LegacyUIService implements UIService {
@PostConstruct
public void init() {
System.out.println("旧UI服务已启用 - 使用传统界面");
}
@Override
public String renderPage(String pageName, Map<String, Object> model) {
return "<html>" +
"<head><title>Legacy UI - " + pageName + "</title>" +
"<link rel='stylesheet' href='/css/legacy-theme.css'></head>" +
"<body>" +
renderLegacyContent(pageName, model) +
"</body></html>";
}
private String renderLegacyContent(String pageName, Map<String, Object> model) {
return "<table width='100%'>" +
"<tr><td>" + renderLegacyNavigation() + "</td></tr>" +
"<tr><td>" + renderLegacyMainContent(pageName, model) + "</td></tr>" +
"<tr><td>" + renderLegacyFooter() + "</td></tr>" +
"</table>";
}
private String renderLegacyNavigation() {
return "<table><tr>" +
"<td><a href='/dashboard'>Dashboard</a></td>" +
"<td><a href='/profile'>Profile</a></td>" +
"<td><a href='/settings'>Settings</a></td>" +
"</tr></table>";
}
private String renderLegacyMainContent(String pageName, Map<String, Object> model) {
return "<h2>Welcome to " + pageName + "</h2>" +
"<p>This is the legacy interface.</p>";
}
private String renderLegacyFooter() {
return "<hr><p>Copyright 2024 Legacy App</p>";
}
}
// 实验性推荐服务
@Component
public class ExperimentalRecommendationService implements RecommendationService {
@PostConstruct
public void init() {
System.out.println("实验性推荐服务已启用 - 使用机器学习算法");
}
@Override
public List<Product> getRecommendations(String userId, int limit) {
System.out.println("使用ML算法为用户 " + userId + " 生成推荐");
// 模拟机器学习推荐算法
return generateMLRecommendations(userId, limit);
}
private List<Product> generateMLRecommendations(String userId, int limit) {
List<Product> recommendations = new ArrayList<>();
// 模拟复杂的ML算法
for (int i = 1; i <= limit; i++) {
Product product = new Product();
product.setId("ml-" + i);
product.setName("ML推荐商品 " + i);
product.setScore(0.95 - (i * 0.1)); // 模拟置信度分数
recommendations.add(product);
}
return recommendations;
}
}
// 标准推荐服务
@Component
public class StandardRecommendationService implements RecommendationService {
@PostConstruct
public void init() {
System.out.println("标准推荐服务已启用 - 使用基于规则的算法");
}
@Override
public List<Product> getRecommendations(String userId, int limit) {
System.out.println("使用规则算法为用户 " + userId + " 生成推荐");
// 模拟基于规则的推荐算法
return generateRuleBasedRecommendations(userId, limit);
}
private List<Product> generateRuleBasedRecommendations(String userId, int limit) {
List<Product> recommendations = new ArrayList<>();
// 模拟简单的规则算法
for (int i = 1; i <= limit; i++) {
Product product = new Product();
product.setId("rule-" + i);
product.setName("规则推荐商品 " + i);
product.setScore(0.80 + (Math.random() * 0.15)); // 模拟分数
recommendations.add(product);
}
return recommendations;
}
}
13. 最佳实践和注意事项
13.1 条件注解设计原则
// ✅ 好的实践:明确的条件逻辑
@Configuration
public class GoodConditionalConfig {
// 1. 条件清晰明确
@Bean
@ConditionalOnClass(RedisTemplate.class)
@ConditionalOnProperty(prefix = "spring.redis", name = "host")
@ConditionalOnMissingBean(RedisTemplate.class)
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
// 2. 提供默认实现
@Bean
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager defaultCacheManager() {
return new ConcurrentMapCacheManager("default", "users", "products");
}
// 3. 合理的条件组合
@Bean
@ConditionalOnWebApplication
@ConditionalOnClass(name = "org.springframework.security.web.SecurityFilterChain")
@ConditionalOnProperty(name = "app.security.enabled", matchIfMissing = true)
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())
.build();
}
}
13.2 性能优化建议
@Configuration
public class PerformanceOptimizedConfig {
// 1. 使用类名字符串避免不必要的类加载
@Bean
@ConditionalOnClass(name = "com.expensive.library.ExpensiveClass")
public ExpensiveService expensiveService() {
return new ExpensiveService();
}
// 2. 合理使用@ConditionalOnBean的搜索范围
@Bean
@ConditionalOnBean(value = DataSource.class, search = SearchStrategy.CURRENT)
public LocalTransactionManager transactionManager(DataSource dataSource) {
return new LocalTransactionManager(dataSource);
}
// 3. 避免在条件中进行昂贵的操作
@Bean
@ConditionalOnProperty(name = "app.feature.enabled")
public FeatureService featureService() {
return new FeatureService();
}
}
// 自定义高效条件
public class CachedCondition implements Condition {
private volatile Boolean cachedResult;
private final Object lock = new Object();
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (cachedResult == null) {
synchronized (lock) {
if (cachedResult == null) {
cachedResult = performExpensiveCheck(context);
}
}
}
return cachedResult;
}
private boolean performExpensiveCheck(ConditionContext context) {
// 昂贵的检查逻辑
String osName = context.getEnvironment().getProperty("os.name");
return osName != null && osName.toLowerCase().contains("linux");
}
}
14. 常见问题和解决方案
14.1 条件注解不生效
问题1:配置类没有被扫描到
// ❌ 问题:配置类不在扫描路径中
package com.other.package.config;
@Configuration
@ConditionalOnProperty(name = "app.feature.enabled", havingValue = "true")
public class FeatureConfig {
@Bean
public FeatureService featureService() {
return new FeatureService();
}
}
// ✅ 解决方案1:确保在扫描路径中
@SpringBootApplication(scanBasePackages = {"com.myapp", "com.other.package"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// ✅ 解决方案2:使用@Import显式导入
@SpringBootApplication
@Import(FeatureConfig.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
问题2:属性配置错误
// ❌ 问题:属性名称拼写错误
@Bean
@ConditionalOnProperty(name = "app.feature.enable", havingValue = "true") // 应该是enabled
public FeatureService featureService() {
return new FeatureService();
}
// ✅ 解决方案:添加调试组件
@Component
public class ConfigurationDebugger {
@Autowired
private Environment environment;
@PostConstruct
public void debugConfiguration() {
System.out.println("=== 配置调试信息 ===");
System.out.println("Active profiles: " + Arrays.toString(environment.getActiveProfiles()));
// 检查关键属性
checkProperty("app.feature.enabled");
checkProperty("spring.redis.host");
checkProperty("spring.datasource.url");
}
private void checkProperty(String propertyName) {
String value = environment.getProperty(propertyName);
System.out.println(propertyName + " = " + value);
}
}
14.2 Bean循环依赖问题
// ❌ 问题:循环依赖
@Configuration
public class CircularDependencyConfig {
@Bean
@ConditionalOnBean(ServiceB.class)
public ServiceA serviceA(ServiceB serviceB) {
return new ServiceA(serviceB);
}
@Bean
@ConditionalOnBean(ServiceA.class)
public ServiceB serviceB(ServiceA serviceA) {
return new ServiceB(serviceA);
}
}
// ✅ 解决方案:重新设计依赖关系
@Configuration
public class FixedDependencyConfig {
@Bean
public ServiceA serviceA() {
return new ServiceA();
}
@Bean
@ConditionalOnBean(ServiceA.class)
public ServiceB serviceB(ServiceA serviceA) {
return new ServiceB(serviceA);
}
@Bean
@ConditionalOnBean({ServiceA.class, ServiceB.class})
public ServiceCoordinator serviceCoordinator(ServiceA serviceA, ServiceB serviceB) {
return new ServiceCoordinator(serviceA, serviceB);
}
}
15. 高级主题
15.1 条件注解执行顺序
// 条件注解的执行顺序演示
@Configuration
public class ConditionalOrderDemo {
// 1. 类级别条件首先执行
@ConditionalOnClass(RedisTemplate.class)
static class RedisConfiguration {
// 2. 方法级别条件随后执行
@Bean
@ConditionalOnProperty(prefix = "spring.redis", name = "host")
@ConditionalOnMissingBean
public RedisTemplate<String, Object> redisTemplate() {
return new RedisTemplate<>();
}
}
}
// 自定义条件的优先级控制
public class HighPriorityCondition implements Condition, Ordered {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
System.out.println("执行高优先级条件检查");
return true;
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
15.2 监控和诊断
@Component
public class ConditionalMonitor implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@EventListener(ContextRefreshedEvent.class)
public void reportConditionalBeans() {
System.out.println("=== 条件Bean创建报告 ===");
String[] beanNames = applicationContext.getBeanDefinitionNames();
int conditionalBeanCount = 0;
for (String beanName : beanNames) {
if (isConditionalBean(beanName)) {
conditionalBeanCount++;
System.out.println("条件Bean: " + beanName);
}
}
System.out.println("总共创建了 " + conditionalBeanCount + " 个条件Bean");
}
private boolean isConditionalBean(String beanName) {
try {
BeanDefinition definition = ((ConfigurableApplicationContext) applicationContext)
.getBeanFactory().getBeanDefinition(beanName);
if (definition instanceof AnnotatedBeanDefinition) {
MethodMetadata metadata = ((AnnotatedBeanDefinition) definition).getFactoryMethodMetadata();
return metadata != null && hasConditionalAnnotations(metadata);
}
} catch (Exception e) {
// 忽略异常
}
return false;
}
private boolean hasConditionalAnnotations(MethodMetadata metadata) {
return metadata.isAnnotated(ConditionalOnClass.class.getName()) ||
metadata.isAnnotated(ConditionalOnProperty.class.getName()) ||
metadata.isAnnotated(ConditionalOnBean.class.getName()) ||
metadata.isAnnotated(Conditional.class.getName());
}
}
总结
SpringBoot条件注解是一个强大而灵活的特性,它让我们能够根据不同的条件智能地配置应用程序。通过本教程,您应该已经掌握了:
🎯 核心知识点
- 基础概念 - 理解条件注解的工作原理和执行时机
- 常用注解 - 熟练使用各种内置条件注解
- 自定义条件 - 能够创建符合业务需求的自定义条件
- 最佳实践 - 遵循良好的设计原则和编码规范
- 问题解决 - 能够诊断和解决常见问题
🚀 实际应用
- 多环境管理 - 开发、测试、生产环境的差异化配置
- 功能开关 - 灵活控制功能的启用和禁用
- 降级方案 - 当某些依赖不可用时提供备选方案
- 性能优化 - 避免不必要的Bean创建和初始化
📚 进阶学习建议
- 深入源码 - 研究SpringBoot自动配置的源码实现
- 实践项目 - 在真实项目中应用条件注解
- 性能监控 - 关注条件注解对启动性能的影响
- 社区学习 - 关注Spring社区的最新发展
🌟 关键要点
- 条件注解在应用启动时执行,不是运行时
- 合理组合多个条件注解可以实现复杂的配置逻辑
- 自定义条件类应该尽可能高效,避免昂贵的操作
- 使用
@ConditionalOnMissingBean
提供默认实现是好的实践 - 测试条件注解配置是确保功能正确的重要环节
记住,条件注解不仅仅是技术工具,更是设计思想的体现。合理使用条件注解可以让您的应用更加灵活、可维护和可扩展。