添加枚举字段注解扫描注入

This commit is contained in:
2025-05-12 18:14:38 +08:00
parent a872e4e1d1
commit 07b0c37c26
3 changed files with 193 additions and 1 deletions

View File

@@ -18,7 +18,11 @@ public class ReflectionsConfig {
@Bean("reflections")
Reflections reflections() {
return new Reflections(new ConfigurationBuilder()
.addScanners(Scanners.MethodsAnnotated, Scanners.SubTypes, Scanners.TypesAnnotated)
.addScanners(
Scanners.MethodsAnnotated,
Scanners.SubTypes,
Scanners.FieldsAnnotated,
Scanners.TypesAnnotated)
.forPackages("quant.rich.emoney"));
}

View File

@@ -0,0 +1,75 @@
package quant.rich.emoney.entity.config;
import java.net.Proxy;
import java.time.LocalDateTime;
import java.util.Objects;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.beans.factory.annotation.Autowired;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import quant.rich.emoney.entity.config.DeviceInfoConfig.DeviceInfo;
import quant.rich.emoney.interceptor.EnumOptionsInterceptor.EnumOptions;
import quant.rich.emoney.interfaces.ConfigInfo;
import quant.rich.emoney.interfaces.IConfig;
import quant.rich.emoney.interfaces.ValidEmoneyRequestConfig;
import quant.rich.emoney.patch.okhttp.PatchOkHttp;
import quant.rich.emoney.util.EncryptUtils;
import quant.rich.emoney.util.SpringContextHolder;
import quant.rich.emoney.util.TextUtils;
/**
* 独立出来一个代理设置的原因是后续可能需要做一个代理池,这样的话独立配置比较适合后续扩展
*/
@Data
@Accessors(chain = true)
@Slf4j
@ValidEmoneyRequestConfig
@ConfigInfo(field = "proxy", name = "代理设置", initDefault = true)
public class ProxyConfig implements IConfig<ProxyConfig> {
/**
* 代理类型
*/
@EnumOptions("ProxyTypeEnum")
private Proxy.Type proxyType = Proxy.Type.DIRECT;
/**
* 代理主机
*/
private String proxyHost = "";
/**
* 代理端口
*/
private Integer proxyPort = 0;
/**
* 是否忽略 HTTPS 证书校验
*/
private Boolean ignoreHttpsVerification = false;
public void afterBeanInit() {
}
public ProxyConfig() {}
}

View File

@@ -0,0 +1,113 @@
package quant.rich.emoney.interceptor;
import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AliasFor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.*;
import jakarta.servlet.http.*;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.*;
import java.util.*;
import java.util.Map.Entry;
import java.lang.annotation.*;
@Component
@Slf4j
public class EnumOptionsInterceptor implements HandlerInterceptor {
@Autowired
Reflections reflections;
private Map<String, Map<String, Enum<?>>> options = null;
private Map<String, String> optionNameMap = new HashMap<>();
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 只在 ControllerHandlerMethod里才做注入
if (!(handler instanceof HandlerMethod)) {
// 静态资源、图片、css、js 都会被 ResourceHttpRequestHandler 处理,
// 这里一律跳过
return true;
}
// 排除 @ResponseBody/json 接口
HandlerMethod hm = (HandlerMethod) handler;
if (hm.hasMethodAnnotation(ResponseBody.class)
|| hm.getBeanType().isAnnotationPresent(RestController.class)) {
return true;
}
if (options == null) {
options = new HashMap<>();
Set<Field> enumOptionFields = reflections.getFieldsAnnotatedWith(EnumOptions.class);
for (Field f : enumOptionFields) {
if (!f.isAnnotationPresent(EnumOptions.class)) continue;
if (!Enum.class.isAssignableFrom(f.getType())) continue;
// 拿到注解和名前缀
EnumOptions anno = f.getAnnotation(EnumOptions.class);
String prefix = anno.value().isBlank() ?
f.getName() + "Enum" :
anno.value();
prefix = prefix.toUpperCase().charAt(0) + prefix.substring(1);
if (optionNameMap.containsKey(prefix)) {
log.warn("EnumOption name {}:{} has already been taken by {}, please check",
prefix, f.getType().getName(), optionNameMap.get(prefix));
continue;
}
optionNameMap.put(prefix, f.getType().getName());
// enum 值列表
@SuppressWarnings("unchecked")
Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) f.getType();
Enum<?>[] constants = enumType.getEnumConstants();
// 构造 Map<name, constant>
Map<String, Enum<?>> optionsMap = new LinkedHashMap<>();
for (Enum<?> c : constants) {
optionsMap.put(c.name(), c);
}
// 放到 Model 里
options.put(prefix, optionsMap);
}
}
for (Entry<String, Map<String, Enum<?>>> entry : options.entrySet()) {
request.setAttribute(entry.getKey(), entry.getValue());
log.debug("Inject enums {}: {} to request {}",
entry.getKey(), optionNameMap.get(entry.getKey()),
request.getRequestURI());
}
return true;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public static @interface EnumOptions {
/**
* 注入到 Model 时用的属性名前缀,
* 默认:字段名 + "Options"
*/
String value() default "";
}
}