What is a proxy object?
Proxy Object is an object that adds additional logic on top of an object that is being proxied without having to modify code of the proxied object. The proxy object has the same public methods as an object that is being proxied and it should be as much as possible indistinguishable from the proxied object. When a method is invoked on Proxy Object, additional code, usually before and after sections are invoked, also code from the proxied object is invoked by Proxy Object.
Basically, a proxy object is a special kind of object that holds a reference to the object that is being proxied. And whenever we are invoking some kind of method on the object we are actually invoking it through the interface. But instead of invoking it directly on the proxy object, we are actually invoking this on the proxy object itself.
It will be much easier to understand this explanation when we will look into the code.
Let's consider how we can create a proxy manually.
For example, we have the
PersonDao and this data object can provide two methods which are implemented by
After running you will get the next output. As you can see all methods were invoked.
Searching for person... Saving person... Process finished with exit code 0
Now let's say that we want to add some kind of code but it's not the business code. It is like additional code like for example logging, transactions.
So one way would be to modify
PersonDaoImpl class. And it will create a code that is hard to read because we are not creating any separations of concerns and we are mixing business logic with the logging or security logic.
The other possibility is to create a Proxy pattern. So we can do this by implementing a class that would take as input the original class and it would implement the same interface.
So basically we will have a delegation into the original class. And when we call any method we will call also before and after section. This can be a security check or logging.
After the running this code you will get the next output:
before findById Searching for person... after findById before save Saving person... after save Process finished with exit code 0
This is nice. But the issue of this code is that for each part we need to write manually each of the sections. There are two mechanisms to avoid this duplication.
What types of proxies Spring can we create?
Spring Framework supports two kinds of proxies:
- JDK Dynamic Proxy – used by default if the target object implements an interface
- CGLIB Proxy – use when the target does not implement any interface
JDK Dynamic Proxy
In this code, I am creating a new proxy instance for the loaded class PersonDao and that is having interfaces, and then I am creating invocation handler.
So whenever you want to create JDK Proxy you are using
Proxy.newProxyInstance. And also we need to pass invocation handler. This is the class that will be invoked instead of the original code. And this class is representing the proxy.
Limitations of JDK Dynamic Proxy:
- Requires proxy object to implement the interface
- Only interface methods will be proxied
- No support for self-invocation
Let's look at another example.
DepartmentDao has not any interface so we can't use JDK Proxy.
But we can use
Enhancer class from CGLIB Proxy.
Firstly, we need to implement the callback which will be the method interceptor in our case. In this method, we are implementing the before and after setctions
Limitations of CGLIB Proxy:
- Does not work for final classes. Because under the hood it creates the extension of the original class. So it uses inheritance.
- Does not work for final methods
- No support for self-invocation
Now let's have a look at how Spring is using the proxy during the application config creation.
So here we have a simple application that has a configuration class.
And instead of using @Component over bean class, the bean is created in configuration class.
And if we invoke this code with the debug mode we will see that actually
springBean() method is not invoked on original code. But instead, Spring is creating a CGLIB proxy for this application config.
Connected to the target VM, address: '127.0.0.1:8542', transport: 'socket' Creating SpringBean in ApplicationConfig$$EnhancerBySpringCGLIB$$7bc8e93b Disconnected from the target VM, address: '127.0.0.1:8542', transport: 'socket' Process finished with exit code 0
You might be wondering why Spring is creating the proxy for this bean. The answer is because this bean is a singleton. And we don't want this method to be invoked twice.
What is the power of a proxy object and where are the disadvantages?
- Ability to change the behavior of existing beans without changing the original code
- Separation of concerns (logging, transactions, security, …)
- May create code hard to debug. Because on the code level, it looks like you are invoking original object but actually you are not invoking the original object.
- Needs to use unchecked exception for exceptions not declared in the original method
- May cause performance issues if before/after a section in proxy code is using IO (Network, Disk)
- May cause unexpected equals operator (==) results since Proxy Object and Proxied Object are two different objects