Java interview and written exam experience, learn a bit of Java technology every day

Public account Java interview
Follow me to avoid getting lost
Author: Bo Saidong
Source: https://www.cnblogs.com/bryan31/

Background
Due to business needs, I have to integrate with many different third-party companies. These service providers all offer HTTP-based APIs. However, the specific details of the APIs provided by each company vary greatly. Some are based on <span>RESTFUL</span>
specifications, while others follow traditional HTTP specifications; some require signatures in the <span>header</span>
, some require <span>SSL</span>
mutual authentication, while others only need <span>SSL</span>
one-way authentication; some serialize data in <span>JSON</span>
, while others use <span>XML</span>
for serialization. The differences in these details are too numerous.
It is normal for different companies to have different API specifications. However, for me, if I want my code to be elegant, I must solve a pain point:
With so many differences in APIs from different service providers, how can I maintain a public HTTP calling suite that does not involve business logic? Ideally, it should be distinguishable through configuration or simple parameters for convenient calls?
I certainly know that there are many excellent and well-known open-source HTTP frameworks that can achieve any form of HTTP call, and I have used many of them in my years of development experience. For example, <span>Apache</span>
‘s <span>HttpClient</span>
, the excellent <span>OkHttp</span>
, and <span>Jersey Client</span>
.
However, the interfaces of these <span>HTTP</span>
open-source frameworks are relatively different. No matter which one I choose, in my scenario, I do not want to write a bunch of HTTP calling code every time I call each third-party HTTP API.
Therefore, in this scenario, I need to encapsulate each different HTTP API. Only such code can be more elegant, with a lower coupling between business code and HTTP calling logic.
Unfortunately, I am relatively lazy. On one hand, I feel that encapsulating it would take too much time, and on the other, I believe there should be better choices for encapsulating such low-level HTTP calls. I do not want to reinvent the wheel.
So, I discovered an excellent open-source HTTP framework that can shield all the differences brought by different HTTP APIs. It allows you to perform very complex HTTP calls through simple configurations, just like calling an RPC framework.
Forest
https://gitee.com/dromara/forest

Getting Started
<span>Forest</span>
supports automatic configuration for <span>Spring Boot</span>
, so you only need to introduce one dependency.
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>spring-boot-starter-forest</artifactId>
<version>1.3.0</version>
</dependency>
Define your own interface class
public interface MyClient {
@Request(url = "http://baidu.com")
String simpleRequest();
@Request(
url = "http://ditu.amap.com/service/regeo",
dataType = "json"
)
Map getLocation(@DataParam("longitude") String longitude, @DataParam("latitude") String latitude);
}
Configure the package scanning for proxy interface classes in the main application class
@SpringBootApplication
@ForestScan(basePackages = "com.example.demo.forest")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
At this point, you can inject your proxy interface from the Spring container and call the HTTP API just like calling a local method.
@Autowired
private MyClient myClient;
@Override
public void yourMethod throws Exception {
Map result = myClient.getLocation("124.730329","31.463683");
System.out.println(JSON.toJSONString(result,true));
}
Logging prints, <span>Forest</span>
will print the internal HTTP framework used, the actual request URL, and the response. Of course, logging can be controlled via configuration.

Features
I believe that for developers who are particularly engaged in integrating third-party APIs, this open-source framework can greatly enhance your efficiency.
<span>Forest</span>
encapsulates two different HTTP frameworks at the bottom: <span>Apache HttpClient</span>
and <span>OkHttp</span>
. Therefore, this open-source framework does not reinvent the wheel for the underlying implementation, but has put a lot of effort into usability.
I used <span>Forest</span>
to complete a project that integrated with multiple service provider APIs. These APIs, with their distinct styles, were converted to local methods in just one hour, and the project went live smoothly.
<span>Forest</span>
, as a higher-level HTTP framework, actually requires you to write very little code. Most of the time, you can complete localized HTTP calls with just some configuration. However, the range it covers is very broad, meeting the vast majority of your HTTP call requests.
<span>Forest</span>
has the following features:
-
Based on <span>HttpClient</span>
and<span>OkHttp</span>
as backend frameworks -
Send HTTP requests by calling local methods, achieving decoupling between business logic and HTTP protocol -
More lightweight compared to Feign, does not depend on <span>Spring Cloud</span>
or any registry -
Supports all request methods: <span>GET</span>
,<span>HEAD</span>
,<span>OPTIONS</span>
,<span>TRACE</span>
,<span>POST</span>
,<span>DELETE</span>
,<span>PUT</span>
,<span>PATCH</span>
-
Supports flexible template expressions -
Supports filters to filter incoming data -
Defines <span>HTTP</span>
requests based on annotations and configurations -
Supports integration with <span>Spring</span>
and<span>Spring Boot</span>
-
Implements <span>JSON</span>
and<span>XML</span>
serialization and deserialization -
Supports JSON conversion frameworks: <span>Fastjson</span>
,<span>Jackson</span>
,<span>Gson</span>
-
Supports <span>JAXB</span>
style<span>XML</span>
conversion -
Supports <span>SSL</span>
one-way and two-way encryption -
Supports setting up HTTP connection pools -
Can implement request result callbacks through <span>OnSuccess</span>
and<span>OnError</span>
interface parameters -
Simple configuration, generally only requires <span>@Request</span>
annotation to define the majority of requests -
Supports asynchronous request calling
Two Great Features
Here, I won’t describe the usage and configuration methods one by one. Those interested can read the detailed documentation:
http://forest.dtflyx.com/
I just want to analyze two features of this framework that I think are quite good
Template Expressions and Parameter Mapping Binding Functionality
Template expressions are especially convenient to use, for example
@Request(
url = "${0}/send?un=${1}&pw=${2}&ph=${3}&ct=${4}",
type = "get",
dataType = "json"
)
public Map send(
String base,
String userName,
String password,
String phone,
String content
);
The above uses index numbers for value retrieval, and you can also retrieve values by name:
@Request(
url = "${base}/send?un=${un}&pw=${pw}&ph=${3}&ct=${ct}",
type = "get",
dataType = "json"
)
public Map send(
@DataVariable("base") String base,
@DataVariable("un") String userName,
@DataVariable("pw") String password,
@DataVariable("ph") String phone,
@DataVariable("ct") String content
);
It can even be simplified like this:
@Request(
url = "${base}/send",
type = "get",
dataType = "json"
)
public Map send(
@DataVariable("base") String base,
@DataParam("un") String userName,
@DataParam("pw") String password,
@DataParam("ph") String phone,
@DataParam("ct") String content
);
All three of the above methods are equivalent
Of course, you can also bind parameters to the header and body, and you can even use some expressions to serialize objects into JSON or XML:
@Request(
url = "${base}/pay",
contentType = "application/json",
type = "post",
dataType = "json",
headers = {"Authorization: ${1}"},
data = "${json($0)}"
)
public PayResponse pay(PayRequest request, String auth);
Details on data binding can be found in the documentation
Support for <span>HTTPS</span>
In the past, when using other HTTP frameworks to handle HTTPS, I always found it particularly troublesome, especially with mutual certificates. Every time I encountered a problem, I could only search on Baidu and modify my code based on others’ experiences.
<span>Forest</span>
has also thought this through. The underlying implementation perfectly encapsulates support for one-way and two-way certificates for HTTPS. It can be quickly completed through simple configurations. For example, for two-way certificates:
@Request(
url = "${base}/pay",
contentType = "application/json",
type = "post",
dataType = "json",
keyStore = "pay-keystore",
data = "${json($0)}"
)
public PayResponse pay(PayRequest request);
Where <span>pay-keystore</span>
corresponds to the <span>application.yml</span>
file in <span>ssl-key-stores</span>
forest:
...
ssl-key-stores:
- id: pay-keystore
file: test.keystore
keystore-pass: 123456
cert-pass: 123456
protocols: SSLv3
With this configuration, you’re all set; the rest is just local code invocation.
Conclusion
<span>Forest</span>
has many other functional settings. Interested readers should carefully read the documentation and examples.
However, I want to say that many people must be thinking, isn’t this just <span>Feign</span>
?
When I was developing a <span>Spring Cloud</span>
project, I also used <span>Feign</span>
for a while. Personally, I feel that <span>Forest</span>
does indeed resemble the configuration and usage design of <span>Feign</span>
, but <span>Feign</span>
serves more as a member of the <span>Spring Cloud</span>
ecosystem, acting as an RPC communication tool that not only handles HTTP communication but also performs load balancing for calling addresses issued by the registry.
On the other hand, <span>Forest</span>
is positioned as a high-level HTTP tool, focusing on user-friendliness and ease of use. From a usage perspective, I personally find <span>Forest</span>
configuration to be simpler and more direct. The many features it provides can solve many pain points for users.
The spirit of open source is invaluable, and good open-source projects need everyone’s support and contributions. I hope this article can provide everyone with a new choice when selecting an HTTP client framework: <span>Forest</span>