引言

在项目中可能我们会调用其他的rest接口,我们会写一个HttpClientSender工具类,返回后然后一大堆的条件判断,既繁琐又不直观。我们可以运用所学的FactoryBean手写一个rest代理,使用时可以像Mybatis那样注入接口就可以,方便又简洁。

此工具类是依据HttpClient编写的。灵活性比较高,代码量也是比较多,还是有一些局限性,比如请求方法只支持GET、POST、PUT、DELETE等。

FactoryBean

使用FactoryBean来动态获取代理对象,这里我们定义一个RestServiceProxyFactoryBean<T>实现FactoryBean接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* @param <T>
* @author max
*/
@Data
public class RestServiceProxyFactoryBean<T> implements FactoryBean<T> {

/**
* RestServiceProxyFactory路径
*/
private RestServiceProxyFactory factory;
/**
* 接口路径
*/
private Class<T> type;

@Override
public T getObject() throws Exception {
return factory.newRestServiceProxy(type);
}

@Override
public Class<?> getObjectType() {
return type;
}

@Override
public boolean isSingleton() {
return true;
}
}

RestServiceProxyFactoryBean有两个参数:

  1. RestServiceProxyFactory:创建代理对象的factory,下面会说到。
  2. type:接口所在路径。

代理工厂RestServiceProxyFactory

当程序调用rest接口时,会由代理工厂根据各种配置生成代理对象并填充返回结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
* @author max
*/
@Slf4j
@Data
public class RestServiceProxyFactory {

private final static RestClient restClient = new RestClient();

private ServiceConfigManager configManager;

/**
* 配置文件路径
*/
private String location;

public RestServiceProxyFactory() {

}

public void init() {
configManager = ServiceConfigManager.build(location);
}


public <T> T newRestServiceProxy(Class<T> clazz) {
return Reflection.newProxy(clazz, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}

if (method.isDefault()) {
MethodHandle methodHandler = RestServiceProxyFactory.this.getMethodHandler(method);
return methodHandler.bindTo(proxy).invokeWithArguments(args);
}

InvokeParams invokeParams = InvokeParams.getInstance(configManager, method, args);

Object ret = null;
try {
ret = restClient.invoke(invokeParams);
} catch (Throwable e) {
e.printStackTrace();
}
return ret;
}
});
}

private MethodHandle getMethodHandler(Method method)
throws NoSuchMethodException, IllegalAccessException, InstantiationException, java.lang.reflect.InvocationTargetException {

Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
constructor.setAccessible(true);

Class<?> declaringClass = method.getDeclaringClass();
int allModes = (MethodHandles.Lookup.PUBLIC | MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE);
return constructor.newInstance(declaringClass, allModes)
.unreflectSpecial(method, declaringClass);
}
}

开源地址

由代理对象根据配置可以调用HTTP请求反序列化并封装返回结果,这样就不需要自己做其他的工作。项目已经更新到我的GitHub:https://github.com/mx-go/rest-proxyREADM中有使用方法。打成jar放到自己的项目中即可使用。