java自定义注解本身不执行启停逻辑,需结合反射、配置中心与aop或@conditional实现动态控制;核心是注解声明意图,运行时机制响应意图。

Java 自定义注解本身不直接执行启用/关闭逻辑,它只是元数据标记;真正实现动态启停,需要结合注解 + 反射 + 条件判断(如配置中心、开关变量、Spring 的 @Conditional 或 AOP 拦截)来完成。核心思路是:用注解声明意图,用运行时机制读取并响应这个意图。
定义启用/关闭注解
先创建一个自定义注解,用于标记哪些类、方法或模块需要受控启停:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ModuleToggle {
String value() default ""; // 模块标识,如 "payment"、"sms"
boolean enabled() default true; // 默认启用,但可被外部覆盖
}
这个注解支持类和方法级别,value() 用于区分不同业务模块,enabled() 是初始状态(仅作参考,实际以运行时配置为准)。
配合配置中心或本地开关做动态判定
不能硬编码开关值,需从外部(如 Nacos、Apollo、Spring Boot 的 @Value 或 Environment)读取实时配置:
- 在 Spring 环境中,推荐用
@ConfigurationProperties或@Value("${module.payment.enabled:true}")绑定开关 - 定义一个工具类统一判断模块是否开启:
public static boolean isModuleEnabled(String moduleKey) {<br> return Boolean.parseBoolean(environment.getProperty("module." + moduleKey + ".enabled", "true"));<br> } - 避免每次反射都查配置,可加简单缓存(如
ConcurrentMap),键为moduleKey,值为布尔结果
在切面或代理中拦截并控制执行
最常用且解耦的方式是使用 Spring AOP,在方法执行前检查注解与开关状态:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
@Aspect
@Component
public class ModuleToggleAspect {
@Around("@annotation(toggle)")
public Object checkModuleEnable(ProceedingJoinPoint joinPoint, ModuleToggle toggle) throws Throwable {
String module = StringUtils.defaultIfBlank(toggle.value(), toggle.getClass().getSimpleName());
if (!ModuleToggleUtils.isModuleEnabled(module)) {
throw new UnsupportedOperationException("模块 [" + module + "] 当前已关闭");
// 或返回默认值、空结果、跳过执行等,按业务定
}
return joinPoint.proceed();
}
}
这样,只要方法上加了 @ModuleToggle(value = "order"),AOP 就会自动校验 module.order.enabled 配置项,决定是否放行。
对 Bean 生命周期做条件注册(适用于类级启停)
若要整个服务类(如 @Service)不加载,可用 Spring 的条件化注册:
- 让目标类实现
InitializingBean或加@PostConstruct,启动时检查开关并抛异常(不推荐,破坏 IOC) - 更规范的做法:配合
@ConditionalOnProperty或自定义@Conditional注解
例如:@ConditionalOnProperty(name = "module.sms.enabled", havingValue = "true", matchIfMissing = true) - 或者写一个
Condition实现类,读取ModuleToggle和配置,决定是否创建 Bean
这种方式在容器启动阶段就过滤掉不需要的 Bean,比运行时拦截更轻量,适合模块级整体开关。
不复杂但容易忽略:注解只是“声明”,真正的开关逻辑必须落在运行时执行路径中——要么靠 AOP 拦截方法调用,要么靠条件化注册 Bean,再配合可动态刷新的配置源,才能做到真正的“动态”启停。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










