In this article, I will discuss how dynamic proxies work in the Java platform and how dependency injection takes advantage of this Java feature. This writing is derived from my search to try and inject request scope objects into singleton objects, within the HK2 framework (or more precisely, HK2 within a Jersey application). I was going to include my findings all into one blog, but I felt this topic is too broad for a problem that is solved in two lines of code.
First, I will go through a quick discussion about the Proxy Pattern, then show how to use dynamic proxies with the Java language, then finally go through an example using dynamic proxies and custom dependency injection.
Table of Contents:
Proxy Pattern
I won’t get into too much detail about the proxy pattern. There is good reading all over the internet. I will just give a brief analogy, along with some brief code example of the pattern.
I’m sure most of you have heard the phrase “vote by proxy”. It’s when someone places a vote for someone else. For example say there is some arbitrary vote among board members of a corporation. Member B is sick in the hospital, so can’t attend the board meeting. So member B signs over a proxy vote to member A. So during the vote meeting, member A places member B’s vote for them. Member A is just acting as a delegate for member B.
The proxy pattern works the same. Here is a class diagram (from wikipedia).
Say Member
is the interface
public interface Member {
void vote();
}
Then you have MemberA
and MemberB
public class MemberA implements Member {
public void vote() {}
}
public class MemberB implements Member {
public void vote() {}
}
Since member B will not be present, we need a proxy for it. The proxy should also implement the Member
interface and it should hold a reference to MemberB
.
public class MemberBProxy implements Member {
private MemberB memberB;
public void vote {
memberB.vote();
}
}
So now MemberA
can get a the member B proxy and make the proxy vote for member B.
This might not be the greatest example, as the proxy does nothing but call the vote on member B. But with real proxies, there is usually something else going on under the hood. For example in the case of remote proxies, the vote()
method might actually make a network call to a remote MemberB
. An example of this in the Java platform, would be RMI (remote method invocation). The example later will describe another use case, that is usually handled transparently for the developer.
Dynamic Proxies
With the example above, we had to manually write the proxy class. In Java though, this is not required, with the introduction of dynamic proxies in 1.3. The core interface to dynamic proxies is java.lang.reflect.Proxy
. To use it, we require to components, our interface to proxy, and an InvocationHandler
. Let’s look at a quick example, using the same above analogy.
Member memberBProxy = (Memeber) Proxy.newProxyInstance(
Memeber.class.getClassLoader(),
new Class[] { Member.class },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
return method.invoke(new MemberB(), args);
}
}
);
That’s it. Now memberBProxy
is a Proxy
instance, not a MemberB
instance. If you print out the class name of the Member
object, you will actually see that the class name is com.sun.proxy.ProxyX
, and not MemberB
.
Let’s walk through it real quick. Here’s is the signature for Proxy#newProxyInstance
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
It first requires the ClassLoader
for which to define proxy. Second it requires to interfaces to implements, then finally the InvocationHandler
. The InvocationHandler
has only a single callback method we need to implement
Object invoke(Object proxy, Method method, Object[] args)
The first argument is the actual proxy object. You should rarely have use for this. The second argument is the java.lang.reflect.Method
. If you’ve had any experience with Java reflection, you should be familiar with the interface. With the Method
, we can invoke a method call by passing the object for which to invoke the method on, along with any arguments, hence the last line
return method.invoke(new MemberB(), args);
Here the proxy hands the Method
and the arguments passed to the method call on the proxy. We as the InvocationHandler
implementer, can do whatever we want with the Method
and method arguments. Here we are simply say that the called method should be invoked in the new MemberB()
object, passing in the arguments.
To get a clearer picture, just look at it like the Proxy
instance has all the methods the Member
interface has. So when we call Proxy#vote()
, it calls the InvocationHandler#invoke
passing in itself, the Method
, and the arguments passed to vote()
(if any). And out InvocationHandler
implementation simply call the method, by calling inovoke
on the Method
object. the Method
object then invokes the vote()
on the actual MemberB
object.
And that’s it. As you can see, dynamic proxies are fairly simple to implements.
Dynamic Proxy and Custom Injection Example
- Get the GitHub project
What I will attempt to do here is try to explain how dynamic proxies are used in a dependency injection framework. One of the main use cases for dynamic proxies within DI, is when dealing with scopes. For instance you have a service or a controller that is in a singleton scope, meaning that there is only one create per application. That singleton service is dependent on a server this is in a request scope, meaning that one should be created for each request. The classes might look something like (it’s all imaginary - no specific framework).
@Controller(scope = "singleton")
public class AppController {
@Inject
SingletonService service;
}
@Singleton
public class SingletonService {
@Inject
RequestScopedService service;
}
@RequestScoped
public class RequestScopedService {}
The problem here is that when the SingletonService
is created on start-up, all the injections need to be performed. But there is no request on start-up, so there should be no RequestScopedService
currently tied to a request. Another problem is how do we managed which request gets which RequestScopedService
. Maybe we can add a setter in the SingletonService
where we can set a new RequestScopedService
for each request. But that doesn’t work, because the SingletonService
will be accessed concurrently, as is how some servers work (a thread per request).
This is where dynamic proxies come to the rescue. When the SingletonService
is created on start up, instead of injection an actual RequestScopedService
, we will inject a Proxy
of the service. When calls are made on the RequestScopedService
from inside the SingletonService
, the call will actually be made on the Proxy
, which will delegate the call to the InvocationHandler#invoke
method, which we will implement to call a RequestScopedService
that we obtain from a ThreadLocal
. A new RequestScopedService
will be set in the ThreadLocal
every time a request is processed, which will be handled in a separate thread. If you have ever heard the term “thread local proxy”, this is pretty much the gist of how it works.
So let’s try and implement all this. We will even implement our own dependency injection. We will implement a simple server framework, that allows a user to implement a custom RequestHandler
that can inject our SingletonService
which will in turn be dependent on a RequestScopedService
. Here is the class diagram. (To follow along, it’s best to grab the GitHub project from the above link).
As stated earlier, the user will be able to implement a custom RequestHandler
and inject our SingletonService
. In the project, there is default implementation that just returns the message from the SingletonService
, as a Response
public class DefaultRequestHandler implements RequestHandler {
@Inject
private SingletonService singletonService;
@Override
public Response handleRequest(Request request) {
return new Response(singletonService.getMessage());
}
}
Then the user creates the Server
passing the implementation class to the constructor
Server server = new Server(DefaultRequestHandler.class);
In the server constructor, you will see a couple things, the validation of the user defined RequestHandler
class, and the creation of the SingletonService
. Validation is not important here, and here is the creation of the SingletonService
.
private static SingletonService initSingletonService() {
Service proxyService = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(), new Class[]{Service.class},
new ServiceInvocationHandler());
return new SingletonService(proxyService);
}
The first thing we do is create the proxy of the Service
class. Here is the ServiceInvocationHandler
public class ServiceInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) .. {
Service service = ThreadLocalService.get();
return method.invoke(service, args);
}
}
It doesn’t do much. It just retrieves the Service
from the ThreadLocalService
, and invokes the proxied method on the service. We will see a bit later when the instance of the RequestScopedService
is set into the ThreadLocal
.
Then the SingletonService
is created with the proxy Service
. So now, when SingletonService
calls a method on the Service
, the proxy will lookup the thread local Service
and delegate the call to its methods.
That’s it for the server bootstrap. Now we can get into the runtime and request processing. Here is the sequence diagram from the request processing flow.
First the Main
program calls Server#sendRequest(Request)
passing in a new Request
object. A Request
object has nothing more than the name of the client.
when we call sendRequest
on the Server
, all it does it add the Request
to the BlockingQueue
.
public void sendRequest(Request request) {
try {
requests.put(request);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
When the server is started, it constantly polls the BlockingQueue
, waiting for a new Request
public void startServer() {
executors.submit(new Runnable() {
@Override
public void run() {
while (true) {
try {
Request request = requests.take();
if (request.isShutdownTrigger()) {
break;
}
executors.submit(new RequestProcessor(userDefineHandler,
singletonService,
request));
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
System.out.println("Server shutdown!");
}
});
}
When a Request
is received, the Server
creates a new RequestProcessor
, passing in the Request
object, the SingletonService
object, and the use defined RequestHandler
class. If you look at the run()
method of the RequestProcessor
, you will see the following
private void setThreadLocalService() {
ThreadLocalService.set(new RequestScopedService(request));
}
@Override
public void run() {
setThreadLocalService();
RequestHandler handler = initInjections();
Response response = handler.handleRequest(request);
System.out.println(response.getMessage());
}
So the first thing the processor does is set the RequestScopedService
into the ThreadLocalService
. Then the RequestHandler
is instantiated, using some reflection, as seen in the initInjections()
method
RequestHandler initInjections() {
try {
for (Field field : handlerCls.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)
&& field.getType() == SingletonService.class) {
return createHandler(field, handlerCls);
}
}
Constructor[] cons = handlerCls.getConstructors();
for (Constructor con : cons) {
if (con.isAnnotationPresent(Inject.class)
&& con.getParameterCount() == 1
&& con.getParameterTypes()[0] == SingletonService.class) {
return (RequestHandler) con.newInstance(singletonService);
}
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
throw new RuntimeException("RequestHandler could not be created.");
}
The method simply checks to see if we should be doing field injection or constructor injection. It makes sure the field or constructor in annotated with @Inject
. If the field is annotated, and the field type is SingletonService
, we will set the field with the SingletonService
, using reflection. Similar events occur to process constructor injection.
The last thing the RequestHander
does is simply call the handleRequest
of the RequestHandler
, which returns a Response
, then the processor prints the Response
message. And that’s it for the processing of a single request.
If you run the Main
class, you should see something similar to the following
Message: Hello Kobe
meta-info:
service class: com.sun.proxy.$Proxy2
service id: 1
thread name: pool-1-thread-2
Message: Hello Lebron
meta-info:
service class: com.sun.proxy.$Proxy2
service id: 2
thread name: pool-1-thread-3
... three more
The first thing that you should notice is that the service class is indeed the Proxy
instance, and not the RequestScopedService
. The underlying will RequestScopedService
will remain the same as long as the request is being processed. So all made on the Service
inside the SingletonService
will always be delegated to actual RequestScopedService
associated with a particular thread.
And that’s it.
Summary
We covered some basics on the Proxy Pattern, and saw how it uses a wrapper or delegation model to make calls to an underlying object. Then we covered dynamic proxies, and how it is implemented in the Java language. The finally we went through an example of how dynamic proxies work with scoped dependency injection. If an object in a lesser scope needs to be injected in to an object in a wider scope, the lesser scope object needs to be proxied, so that different thread have access to their own lesser scoped object instance.
The example is far from some thing you’d use in real life, but I hope it gives you a better understanding of what goes on under the hood with regards to the combination of the two subject matters we discussed, dynamic proxies and dependency injection.
Support Me
If you found any information in this post useful, please show your support and like it, share it, tweet it, pin it, and/or plus one it. Much thanks!