Javaのアノテーションを作ってみる

列挙型作成

  • 型をenumとして宣言することで列挙型となる
  • 取りうる値は,区切りで並べればよい
public enum FieldType {
  Number,
  Text,
  List
}

アノテーション作成

  • 型を@interfaceとして宣言することでアノテーションとなる
  • 保持期間は@Retentionで指定
  • 付与可能な対象は@Targetで指定
  • フィールドのデフォルト値はdefaultで指定
import static java.lang.annotation.ElementType.FIELD;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
import static xxx.FieldType.Text;
import xxx.FieldType;

@Retention(RUNTIME)
@Target(FIELD)
public @interface Attr {
    int length() default 0;
    int size() default 1;
    boolean isDummy() default false;
    FieldType type() default Text;
}

アノテーションを付与

  • @Targetに対応した要素の前にアノテーションを記載する
@Attr(length=10, type=Number, isDummy=false)
String value1;
@Attr(length=3、size=10, type=List, isDummy=false)
SampleItem[] items;

アノテーションの取得

  • アノテーションはリフレクションを使い取得する
import java.lang.reflect.Field;
:
// 全フィールド取得
Field[] fields = this.getClass().getDeclaredFields();
// 指定フィールド取得
Field field = this.getClass().getDeclaredField("フィールド名");
:
Attr attr = field.getAnnotation(Attr.class);

実践

Controller

package com.example.demo.controller;

import com.example.models.SampleData;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api/v1")
public class SampleController {

    @RequestMapping("sample")
    public Object get(@RequestBody @Validated SampleData data, BindingResult result) {
        if (result.hasErrors()) {
            return result.getAllErrors();
        }
        return "[" + data.toFixedString() + "]";
    }
}

DTOの抽象クラス

package com.example.models;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.HashMap;

public abstract class BaseData {
    private Map<String, Attr> attrMap = createMap();

    private Map<String, Attr> createMap() {
        Map<String, Attr> attrMap = new HashMap<String, Attr>();
        Field[] fields = this.getClass().getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            Attr attr = field.getAnnotation(Attr.class);
            attrMap.put(field.getName(), attr);
        }
        return attrMap;
    }

    abstract public String toFixedString();

    public String toFixedString(String name, String value) {
        Attr attr = attrMap.get(name);
        if (value == null) {
            return toFixedBlank(attr);
        }
        switch (attr.type()) {
            case Text:
                return String.format("%-" + attr.length() + "s", value);
            case Number:
                return String.format("%" + attr.length() + "s", value).replace(" ", "0");
            default:
                return "<error>";
        }
    }

    public String toFixedString(String name, BaseData[] items) {
        Attr attr = attrMap.get(name);
        if (items == null) {
            return toFixedBlank(attr);
        }
        StringBuilder sb = new StringBuilder();
        String blank = String.format("%" + attr.length() + "s", " ");
        for (int i = 0; i < attr.size(); i++) {
            if (items == null || i >= items.length) {
                sb.append(blank);
            } else {
                sb.append(items[i].toFixedString());
            }
        }
        return sb.toString();
    }

    private String toFixedBlank(Attr attr) {
        return String.format("%" + attr.length() * attr.size() + "s", " ");
    }
}

DTO(親)

package com.example.models;

import static com.example.models.FieldType.*;
import javax.validation.Valid;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Length;
import lombok.Data;

@Data
public class SampleData extends BaseData {
    @Length(max=10)
    @Attr(length=10, type=Text, isDummy=false)
    public String value1;

    @Length(max=5)
    @Pattern(regexp="[0-9]+")
    @Attr(length=5, type=Number, isDummy=true)
    public String value2;

    @Valid
    @Size(max=3)
    @Attr(length=SampleItem._length, size=3, type=List, isDummy=false)
    SampleItem[] items;

    public SampleItem[] getItems() { return this.items; }
    public void setItems(SampleItem[] items) { this.items = items; }

    public String toFixedString() {
        StringBuilder sb = new StringBuilder();
        sb.append(toFixedString("value1", value1));
        sb.append(toFixedString("value2", value2));
        sb.append(toFixedString("items", items));
        return sb.toString();
    }    
}

DTO(子)

package com.example.models;

import static com.example.models.FieldType.*;
import org.hibernate.validator.constraints.Length;
import lombok.Data;

@Data
public class SampleItem extends BaseData {
    public static final int _length = 18;

    @Length(max=9)
    @Pattern(regexp="[0-9]+")
    @Attr(length=9, type=Number, isDummy=false)
    String valueA;

    @Length(max=9)
    @Attr(length=9, type=Text, isDummy=true)
    String valueB;

    public String toFixedString() {
        StringBuilder sb = new StringBuilder();
        sb.append(toFixedString("valueA", valueA));
        sb.append(toFixedString("valueB", valueB));
        return sb.toString();
    }

    public String getValueA() { return this.valueA; }
    public void setValueA(String valueA) { this.valueA = valueA; }

    public String getValueB() { return this.valueB; }
    public void setValueB(String valueB) { this.valueB = valueB; }
}
タイトルとURLをコピーしました