本文目录导读:

我来为您提供一个完整的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);
}
}
最佳实践建议
- 命名规范:功能开关名称应有意义且易于理解
- 默认值设置:新功能默认关闭,稳定后逐步开放
- 监控告警:记录功能开关使用情况
- 灰度发布:支持按用户比例或条件启用
- 定期清理:移除已稳定功能的老开关
- 文档记录:保持功能开关文档更新
这个案例提供了从简单到复杂的多种实现方案,您可以根据项目需求选择合适的方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播