Using Annotations and Reflection to Eliminate Duplicate Java Backend Code

This article demonstrates how to replace repetitive Java backend code for constructing fixed‑length API request strings by defining POJOs, custom annotations, and a reflection‑based utility that formats parameters, pads strings, handles numeric and monetary fields, and appends an MD5 signature, thereby improving maintainability and reducing bugs.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Using Annotations and Reflection to Eliminate Duplicate Java Backend Code

Case Analysis

Assume a bank provides API interfaces that require parameters to be serialized into a single fixed‑length string rather than JSON. The parameters must be concatenated in a specific order, with each field padded to a defined length.

String‑type parameters that do not reach the required length are right‑padded with underscores, keeping the content left‑aligned.

Numeric‑type parameters that do not reach the required length are left‑padded with zeros, keeping the number right‑aligned.

Monetary values are rounded down to two decimal places (cents) and treated as numeric fields with left‑padding.

The final concatenated string is hashed with MD5 to produce a signature.

Initial Code Implementation

public class BankService {

    //创建用户方法
    public static String createUser(String name, String identity, String mobile, int age) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        //字符串靠左,多余的地方填充_
        stringBuilder.append(String.format("%-10s", name).replace(' ', '_'));
        //字符串靠左,多余的地方填充_
        stringBuilder.append(String.format("%-18s", identity).replace(' ', '_'));
        //数字靠右,多余的地方用0填充
        stringBuilder.append(String.format("%05d", age));
        //字符串靠左,多余的地方用_填充
        stringBuilder.append(String.format("%-11s", mobile).replace(' ', '_'));
        //最后加上MD5作为签名
        stringBuilder.append(DigestUtils.md2Hex(stringBuilder.toString()));
        return Request.Post("http://localhost:45678/reflection/bank/createUser")
                .bodyString(stringBuilder.toString(), ContentType.APPLICATION_JSON)
                .execute().returnContent().asString();
    }

    //支付方法
    public static String pay(long userId, BigDecimal amount) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        //数字靠右,多余的地方用0填充
        stringBuilder.append(String.format("%020d", userId));
        //金额向下舍入2位到分,以分为单位,作为数字靠右,多余的地方用0填充
        stringBuilder.append(String.format("%010d", amount.setScale(2, RoundingMode.DOWN).multiply(new BigDecimal("100")).longValue()));
        //最后加上MD5作为签名
        stringBuilder.append(DigestUtils.md2Hex(stringBuilder.toString()));
        return Request.Post("http://localhost:45678/reflection/bank/pay")
                .bodyString(stringBuilder.toString(), ContentType.APPLICATION_JSON)
                .execute().returnContent().asString();
    }
}

This implementation meets the basic requirements but suffers from several problems: duplicated logic, hard‑coded field order, difficulty keeping method signatures aligned with the API specification, and lack of a clear, maintainable way to verify parameters.

Optimizing with Interfaces and Reflection

1.3.1 Define POJO Classes for All API Parameters

@Data
public class CreateUserAPI {
    private String name;
    private String identity;
    private String mobile;
    private int age;
}

1.3.2 Define Custom Annotations

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
public @interface BankAPI {
    String desc() default "";
    String url() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface BankAPIField {
    int order() default -1;
    int length() default -1;
    String type() default "";
}

1.3.3 Reflection‑Based Dynamic Parameter Assembly

private static String remoteCall(AbstractAPI api) throws IOException {
    //从BankAPI注解获取请求地址
    BankAPI bankAPI = api.getClass().getAnnotation(BankAPI.class);
    StringBuilder stringBuilder = new StringBuilder();
    Arrays.stream(api.getClass().getDeclaredFields())
        .filter(field -> field.isAnnotationPresent(BankAPIField.class))
        .sorted(Comparator.comparingInt(f -> f.getAnnotation(BankAPIField.class).order()))
        .peek(f -> f.setAccessible(true))
        .forEach(field -> {
            BankAPIField bankAPIField = field.getAnnotation(BankAPIField.class);
            Object value = "";
            try { value = field.get(api); } catch (IllegalAccessException e) { e.printStackTrace(); }
            switch (bankAPIField.type()) {
                case "S": {
                    stringBuilder.append(String.format("%-" + bankAPIField.length() + "s", value.toString()).replace(' ', '_'));
                    break;
                }
                case "N": {
                    stringBuilder.append(String.format("%" + bankAPIField.length() + "s", value.toString()).replace(' ', '0'));
                    break;
                }
                case "M": {
                    if (!(value instanceof BigDecimal))
                        throw new RuntimeException(String.format("%s 的 %s 必须是BigDecimal", api, field));
                    stringBuilder.append(String.format("%0" + bankAPIField.length() + "d",
                        ((BigDecimal) value).setScale(2, RoundingMode.DOWN).multiply(new BigDecimal("100")).longValue()));
                    break;
                }
                default:
                    break;
            }
        });
    //签名逻辑
    stringBuilder.append(DigestUtils.md2Hex(stringBuilder.toString()));
    String param = stringBuilder.toString();
    long begin = System.currentTimeMillis();
    String result = Request.Post("http://localhost:45678/reflection" + bankAPI.url())
            .bodyString(param, ContentType.APPLICATION_JSON)
            .execute().returnContent().asString();
    log.info("调用银行API {} url:{} 参数:{} 耗时:{}ms", bankAPI.desc(), bankAPI.url(), param, System.currentTimeMillis() - begin);
    return result;
}

By using reflection to obtain class information at runtime, the assembly process becomes generic, centralized, and free of repetitive code, which greatly simplifies development and reduces the chance of bugs.

1.3.4 Application in Code

@BankAPI(url = "/bank/createUser", desc = "创建用户接口")
@Data
public class CreateUserAPI extends AbstractAPI {
    @BankAPIField(order = 1, type = "S", length = 10)
    private String name;
    @BankAPIField(order = 2, type = "S", length = 18)
    private String identity;
    @BankAPIField(order = 4, type = "S", length = 11) // order must follow API spec
    private String mobile;
    @BankAPIField(order = 3, type = "N", length = 5)
    private int age;
}

@BankAPI(url = "/bank/pay", desc = "支付接口")
@Data
public class PayAPI extends AbstractAPI {
    @BankAPIField(order = 1, type = "N", length = 20)
    private long userId;
    @BankAPIField(order = 2, type = "M", length = 10)
    private BigDecimal amount;
}

The annotated classes together with the remoteCall utility allow developers to define API parameters declaratively and let the framework handle formatting, padding, and signing automatically.

Source: https://llchen60.com/利用注解-反射消除重复代码/

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaReflectionCode RefactoringAPIannotations
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.