本文目录导读:

在Java中实现“服务随境”通常有两种理解:
- 服务随环境切换:开发、测试、生产等不同环境自动切换配置
- 服务随上下文变化:运行时根据不同上下文动态调用不同实现
下面分别给出实现方案。
服务随环境切换(环境配置)
方案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测试流量切换 | 流量染色 + 灰度发布平台 |
最佳实践:将环境切换和业务逻辑切换分离,环境切换通过配置/部署实现,业务逻辑切换通过代码设计模式实现,避免混合使用导致维护困难。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播