Java案例如何实现服务随境?

wen python案例 1

本文目录导读:

Java案例如何实现服务随境?

  1. 服务随环境切换(环境配置)
  2. 服务随上下文变化(策略模式)
  3. 实战:动态租户切换(多租户)
  4. 选择建议

在Java中实现“服务随境”通常有两种理解:

  1. 服务随环境切换:开发、测试、生产等不同环境自动切换配置
  2. 服务随上下文变化:运行时根据不同上下文动态调用不同实现

下面分别给出实现方案。

服务随环境切换(环境配置)

方案1:Spring Boot多环境配置

application.yml 多文档块方式:

spring:
  profiles:
    active: @profile.active@  # Maven打包时动态注入
---
# 开发环境
spring:
  profiles: dev
server:
  port: 8080
database:
  url: jdbc:mysql://localhost:3306/dev_db
---
# 生产环境
spring:
  profiles: prod
server:
  port: 80
database:
  url: jdbc:mysql://prod-server:3306/prod_db

或者多配置文件方式:

application-dev.yml
application-prod.yml

启动指定环境:

java -jar app.jar --spring.profiles.active=prod

Maven配置动态环境:

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <profile.active>dev</profile.active>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <profile.active>prod</profile.active>
        </properties>
    </profile>
</profiles>

方案2:环境变量+配置中心

使用Nacos或Apollo配置中心,运行时动态刷新配置:

@RefreshScope
@Service
public class DynamicService {
    @Value("${service.mode:normal}")
    private String serviceMode;
    public void execute() {
        if ("fast".equals(serviceMode)) {
            // 快速模式
        } else {
            // 常规模式
        }
    }
}

服务随上下文变化(策略模式)

方案1:Spring IOC + 策略模式

// 定义策略接口
public interface PaymentStrategy {
    String getType();
    void pay(BigDecimal amount);
}
// 实现不同策略
@Service
@Order(1)
public class WechatPayStrategy implements PaymentStrategy {
    @Override
    public String getType() { return "wechat"; }
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("微信支付:" + amount);
    }
}
@Service
@Order(2)  
public class AlipayStrategy implements PaymentStrategy {
    @Override
    public String getType() { return "alipay"; }
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("支付宝支付:" + amount);
    }
}
// 策略上下文
@Component
public class PaymentContext {
    @Autowired
    private List<PaymentStrategy> strategies;
    public PaymentStrategy getStrategy(String type) {
        return strategies.stream()
            .filter(s -> s.getType().equals(type))
            .findFirst()
            .orElseThrow(() -> new RuntimeException("不支持的支付类型"));
    }
}
// 使用
@RestController
public class PaymentController {
    @Autowired
    private PaymentContext context;
    @PostMapping("/pay")
    public String pay(@RequestParam String type, @RequestParam BigDecimal amount) {
        PaymentStrategy strategy = context.getStrategy(type);
        strategy.pay(amount);
        return "success";
    }
}

方案2:Map注入 + @PostConstruct

@Component
public class ServiceRouter {
    @Autowired
    private List<ServiceHandler> handlers;
    private Map<String, ServiceHandler> handlerMap;
    @PostConstruct
    public void init() {
        handlerMap = handlers.stream()
            .collect(Collectors.toMap(ServiceHandler::getType, Function.identity()));
    }
    public ServiceHandler getHandler(String type) {
        return handlerMap.get(type);
    }
}

方案3:注解+反射(更灵活)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceType {
    String value();
}
@ServiceType("vip")
@Service
public class VipUserService implements UserService {
    // ...
}
@Component
public class ServiceFactory {
    @Autowired
    private ApplicationContext context;
    public <T> T getService(String type, Class<T> clazz) {
        Map<String, Object> beans = context.getBeansWithAnnotation(ServiceType.class);
        for (Object bean : beans.values()) {
            ServiceType annotation = bean.getClass().getAnnotation(ServiceType.class);
            if (annotation.value().equals(type)) {
                return clazz.cast(bean);
            }
        }
        return null;
    }
}

实战:动态租户切换(多租户)

@Component
public class TenantContextHolder {
    private static final ThreadLocal<String> CURRENT_TENANT = new ThreadLocal<>();
    public static void setTenant(String tenant) {
        CURRENT_TENANT.set(tenant);
    }
    public static String getTenant() {
        return CURRENT_TENANT.get();
    }
    public static void clear() {
        CURRENT_TENANT.remove();
    }
}
// 拦截器自动设置租户
@Component
public class TenantInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String tenant = request.getHeader("X-Tenant-ID");
        TenantContextHolder.setTenant(tenant);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                                Object handler, Exception ex) {
        TenantContextHolder.clear();
    }
}
// 数据源动态切换
@Aspect
@Component
@Order(Ordered.LOWEST_PRECEDENCE - 1)
public class DataSourceAspect {
    @Around("@annotation(switchDataSource)")
    public Object around(ProceedingJoinPoint point, SwitchDataSource switchDataSource) throws Throwable {
        String tenant = TenantContextHolder.getTenant();
        DynamicDataSource.setDataSource(tenant);
        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
        }
    }
}

选择建议

场景 推荐方案
不同环境配置切换 Spring Profiles + 配置中心
运行时代码逻辑切换 策略模式 + Spring IOC
多租户数据隔离 ThreadLocal + 动态数据源
A/B测试流量切换 流量染色 + 灰度发布平台

最佳实践:将环境切换和业务逻辑切换分离,环境切换通过配置/部署实现,业务逻辑切换通过代码设计模式实现,避免混合使用导致维护困难。

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