本文目录导读:

我来为您提供一个完整的Java功能开关案例,包含多种实现方案。
基于配置文件的功能开关
1 简单属性配置方案
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class FeatureToggleService {
@Value("${feature.new-search.enabled:true}")
private boolean newSearchEnabled;
@Value("${feature.payment.v2.enabled:false}")
private boolean paymentV2Enabled;
@Value("${feature.recommendation.enabled:true}")
private boolean recommendationEnabled;
public boolean isNewSearchEnabled() {
return newSearchEnabled;
}
public boolean isPaymentV2Enabled() {
return paymentV2Enabled;
}
public boolean isRecommendationEnabled() {
return recommendationEnabled;
}
}
2 application.yml 配置
feature:
new-search:
enabled: true
payment:
v2:
enabled: false
recommendation:
enabled: true
自定义注解功能开关
1 定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FeatureToggle {
String value(); // 功能名称
boolean enabled() default true; // 默认启用
}
2 定义切面
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class FeatureToggleAspect {
private final FeatureToggleService featureToggleService;
public FeatureToggleAspect(FeatureToggleService featureToggleService) {
this.featureToggleService = featureToggleService;
}
@Around("@annotation(featureToggle)")
public Object checkFeatureToggle(ProceedingJoinPoint joinPoint, FeatureToggle featureToggle)
throws Throwable {
String featureName = featureToggle.value();
boolean isEnabled = checkFeatureEnabled(featureName);
if (!isEnabled) {
// 功能关闭时返回默认值或抛出异常
if (featureToggle.enabled()) {
return getDefaultResult(joinPoint);
}
}
return joinPoint.proceed();
}
private boolean checkFeatureEnabled(String featureName) {
// 实际项目中可以调用配置中心或数据库判断
switch (featureName) {
case "new-search":
return featureToggleService.isNewSearchEnabled();
case "payment-v2":
return featureToggleService.isPaymentV2Enabled();
default:
return true;
}
}
private Object getDefaultResult(ProceedingJoinPoint joinPoint) {
// 返回方法的默认返回值
Class<?> returnType = ((org.aspectj.lang.reflect.MethodSignature)
joinPoint.getSignature()).getReturnType();
if (returnType.equals(boolean.class)) {
return false;
} else if (returnType.equals(int.class)) {
return 0;
} else if (returnType.equals(String.class)) {
return "";
} else if (returnType.isPrimitive()) {
return 0;
} else {
return null;
}
}
}
基于数据库的动态功能开关
1 实体类
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.time.LocalDateTime;
@Entity
@Table(name = "feature_toggles")
public class FeatureToggle {
@Id
private String featureName;
private boolean enabled;
private String description;
private LocalDateTime lastModified;
// getters and setters
public String getFeatureName() {
return featureName;
}
public void setFeatureName(String featureName) {
this.featureName = featureName;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public LocalDateTime getLastModified() {
return lastModified;
}
public void setLastModified(LocalDateTime lastModified) {
this.lastModified = lastModified;
}
}
2 服务层实现
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class DynamicFeatureToggleService {
private final FeatureToggleRepository featureToggleRepository;
public DynamicFeatureToggleService(FeatureToggleRepository featureToggleRepository) {
this.featureToggleRepository = featureToggleRepository;
}
@Cacheable(value = "featureToggles", key = "#featureName")
public boolean isFeatureEnabled(String featureName) {
return featureToggleRepository.findById(featureName)
.map(FeatureToggle::isEnabled)
.orElse(true); // 默认启用
}
@CacheEvict(value = "featureToggles", key = "#featureName")
public void updateFeatureToggle(String featureName, boolean enabled) {
FeatureToggle toggle = featureToggleRepository.findById(featureName)
.orElseGet(() -> {
FeatureToggle newToggle = new FeatureToggle();
newToggle.setFeatureName(featureName);
return newToggle;
});
toggle.setEnabled(enabled);
toggle.setLastModified(LocalDateTime.now());
featureToggleRepository.save(toggle);
}
}
完整示例应用
1 业务代码示例
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private FeatureToggleService featureToggleService;
@Autowired
private DynamicFeatureToggleService dynamicFeatureToggleService;
@FeatureToggle(value = "new-search", enabled = true)
public List<String> searchProducts(String keyword) {
// 旧版搜索逻辑
return oldSearchProducts(keyword);
}
public List<String> newSearchProducts(String keyword) {
if (!dynamicFeatureToggleService.isFeatureEnabled("new-search")) {
return oldSearchProducts(keyword);
}
// 新版搜索逻辑
return newSearchLogic(keyword);
}
public PaymentResult processPayment(PaymentRequest request) {
if (featureToggleService.isPaymentV2Enabled()) {
return processPaymentV2(request);
} else {
return processPaymentV1(request);
}
}
@FeatureToggle(value = "recommendation", enabled = true)
public List<Product> getRecommendations(Long userId) {
// 推荐逻辑
if (!featureToggleService.isRecommendationEnabled()) {
return Collections.emptyList();
}
return getRecommendationLogic(userId);
}
private List<String> oldSearchProducts(String keyword) {
// 旧版搜索实现
return Arrays.asList("旧版搜索结果");
}
private List<String> newSearchLogic(String keyword) {
// 新版搜索实现
return Arrays.asList("新版搜索结果");
}
}
2 配置类
import org.springframework.context.annotation.Configuration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
@Configuration
@EnableCaching
@EnableScheduling
public class FeatureConfig {
// 定期刷新功能开关
@Scheduled(fixedDelay = 60000) // 每分钟刷新一次
public void refreshFeatureToggles() {
// 清理缓存,从数据库重新加载
cacheManager.getCache("featureToggles").clear();
}
}
3 测试类
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class FeatureToggleTest {
@Autowired
private OrderService orderService;
@Autowired
private FeatureToggleService featureToggleService;
@Test
public void testFeatureToggle() {
// 测试属性配置
System.out.println("New Search Enabled: " +
featureToggleService.isNewSearchEnabled());
System.out.println("Payment V2 Enabled: " +
featureToggleService.isPaymentV2Enabled());
// 测试不同分支
List<String> results = orderService.newSearchProducts("test");
System.out.println("Search results: " + results);
}
}
使用第三方库
1 Togglz 集成示例
import org.togglz.core.Feature;
import org.togglz.core.annotation.EnabledByDefault;
import org.togglz.core.annotation.Label;
import org.togglz.core.context.FeatureContext;
public enum AppFeatures implements Feature {
@EnabledByDefault
@Label("新搜索功能")
NEW_SEARCH,
@Label("支付V2")
PAYMENT_V2,
@Label("推荐系统")
RECOMMENDATION;
public boolean isActive() {
return FeatureContext.getFeatureManager().isActive(this);
}
}
最佳实践建议
- 命名规范:功能开关名称应有意义且易于理解
- 默认值设置:新功能默认关闭,稳定后逐步开放
- 监控告警:记录功能开关使用情况
- 灰度发布:支持按用户比例或条件启用
- 定期清理:移除已稳定功能的老开关
- 文档记录:保持功能开关文档更新
这个案例提供了从简单到复杂的多种实现方案,您可以根据项目需求选择合适的方案。