如何优雅实现基于类非空属性生成URL字符串(多实现类场景)
优化Request接口实现类的URL拼接方案
方案一:反射+自定义注解
通过自定义注解标记属性对应的URL参数名和是否必填,利用反射统一遍历属性、校验必填项并拼接非空参数。这种方式能彻底摆脱if-else,属性增减只需添加/移除注解即可。
步骤1:定义参数注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface RequestParam { // URL中对应的参数名 String name(); // 是否为必填参数,默认非必填 boolean required() default false; }
步骤2:定义Request接口
public interface Request { String getUrl(); }
步骤3:实现具体请求类
public class UserRequest implements Request { @RequestParam(name = "userId", required = true) private Long userId; @RequestParam(name = "userName") private String userName; @RequestParam(name = "age") private Integer age; // 构造器、Getter/Setter省略 @Override public String getUrl() { return RequestUrlBuilder.build(this); } }
步骤4:编写通用URL构建工具类
public class RequestUrlBuilder { public static String build(Request request) { StringBuilder urlBuilder = new StringBuilder("http://example.com/api"); Field[] fields = request.getClass().getDeclaredFields(); List<String> paramList = new ArrayList<>(); for (Field field : fields) { RequestParam paramAnno = field.getAnnotation(RequestParam.class); if (paramAnno == null) continue; field.setAccessible(true); Object value; try { value = field.get(request); } catch (IllegalAccessException e) { throw new RuntimeException("读取属性失败: " + field.getName(), e); } // 校验必填参数 if (paramAnno.required() && value == null) { throw new IllegalArgumentException("必填参数[" + paramAnno.name() + "]不可为空"); } // 非空参数加入拼接列表 if (value != null) { paramList.add(paramAnno.name() + "=" + value.toString()); } } if (!paramList.isEmpty()) { urlBuilder.append("?").append(String.join("&", paramList)); } return urlBuilder.toString(); } }
方案二:Map映射+Stream过滤
让每个Request实现类自行维护参数名与属性值的映射关系,利用Stream过滤非空值后统一拼接。这种方式无反射开销,逻辑直观,适合对性能敏感的场景。
步骤1:改造Request接口
public interface Request { // 返回基础URL String getBaseUrl(); // 返回参数名与属性值的映射(需自行处理必填校验) Map<String, Object> getParamMap(); // 默认实现URL拼接逻辑 default String getUrl() { StringBuilder urlBuilder = new StringBuilder(getBaseUrl()); List<String> paramList = getParamMap().entrySet().stream() .filter(entry -> entry.getValue() != null) .map(entry -> entry.getKey() + "=" + entry.getValue().toString()) .collect(Collectors.toList()); if (!paramList.isEmpty()) { urlBuilder.append("?").append(String.join("&", paramList)); } return urlBuilder.toString(); } }
步骤2:实现具体请求类
public class UserRequest implements Request { private Long userId; // 必填 private String userName; private Integer age; // 构造器、Getter/Setter省略 @Override public String getBaseUrl() { return "http://example.com/api/user"; } @Override public Map<String, Object> getParamMap() { // 手动校验必填参数 if (userId == null) { throw new IllegalArgumentException("userId不可为空"); } Map<String, Object> paramMap = new HashMap<>(); paramMap.put("userId", userId); paramMap.put("userName", userName); paramMap.put("age", age); return paramMap; } }
方案对比
- 反射+注解:代码侵入性低,属性变更只需修改注解,适合属性频繁变动的场景;缺点是反射存在轻微性能开销。
- Map映射+Stream:无反射开销,性能更优,每个类的参数逻辑自主可控;缺点是属性变更需修改
getParamMap()方法,但比if-else更易维护。
内容的提问来源于stack exchange,提问作者EraZer
相关产品推荐
相关产品推荐

