add BeanQueryMapEncoder (#802) · OpenFeign/feign@73d91bc · GitHub
Skip to content

Commit 73d91bc

Browse files
王灿velo
authored andcommitted
add BeanQueryMapEncoder (#802)
* changed default query encoder result from POJO field to getter property * changed default query encoder result from POJO field to getter property * reset mistakenly deleted file * Create PropertyQueryMapEncoder and extract QueryMapEncoder.Default to FieldQueryMapEncoder * rename PropertyQueryMapEncoder to BeanQueryMapEncoder and add README * fix README * add comments to QueryMapEncoder and remove deprecation on Default * rename test name * rename package name queryMap to querymap * format code
1 parent 907b80c commit 73d91bc

7 files changed

Lines changed: 427 additions & 74 deletions

File tree

‎README.md‎

Lines changed: 12 additions & 0 deletions

‎core/src/main/java/feign/QueryMapEncoder.java‎

Lines changed: 15 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@
1313
*/
1414
package feign;
1515

16-
import feign.codec.EncodeException;
17-
import java.lang.reflect.Field;
18-
import java.util.ArrayList;
19-
import java.util.Collections;
20-
import java.util.HashMap;
21-
import java.util.List;
16+
import feign.querymap.BeanQueryMapEncoder;
17+
import feign.querymap.FieldQueryMapEncoder;
2218
import java.util.Map;
2319

24-
/** A QueryMapEncoder encodes Objects into maps of query parameter names to values. */
20+
/**
21+
* A QueryMapEncoder encodes Objects into maps of query parameter names to values.
22+
*
23+
* @see FieldQueryMapEncoder
24+
* @see BeanQueryMapEncoder
25+
*/
2526
public interface QueryMapEncoder {
2627

2728
/**
@@ -32,55 +33,11 @@ public interface QueryMapEncoder {
3233
*/
3334
Map<String, Object> encode(Object object);
3435

35-
class Default implements QueryMapEncoder {
36-
37-
private final Map<Class<?>, ObjectParamMetadata> classToMetadata =
38-
new HashMap<Class<?>, ObjectParamMetadata>();
39-
40-
@Override
41-
public Map<String, Object> encode(Object object) throws EncodeException {
42-
try {
43-
ObjectParamMetadata metadata = getMetadata(object.getClass());
44-
Map<String, Object> fieldNameToValue = new HashMap<String, Object>();
45-
for (Field field : metadata.objectFields) {
46-
Object value = field.get(object);
47-
if (value != null && value != object) {
48-
fieldNameToValue.put(field.getName(), value);
49-
}
50-
}
51-
return fieldNameToValue;
52-
} catch (IllegalAccessException e) {
53-
throw new EncodeException("Failure encoding object into query map", e);
54-
}
55-
}
56-
57-
private ObjectParamMetadata getMetadata(Class<?> objectType) {
58-
ObjectParamMetadata metadata = classToMetadata.get(objectType);
59-
if (metadata == null) {
60-
metadata = ObjectParamMetadata.parseObjectType(objectType);
61-
classToMetadata.put(objectType, metadata);
62-
}
63-
return metadata;
64-
}
65-
66-
private static class ObjectParamMetadata {
67-
68-
private final List<Field> objectFields;
69-
70-
private ObjectParamMetadata(List<Field> objectFields) {
71-
this.objectFields = Collections.unmodifiableList(objectFields);
72-
}
73-
74-
private static ObjectParamMetadata parseObjectType(Class<?> type) {
75-
List<Field> fields = new ArrayList<Field>();
76-
for (Field field : type.getDeclaredFields()) {
77-
if (!field.isAccessible()) {
78-
field.setAccessible(true);
79-
}
80-
fields.add(field);
81-
}
82-
return new ObjectParamMetadata(fields);
83-
}
84-
}
85-
}
36+
/**
37+
* @deprecated use {@link BeanQueryMapEncoder} instead. default encoder uses reflection to inspect
38+
* provided objects Fields to expand the objects values into a query string. If you prefer
39+
* that the query string be built using getter and setter methods, as defined in the Java
40+
* Beans API, please use the {@link BeanQueryMapEncoder}
41+
*/
42+
class Default extends FieldQueryMapEncoder {}
8643
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* Copyright 2012-2018 The Feign Authors
3+
*
4+
* <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5+
* except in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* <p>http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
10+
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
* express or implied. See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package feign.querymap;
15+
16+
import feign.QueryMapEncoder;
17+
import feign.codec.EncodeException;
18+
import java.beans.IntrospectionException;
19+
import java.beans.Introspector;
20+
import java.beans.PropertyDescriptor;
21+
import java.lang.reflect.InvocationTargetException;
22+
import java.util.*;
23+
24+
/**
25+
* the query map will be generated using java beans accessible getter property as query parameter
26+
* names.
27+
*
28+
* <p>eg: "/uri?name={name}&number={number}"
29+
*
30+
* <p>order of included query parameters not guaranteed, and as usual, if any value is null, it will
31+
* be left out
32+
*/
33+
public class BeanQueryMapEncoder implements QueryMapEncoder {
34+
private final Map<Class<?>, ObjectParamMetadata> classToMetadata =
35+
new HashMap<Class<?>, ObjectParamMetadata>();
36+
37+
@Override
38+
public Map<String, Object> encode(Object object) throws EncodeException {
39+
try {
40+
ObjectParamMetadata metadata = getMetadata(object.getClass());
41+
Map<String, Object> propertyNameToValue = new HashMap<String, Object>();
42+
for (PropertyDescriptor pd : metadata.objectProperties) {
43+
Object value = pd.getReadMethod().invoke(object);
44+
if (value != null && value != object) {
45+
propertyNameToValue.put(pd.getName(), value);
46+
}
47+
}
48+
return propertyNameToValue;
49+
} catch (IllegalAccessException | IntrospectionException | InvocationTargetException e) {
50+
throw new EncodeException("Failure encoding object into query map", e);
51+
}
52+
}
53+
54+
private ObjectParamMetadata getMetadata(Class<?> objectType) throws IntrospectionException {
55+
ObjectParamMetadata metadata = classToMetadata.get(objectType);
56+
if (metadata == null) {
57+
metadata = ObjectParamMetadata.parseObjectType(objectType);
58+
classToMetadata.put(objectType, metadata);
59+
}
60+
return metadata;
61+
}
62+
63+
private static class ObjectParamMetadata {
64+
65+
private final List<PropertyDescriptor> objectProperties;
66+
67+
private ObjectParamMetadata(List<PropertyDescriptor> objectProperties) {
68+
this.objectProperties = Collections.unmodifiableList(objectProperties);
69+
}
70+
71+
private static ObjectParamMetadata parseObjectType(Class<?> type)
72+
throws IntrospectionException {
73+
List<PropertyDescriptor> properties = new ArrayList<PropertyDescriptor>();
74+
75+
for (PropertyDescriptor pd : Introspector.getBeanInfo(type).getPropertyDescriptors()) {
76+
boolean isGetterMethod = pd.getReadMethod() != null && !"class".equals(pd.getName());
77+
if (isGetterMethod) {
78+
properties.add(pd);
79+
}
80+
}
81+
82+
return new ObjectParamMetadata(properties);
83+
}
84+
}
85+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* Copyright 2012-2018 The Feign Authors
3+
*
4+
* <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5+
* except in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* <p>http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
10+
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
* express or implied. See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package feign.querymap;
15+
16+
import feign.QueryMapEncoder;
17+
import feign.codec.EncodeException;
18+
import java.lang.reflect.Field;
19+
import java.util.*;
20+
21+
/**
22+
* the query map will be generated using member variable names as query parameter names.
23+
*
24+
* <p>eg: "/uri?name={name}&number={number}"
25+
*
26+
* <p>order of included query parameters not guaranteed, and as usual, if any value is null, it will
27+
* be left out
28+
*/
29+
public class FieldQueryMapEncoder implements QueryMapEncoder {
30+
31+
private final Map<Class<?>, ObjectParamMetadata> classToMetadata =
32+
new HashMap<Class<?>, ObjectParamMetadata>();
33+
34+
@Override
35+
public Map<String, Object> encode(Object object) throws EncodeException {
36+
try {
37+
ObjectParamMetadata metadata = getMetadata(object.getClass());
38+
Map<String, Object> fieldNameToValue = new HashMap<String, Object>();
39+
for (Field field : metadata.objectFields) {
40+
Object value = field.get(object);
41+
if (value != null && value != object) {
42+
fieldNameToValue.put(field.getName(), value);
43+
}
44+
}
45+
return fieldNameToValue;
46+
} catch (IllegalAccessException e) {
47+
throw new EncodeException("Failure encoding object into query map", e);
48+
}
49+
}
50+
51+
private ObjectParamMetadata getMetadata(Class<?> objectType) {
52+
ObjectParamMetadata metadata = classToMetadata.get(objectType);
53+
if (metadata == null) {
54+
metadata = ObjectParamMetadata.parseObjectType(objectType);
55+
classToMetadata.put(objectType, metadata);
56+
}
57+
return metadata;
58+
}
59+
60+
private static class ObjectParamMetadata {
61+
62+
private final List<Field> objectFields;
63+
64+
private ObjectParamMetadata(List<Field> objectFields) {
65+
this.objectFields = Collections.unmodifiableList(objectFields);
66+
}
67+
68+
private static ObjectParamMetadata parseObjectType(Class<?> type) {
69+
List<Field> fields = new ArrayList<Field>();
70+
for (Field field : type.getDeclaredFields()) {
71+
if (!field.isAccessible()) {
72+
field.setAccessible(true);
73+
}
74+
fields.add(field);
75+
}
76+
return new ObjectParamMetadata(fields);
77+
}
78+
}
79+
}

‎core/src/test/java/feign/FeignTest.java‎

Lines changed: 58 additions & 16 deletions

0 commit comments

Comments
 (0)