Feign Client: Interface & the Proxy Magic
You never wrote a class that implements UserClient. So when you call userClient.getUserById(1L) — who actually runs that code?
Feign Client: Interface & the Proxy Magic
@FeignClient(name = "user-service", url = "https://api.example.com")
public interface UserClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
}
You never wrote a class that implements UserClient. So when you call userClient.getUserById(1L) — who actually runs that code?
That's exactly what this article answers.
The Interface (Your Contract)
The interface is your HTTP blueprint. Nothing more, nothing less.
Each method you declare maps directly to an HTTP call:
javaCopy code
@FeignClient(name = "user-service", url = "https://api.example.com")
public interface UserClient {
// GET https://api.example.com/users/42
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
// POST https://api.example.com/users + JSON body
@PostMapping("/users")
User createUser(@RequestBody User user);
}
The Proxy (The Real Magic 🪄)
A Java Dynamic Proxy is an object that implements an interface at runtime, without you writing a class. It intercepts every method call and lets you decide what to do with it.
You call: userClient.getUserById(42L)
↓
[Proxy intercepts the call]
↓
[Reads method name + annotations]
↓
[Builds HTTP request: GET /users/42]
↓
[Fires the actual HTTP call]
↓
[Returns deserialized User object]
How Feign Creates the Proxy Internally
Under the hood, when you call .target(), Feign does exactly this:
// This is what Feign does internally (simplified)
UserClient proxy = (UserClient) Proxy.newProxyInstance(
UserClient.class.getClassLoader(), // ClassLoader
new Class[]{UserClient.class}, // Interface to implement
new FeignInvocationHandler(...) // Who handles the method calls
);
FeignInvocationHandler is the brain. Every time you call a method on your UserClient, it goes here first.
javaCopy code
// Simplified version of what FeignInvocationHandler does
class FeignInvocationHandler implements InvocationHandler {
private final Map<Method, MethodHandler> methodHandlers;
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 1. Find the right handler for the method you called
MethodHandler handler = methodHandlers.get(method);
// 2. The handler builds the HTTP request and fires it
return handler.invoke(args);
}
}
The Map<Method, MethodHandler> is built once when .target() is called — Feign reads all your annotations upfront and prepares a handler for each method.
The Full Picture
@FeignClient interface
↓
Feign.builder()
↓
.target() is called
↓
Feign reads all @GetMapping,
@PostMapping annotations
↓
Builds Map<Method, MethodHandler>
↓
Proxy.newProxyInstance() creates
a fake implementation
↓
You get back a UserClient object
(actually a Proxy in disguise 🕵️)
↓
You call getUserById(42L)
↓
Proxy → FeignInvocationHandler
↓
Correct MethodHandler fires
↓
GET /users/42 → HTTP call made
↓
JSON decoded → User object returned ✅
🎯 Key Takeaways
ConceptOne-linerInterfaceYour HTTP blueprint — define what to callBuilderAssembles encoder, decoder, interceptors into one client.target()The trigger that creates the proxyJDK ProxyThe invisible implementation Spring creates at runtimeInvocationHandlerIntercepts your method call and fires the HTTP request
메타데이터
- post_id
- 12db57ebaeff
- slug
- feign-client-interface-the-proxy-magic-12db57ebaeff
- url
- https://medium.com/@raghurambtechit/feign-client-interface-the-proxy-magic-12db57ebaeff
- canonical_url
- https://medium.com/@raghurambtechit/feign-client-interface-the-proxy-magic-12db57ebaeff
- author_url
- https://medium.com/@raghurambtechit
- status
- ok
- fetched_at
- 2026-06-09 15:37:30