Java功能开关案例

wen java案例 1

本文目录导读:

Java功能开关案例

  1. 基于配置文件的功能开关
  2. 自定义注解功能开关
  3. 基于数据库的动态功能开关
  4. 完整示例应用
  5. 使用第三方库
  6. 最佳实践建议

我来为您提供一个完整的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);
    }
}

最佳实践建议

  1. 命名规范:功能开关名称应有意义且易于理解
  2. 默认值设置:新功能默认关闭,稳定后逐步开放
  3. 监控告警:记录功能开关使用情况
  4. 灰度发布:支持按用户比例或条件启用
  5. 定期清理:移除已稳定功能的老开关
  6. 文档记录:保持功能开关文档更新

这个案例提供了从简单到复杂的多种实现方案,您可以根据项目需求选择合适的方案。

抱歉,评论功能暂时关闭!