项目地址:https://github.com/dadiyang/http-api-invoker
让http调用跟本地方法调用一样自然优雅
将http请求url和接口绑定,由框架生成接口的代理类,直接调用接口的方法就会自动构建请求参数并发送请求,然后处理请求响应转换为接口方法的返回值返回(支持泛型)。
若与 Spring 集成(可选),更能使用 @Autowired 进行自动注入接口的代理实现。
技术:动态代理 + 反射 + 注解 + 自动包扫描
当调用接口的方法的时候框架会完成以下三个步骤
启动包扫描,类似@ComponentScan。
标注一个类是与Http接口绑定的,需要被包扫描的接口。类似Spring中的@Component注解
标注方法对应的url
指定方法参数名对应的请求参数名称
指定方法参数为 Headers,目前只允许打在类型为 Map
的参数上,否则会抛出 IllegalArgumentException
指定方法参数为 Cookies,目前只允许打在类型为 Map
的参数上,否则会抛出 IllegalArgumentException
<dependency>
<groupId>com.github.dadiyanggroupId>
<artifactId>http-api-invokerartifactId>
<version>1.1.9version>
dependency>
将请求url与接口方法绑定(支持路径参数,例如 {cityName}; 保留路径参数到请求体,例如 #{id}; 配置项,例如 ${api.url.city})
注:路径参数占位符 {} 和配置项占位符 ${}, 参数被匹配为路径参数时会被移除,若不想移除可以使用 #{}
示例:完整实例 请查看CityServiceTest
@HttpApi(prefix="${api.url.city}/city")
public interface CityService {
/**
* 使用URI,会自动添加prefix指定的前缀
*/
@HttpReq("/allCities")
List<City> getAllCities();
/**
* 使用Param注解指定方法参数对应的请求参数名称
*/
@HttpReq("/getById")
City getCity(@Param("id") int id);
/**
* 默认使用GET方法,可以通过method指定请求方式
* 如果是集合类或数组的参数数据会直接当成请求体直接发送
*/
@HttpReq(value = "/save", method = "POST")
boolean saveCities(List<City> cities);
/**
* 使用完整的路径,不会添加前缀
*/
@HttpReq(value = "http://localhost:8080/city/saveCity", method = "POST")
boolean saveCity(@Param("id") String id, @Param("name") String name, @Param("wubaId") int wubaId);
/**
* 支持路径参数
*/
@HttpReq("/getCityRest/{id}")
City getCityRest(@Param("id") int id);
/**
* 可以通过返回 byte[]或 InputStream 来下载资源
* @return 调用接口返回的字节数组
*/
@HttpReq("/picture/landscape.png")
byte[] download();
/**
* 上传输入流
* @param fileName 文件名
* @param in 输入流
*/
@HttpReq(value="/picture/upload", method = "POST")
void upload(@Param("fileName") String fileName,
@Param(value = "media", isBody = true) InputStream in);
/**
* 上传文件
* @param file 文件
*/
@HttpReq(value="/picture/upload", method = "POST")
void upload(@Param(value = "media", isBody = true) File file);
/**
* 带正确请求头的方法
*/
@HttpReq("/getCityRest/{id}")
City getCityWithHeaders(@Param("id") int id, @Headers Map<String, String> headers);
/**
* 带cookie的方法
*/
@HttpReq("/getCityRest/{id}")
City getCityWithCookies(@Param("id") int id, @Cookies Map<String, String> cookies);
}
你唯一需要做的就是定义上面的接口,然后在使用的时候就可以通过工厂类获取接口的代理实现类了。
如果你使用Spring,那简单地配置一下就可以使用@Autowired注解直接注入你的接口实现了
使用 HttpApiProxyFactory.getProxy(接口.class) 获取接口代理类
Properties properties = new Properties();
properties.load(getClass().getClassLoader().getResourceAsStream("conf.properties"));
// properties 是可选的,若不提供则默认使用 System.getProperties() 提供的配置
CityService service = new HttpApiProxyFactory(properties).getProxy(CityService.class);
像@ComponentScan注解一样,在有 @Configuration 注解的类上加上 @HttpApiScan 注解开启服务接口扫描
注: configPaths是可选的,用于填充url中使用的配置占位符,如 ${api.url} 对应 api.url 配置项,这些配置项会自动从Spring的Environment中获取,如果有需要,也可以通过指定 configPaths 加载特定的配置文件。
@Configuration
// 启动服务接口扫描,configPaths是可选的,用于填充url中使用的配置项
@HttpApiScan(configPaths = "classpath:conf.properties")
public class TestApplication {
}
因为是动态代理生成并注册到Spring容器中的,所以IDE可能会警告 “Could not autowired. no beans of type ‘xxx’ type found.” 忽略即可。
@Autowired
private CityService cityService;
可查看项目单元测试类 CityServiceTest
@Autowired
private CityService cityService;
public void test() {
List<City> cities = cityService.getAllCities();
for (City city : cities) {
System.out.println(city);
}
}
public void getProxyTest() throws Exception {
CityService service = new HttpApiProxyFactory().getProxy(CityService.class);
List<City> cities = service.getAllCities();
for (City city : cities) {
System.out.println(city);
}
}
项目地址: https://github.com/dadiyang/http-api-invoker