- 以下みたいな
ID<T>
や Name<T>
みたいな値っぽいクラスのインスタンスを Controller の引数として受け取りたい。
ID<T>
みたいなクラスは随時増えるので、増えるたびに Converter まで作るのを避けるために AbstractAtom
の ConverterFactory
を定義して一度で済ませたい。
ConverterFactory
どうやって書けばいいのん?
package jp.t2v;
public class ID<T> extends ComparableAbstractAtom<Integer, ID<T>> {
private ID(final Integer value) {
super(value);
}
public static <U> ID<U> of(final Integer value) {
if (value <= 0) throw new IllegalArgumentException("value should be positive. " + value);
return new ID<>(value);
}
private static final ID<Object> NOT_ASSIGNED = new ID<>(-1);
@SuppressWarnings("unchecked")
public static <U> ID<U> notAssigned() {
return (ID<U>) NOT_ASSIGNED;
}
}
package jp.t2v;
public class Name<T> extends ComparableAbstractAtom<String, Name<T>> {
private Name(String value) {
super(value);
}
public static <U> Name<U> of(final String value) {
return new Name<>(value);
}
}
package jp.t2v;
import java.io.Serializable;
import java.util.Objects;
public abstract class AbstractAtom<T extends Serializable> implements Serializable {
private static final long serialVersionUID = 1;
protected final T value;
protected AbstractAtom(final T value) {
Objects.requireNonNull(value, "value must be non-null");
this.value = value;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof AbstractAtom)) return false;
AbstractAtom<?> that = (AbstractAtom<?>) o;
return that.canEqual(this) && value.equals(that.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
protected boolean canEqual(final AbstractAtom<?> self) {
return getClass().equals(self.getClass());
}
public T getValue() {
return value;
}
}
package jp.t2v;
import java.io.Serializable;
public abstract class ComparableAbstractAtom<T extends Serializable & Comparable<? super T>, REPR extends ComparableAbstractAtom<T, REPR>>
extends AbstractAtom<T> implements Comparable<REPR> {
protected ComparableAbstractAtom(final T value) {
super(value);
}
@Override
public int compareTo(REPR o) {
return this.value.compareTo(o.value);
}
}
package jp.t2v;
import jp.t2v.User;
import jp.t2v.ID;
import jp.t2v.Organization;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/rest/v1.0/idtest")
public class IdTestResource {
@RequestMapping("/orgs/{organizationId}/users/{userId}")
public ResponseEntity<String> test(
@PathVariable ID<Organization> organizationId,
@PathVariable ID<User> userId
) {
return ResponseEntity.ok("Okey-Dokey");
}
}
package jp.t2v;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class FormatterConfig extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
???
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
???
}
}