Fork me on GitHub

The Story of Dynamic Proxy

Definition and Goal

The dynamic proxy can be divided into two parts: “proxy” and “dynamic”——the “proxy(pattern)” is one kind of design pattern in GoF, and the “dynamic” means the proxy pattern can be polished with the help of reflection mechanism in JVM. The ability to decoupling the routine tasks and business tasks is the reason why dynamic proxy is widely used in many Java Web Framework(Like Spring).

Overview

In this article, we will elaborate on the evolution of proxy during the example Database transaction, to illustrate the idea of this magic proxy, and at the end we will demystify the implementation of how this amazing mechanism implemented behind JDK.

Background

The task of own design is modeling how to manipulate the database transaction, we have only one table named User, providing two method: add, delelete for outsider. Pay attention the fact that we need to use start and end routine before and after the normal operation on database to gurantee the atomic of our transaction.

First, we define the interface ingerated with our functions

1
2
3
4
interface UserService {
public void addUser();
public void deleteUser();
}

Evolution

Naive

The naive way is to implement the interface of the UserService brutally,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class UserServiceNoProxy implements UserService {

@Override
public void addUser() {
System.out.println("Start a database connection");
System.out.println("Add the user information");
System.out.println("Close a database connection");
}

@Override
public void deleteUser() {
System.out.println("Start a database connection");
System.out.println("Delete the user information");
System.out.println("Close a database connection");
}
}

public class TestNoProxy {
public static void main(String[] args) {
UserServiceNoProxy userServiceNoProxy = new UserServiceNoProxy();
userServiceNoProxy.addUser();
userServiceNoProxy.deleteUser();
}
}

The shortcoming of this implementation is the mixture the code of routine operation(Start Close DB connection) and the one of the variable operation(Add or Delete user). So our next improvement is to sepearate these two kinds of code.

Static Proxy

We generate two classes to decouple the routine code and the normal code.

UserServiceImpl: Contains the normal functions to manipulate database

1
2
3
4
5
6
7
8
9
10
11
12
13
class UserServiceImpl implements UserService {

@Override
public void addUser() {
System.out.println("Add the user information");
}

@Override
public void deleteUser() {
System.out.println("Delete the user information");
}

}

UserServiceStaticProxy: The wrapper of UserServiceImpl, attached with routine functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class UserServiceStaticProxy implements UserService {

private UserService userService;

public UserServiceStaticProxy(UserService userService){
super();
this.userService = userService;
}

public void open(){
System.out.println("Start a database connection");
}

public void close(){
System.out.println("Close a database connection");
}

@Override
public void deleteUser() {
this.open();
userService.deleteUser();
this.close();
}

@Override
public void addUser() {
this.open();
userService.addUser();
this.close();
}

}

By the proxy pattern, we can seperate the operation into two classes. But one thing is still disturbing, let’s say, the addUser and deleteUser both contains the open and close , which are redundant. So we need to introduce the dynamic proxy to sublime.

Dynamic Proxy

Before coding, we have to say that the code below is a little bold to your intuition, and you should just follow the rules to generate the code, we will demysify it in the next part.

We need a few package related to Reflect to create a Dynamic proxy.

1
2
3
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

The key idea is to create a dynamic proxy is by implementing InvocationHandler, then override the getProxy and invoke, in the invoke method we wrap the operation logic with routine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ServiceProxy implements InvocationHandler{

private Object target = null;

public Object getProxy(Object obj){
this.target = obj; //Save the object
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Start a database connection");
Object result = method.invoke(target, args);
System.out.println("Close a database connection");
return result;
}
}

Once we create ServiceProxy class, the next part is to use it to manipulate data, the test code is shown below:

1
2
3
4
5
6
7
public class TestDynamicProxy {
public static void main(String[] args) {
UserService service = (UserService) new ServiceProxy().getProxy(new UserServiceImpl());
service.addUser();
service.deleteUser();
}
}

The result should be consistent with the static proxy, but with tiny code. In the next part, I will explore the InvocationHandler in the JDK.

Demystify the magic behind the dynamic Proxy

There are not only one way to implement dynamic proxy in java, the way mentioned about is one of them called JDK Dynamic Proxy, we will focus on it in the part below:

JDK Dynamic Proxy

First we need to recap the procedure of create a dynamic Proxy:

a.Use a proxy class A Implements the InvocationHandler with a implemented class C, finish the invoke and getProxy method, we use the newProxyInstance function to return the real proxy B in getProxy method

b.Instantiaze the proxy B as b

c.Use b as the proxy of C to call function declared in the interface.

By analysis, we can know the fact that proxy class B is generated by the extant code, as for how to create a new class in JVM, we just need to generate the corresponding java file and compile it into binary .class file ,then use the class loader to load the file into corresponding position in JVM memory. We will not talk about the detailed code of it due to the limitation of time.

CGLIB

This is an open-source library to generate the class on low level, which means it skip the copiling time spent in JDK Proxy. We will talk about it later.