Bạn có thể tạo chú thích tùy chỉnh. Tôi sẽ không đi quá nhiều về cách thực hiện, bạn có thể xem this post hoặc this post. Về cơ bản nó dựa trên một cơ sở hạ tầng khác với tiêm phụ thuộc thông thường với Jersey. Bạn có thể xem this package từ dự án Jersey. Đây là nơi tất cả các nhà cung cấp tiêm sống xử lý tiêm @XxxParam
. Nếu bạn kiểm tra mã nguồn, bạn sẽ thấy việc triển khai khá giống nhau. Hai liên kết tôi đã cung cấp ở trên theo cùng một mẫu, cũng như mã bên dưới.
Những gì tôi đã làm là tạo ra một chú thích tùy chỉnh
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface VaryingParam {
String value();
@SuppressWarnings("AnnotationAsSuperInterface")
public static class Factory
extends AnnotationLiteral<VaryingParam> implements VaryingParam {
private final String value;
public static VaryingParam create(final String newValue) {
return new Factory(newValue);
}
public Factory(String newValue) {
this.value = newValue;
}
@Override
public String value() {
return this.value;
}
}
}
Nó có vẻ lạ rằng tôi có một nhà máy để tạo ra nó, nhưng điều này đã được yêu cầu để thực hiện các mã dưới đây, nơi tôi chia giá trị của String và kết thúc tạo một cá thể chú thích mới cho mỗi giá trị phân tách.
Dưới đây là ValueFactoryProvider
(trong đó, nếu bạn đã đọc một trong các bài viết trên, bạn sẽ thấy điều đó là bắt buộc đối với việc chèn thông số phương pháp tùy chỉnh). Nó là một lớp học lớn, chỉ vì tôi đặt tất cả các lớp học cần thiết vào một lớp duy nhất, theo mẫu bạn thấy trong dự án Jersey.
public class VaryingParamValueFactoryProvider extends AbstractValueFactoryProvider {
@Inject
public VaryingParamValueFactoryProvider(
final MultivaluedParameterExtractorProvider mpep,
final ServiceLocator locator) {
super(mpep, locator, Parameter.Source.UNKNOWN);
}
@Override
protected Factory<?> createValueFactory(final Parameter parameter) {
VaryingParam annotation = parameter.getAnnotation(VaryingParam.class);
if (annotation == null) {
return null;
}
String value = annotation.value();
if (value == null || value.length() == 0) {
return null;
}
String[] variations = value.split("\\s*\\|\\s*");
return new VaryingParamFactory(variations, parameter);
}
private static Parameter cloneParameter(final Parameter original, final String value) {
Annotation[] annotations = changeVaryingParam(original.getAnnotations(), value);
Parameter clone = Parameter.create(
original.getRawType(),
original.getRawType(),
true,
original.getRawType(),
original.getRawType(),
annotations);
return clone;
}
private static Annotation[] changeVaryingParam(final Annotation[] annos, final String value) {
for (int i = 0; i < annos.length; i++) {
if (annos[i] instanceof VaryingParam) {
annos[i] = VaryingParam.Factory.create(value);
break;
}
}
return annos;
}
private class VaryingParamFactory extends AbstractContainerRequestValueFactory<Object> {
private final String[] variations;
private final Parameter parameter;
private final boolean decode;
private final Class<?> paramType;
private final boolean isList;
private final boolean isSet;
VaryingParamFactory(final String[] variations, final Parameter parameter) {
this.variations = variations;
this.parameter = parameter;
this.decode = !parameter.isEncoded();
this.paramType = parameter.getRawType();
this.isList = paramType == List.class;
this.isSet = paramType == Set.class;
}
@Override
public Object provide() {
MultivaluedParameterExtractor<?> e = null;
try {
Object value = null;
MultivaluedMap<String, String> params
= getContainerRequest().getUriInfo().getQueryParameters(decode);
for (String variant : variations) {
e = get(cloneParameter(parameter, variant));
if (e == null) {
return null;
}
if (isList) {
List list = (List<?>) e.extract(params);
if (value == null) {
value = new ArrayList();
}
((List<?>) value).addAll(list);
} else if (isSet) {
Set set = (Set<?>) e.extract(params);
if (value == null) {
value = new HashSet();
}
((Set<?>) value).addAll(set);
} else {
value = e.extract(params);
if (value != null) {
return value;
}
}
}
return value;
} catch (ExtractorException ex) {
if (e == null) {
throw new ParamException.QueryParamException(ex.getCause(),
parameter.getSourceName(), parameter.getDefaultValue());
} else {
throw new ParamException.QueryParamException(ex.getCause(),
e.getName(), e.getDefaultValueString());
}
}
}
}
private static class Resolver extends ParamInjectionResolver<VaryingParam> {
public Resolver() {
super(VaryingParamValueFactoryProvider.class);
}
}
public static class Binder extends AbstractBinder {
@Override
protected void configure() {
bind(VaryingParamValueFactoryProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
bind(VaryingParamValueFactoryProvider.Resolver.class)
.to(new TypeLiteral<InjectionResolver<VaryingParam>>() {
})
.in(Singleton.class);
}
}
}
Bạn cần đăng ký lớp học 'Binder
(cuối lớp) với Jersey để sử dụng.
Điều khác biệt lớp này với Jersey QueryParamValueFactoryProvider
là thay vì chỉ xử lý một giá trị chuỗi duy nhất của chú thích, nó chia tách giá trị và cố gắng trích xuất các giá trị từ bản đồ param truy vấn. Giá trị đầu tiên được tìm thấy sẽ được trả lại.Nếu tham số là List
hoặc Set
, nó chỉ tiếp tục tìm kiếm tất cả các tùy chọn và thêm chúng vào danh sách.
Phần lớn điều này giữ tất cả chức năng bạn mong đợi từ chú thích @XxxParam
. Điều duy nhất khó thực hiện (vì vậy tôi đã bỏ qua hỗ trợ trường hợp sử dụng này), là nhiều thông số, ví dụ:
@GET
@Path("multiple")
public String getMultipleVariants(@VaryingParam("param-1|param-2|param-3") String value1,
@VaryingParam("param-1|param-2|param-3") String value2) {
return value1 + ":" + value2;
}
Tôi thực sự không nghĩ rằng nó nên là khó để thực hiện, nếu bạn thực sự cần nó, nó chỉ là vấn đề của việc tạo ra một mới MultivaluedMap
, loại bỏ một giá trị nếu nó được tìm thấy. Điều này sẽ được triển khai theo phương pháp provide()
của số VaryingParamFactory
ở trên. Nếu bạn cần trường hợp sử dụng này, bạn chỉ có thể sử dụng một số List
hoặc Set
để thay thế.
Xem this GitHub Gist (nó khá dài) cho một trường hợp thử nghiệm hoàn chỉnh, sử dụng Khung kiểm tra Jersey. Bạn có thể xem tất cả các trường hợp sử dụng tôi đã kiểm tra trong số QueryTestResource
và nơi tôi đăng ký Binder
bằng ResourceConfig
trong phương pháp thử nghiệm configure()
.