[Spring Boot] Feign Client and JDK Dynamic Proxy
While I was reading Head First Design Patterns book, I came across Proxy pattern. Then, I ran into a brief introduction to Invocation Handler in Java. I remembered a conversation almost 3 years ago with a friend of mine that I worked with before via Pivotal App-Tx engagement (now a colleague actually!) who taught me the basics of Spring Cloud and who encouraged me to become an open-source contributor.
Li Gang: “Do you know how Feign Client works with just interface?”
Li Gang: “It works due to JDK proxy and invocation handler.”
Me: ‘ㅇ_ㅇ … ? ’
My discovery stopped back then but here I am today to learn about JDK Proxy. A lot of blogs talk about JDK Proxy with Spring AOP. In this article, let’s see how Feign Client makes use of JDK Proxy.
Basics of JDK Dynamic Proxy
Proxy can be created with an implementation of
Let’s look at an example to see how it works during runtime. In this example, invocation handler checks the method name and prints accordingly. Then
method.invoke(target, args) invokes the methods on the concrete class. This indicates that pre-processing or post-processing can be added inside
The result looks like the screenshot below.
Back to Feign Client
Whenever I come across a requirement where I need to invoke APIs provided by other services, I have a tendency of using Feign Client. I find it very convenient because
- It is declarative and it follows similar structure to Spring MVC annotations
- No work needed with connections or input/output streams
- Integrates well with other Spring Cloud components out-of-box
So here is a client that gets product information from a fake REST API server.
Of course, I will create an endpoint that makes use of our client.
Here are some questions that I needed answers to.
- How is proxy created?
- How can it run without an implementation? Feign Client is an interface and there is no concrete class for it in our code.
- How can it make HTTP calls just by defining an interface? I know that Feign Client by default uses
Q1. How is proxy created?
From our controller, it can be observed that ProductClient is a proxy. It is a proxy to
HardCodedTarget which implements
Target. But what makes it a proxy?
So when the application starts up,
FeignClientRegistrar will basically scan Feign Clients annotations in our code to create BeanDefinitions to the BeanFactory.
Then when creating a bean, calling
factoryBean.getObject() will return a Feign client created with the specified data and the context information.
Which will lead to
build() builds a factory
This is the place where proxy is created using
Proxy.newProxyInstance(). It is interesting that
InvocationHandler is created with
methodToHandler map. This map contains key as
ProductClient.fetchProduct(int) and value of
SynchronousMethodHandler will later create and send HTTP request. This is basically the key mapping that translates our
client.fetchProduct(id) method to a HTTP call.
Q2. How can it run without an implementation?
From what we have observed up to this point, it is clear that
FeignClientsRegistrar registers required beans. For
FeignClientsRegistrar to be imported, the application needs to declare
Q3. How can it make HTTP calls just by defining an interface?
From this point on, breakpoints take place after
We talked about
methodToHandler map earlier when creating an invocation handler.
FeignInvocationHandler is the invocation handler we created earlier. I mentioned that the
dispatch map in the below code (same as
methodToHandler map) returns value of
SynchronousMethodHandler. This will result returning the result of
invoke method will trigger
Which then will trigger
Lastly, here is the code where
HttpURLConnection is created to send request and return response.
In this article, we saw a real life example of a Proxy pattern in one of my favorite libraries.
This article also covered:
- Basic example of JDK dynamic proxy
- How proxy is used in Feign Client