1. Proxy Pattern
The proxy pattern is a relatively well understood design pattern. Simply put we use a proxy object to replace access to the real object (real object), so that we can provide additional functional operations to extend the functionality of the target object without modifying the original target object.
The main purpose of the proxy pattern is to extend the functionality of the target object, for example, you can add some custom operations before and after the execution of a method of the target object.
There are two implementations of the proxy pattern: static proxy and dynamic proxy, so let’s look at the static proxy implementation first.
2. Static Proxy
In static proxy, we enhance each method of the target object is done manually ( later will demonstrate the specific code ), very inflexible ( for example, once a new method is added to the interface, both the target object and the proxy object have to be modified ) and troublesome ( need to write a separate proxy class for each target class ). Practical application scenarios are very, very rare, and everyday development hardly sees scenarios where static proxies are used.
From the JVM level, static proxies turn interfaces, implementation classes, and proxy classes into actual class files at compile time.
Static proxy implementation steps:
- define an interface and its implementation class.
- create a proxy class that also implements the interface
- inject the target object into the proxy class, and then call the corresponding method in the target class in the corresponding method of the proxy class. In this way, we can block access to the target object through the proxy class and do what we want to do before and after the execution of the target method.
The following shows by code!
1. Define the interface for sending SMS messages
2. Implement the interface for sending SMS messages
3. Create a proxy class and implement the same interface for sending SMS messages
|
|
4. Actual use
After running the above code, the console prints.
As you can see from the output, we have added the send()
method of SmsServiceImpl
.
3. Dynamic Proxy
Dynamic proxies are more flexible than static proxies. Instead of creating a separate proxy class for each target class, and instead of having to implement interfaces, we can directly proxy the implementing class ( CGLIB dynamic proxy mechanism).
From the JVM perspective, dynamic proxies are dynamically generated at runtime to generate class bytecode and load it into the JVM.
Speaking of dynamic proxies, Spring AOP, RPC framework should be two that have to be mentioned, their implementations rely on dynamic proxies.
Dynamic proxies are used relatively little in our daily development, but they are almost a must-have technology in the framework. After learning dynamic proxies, it is also very helpful for us to understand and learn the principles of various frameworks.
In terms of Java, there are many different ways to implement dynamic proxies, such as JDK dynamic proxy, CGLIB dynamic proxy, etc.
3.1. JDK Dynamic Proxy Mechanism
3.1.1. Introduction
The InvocationHandler
interface and the Proxy
class are the core of the Java dynamic proxy mechanism.
The most frequently used method in the Proxy
class is: newProxyInstance()
, which is mainly used to generate a proxy object.
This method has a total of 3 parameters.
- loader : class loader for loading the proxy object.
- interfaces : Some interfaces implemented by the proxied class.
- h : An object that implements the
InvocationHandler
interface.
To implement dynamic proxies, you must also implement InvocationHandler
to customize the processing logic. When our dynamic proxy object calls a method, the method call is forwarded to the invoke
method of the class that implements the InvocationHandler
interface to be called.
The invoke()
method has the following three parameters.
- proxy : the dynamically generated proxy class
- method : the method corresponding to the method called by the proxy class object
- args : the parameters of the current method method
In other words:The proxy object you create with newProxyInstance()
of the Proxy
class will actually call the invoke()
method of the class that implements the InvocationHandler
interface when it invokes the method. You can customize the processing logic in the invoke()
method, such as what to do before and after the method execution.
3.1.2. Steps for using JDK dynamic proxy classes
- define an interface and its implementation classes.
- customize
InvocationHandler
and override theinvoke
method, in which we call the native methods (methods of the proxied class) and customize some processing logic. - pass
Proxy.newProxyInstance(ClassLoader loader,Class<? >[] interfaces,InvocationHandler h)
method to create the proxy object.
3.1.3. Code example
This may be a bit empty and difficult to understand, so I’ll give you an example to get a feel for it!
1. Define the interface for sending SMS messages
2. Implement the interface for sending SMS messages
3. Define a JDK dynamic proxy class
|
|
invoke()
method: When our dynamic proxy object calls the native method, it actually ends up calling the invoke()
method, and then the invoke()
method calls the native method of the proxy object instead.
4. Get the factory class of the proxy object
|
|
getProxy()
: mainly through the Proxy.newProxyInstance()
method to get the proxy object of a class
5. Practical use
After running the above code, the console prints out.
3.2. CGLIB Dynamic Proxy mechanism
3.2.1. Introduction
One of the most fatal problems with JDK dynamic proxies is that they can only proxy classes that implement an interface.
To solve this problem, we can use the CGLIB dynamic proxy mechanism to avoid it.
CGLIBopen in new window(Code Generation Library) is a byte-based ASMopen in new window bytecode generation library, which allows us to modify and dynamically generate bytecode at runtime. CGLIB implements proxies by inheritance. Many well-known open-source frameworks use CGLIBopen in new window, for example, the AOP module in Spring: if the target object implements the interface, the JDK dynamic proxy is used by default, otherwise the CGLIB dynamic proxy is used.
The MethodInterceptor
interface and the Enhancer
class are the core of the CGLIB dynamic proxy mechanism.
You need to customize MethodInterceptor
and override the intercept
method, which is used to intercept methods that enhance the proxied class.
- obj : dynamically generated proxy object
- method : intercepted methods (methods that need to be enhanced)
- args : method entry
- proxy : used to call the original method
You can use the Enhancer
class to get the proxied class dynamically. When the proxy class calls the method, the actual call is to the intercept
method in the MethodInterceptor
.
3.2.2. Steps for using CGLIB dynamic proxy classes
- define a class.
- customize
MethodInterceptor
and override theintercept
method,intercept
is used to intercept methods that enhance the proxied class, similar to theinvoke
method in JDK dynamic proxies. - create the proxy class via
create()
of theEnhancer
class.
3.2.3. Code example
Unlike the JDK dynamic proxies do not require additional dependencies. CGLIBopen in new window(Code Generation Library) is actually part of an open source project and you need to add the dependencies manually if you want to use it.
1. Implement a class that sends SMS messages using Aliyun
2. Custom MethodInterceptor
(method interceptor)
|
|
3. Get the proxy class
|
|
4. Actual use
After running the above code, the console prints out.
3.3. Comparison of JDK Dynamic Proxy and CGLIB Dynamic Proxy
- JDK dynamic proxy can only proxy classes that implement interfaces or proxy interfaces directly, while CGLIB can proxy classes that do not implement any interfaces. In addition, CGLIB dynamic proxy intercepts the method calls of the proxied class by generating a subclass of the proxied class, so it cannot proxy classes and methods declared as final. 2. As far as the efficiency of the two is concerned, the JDK dynamic proxy is superior in most cases, and this advantage becomes more obvious as the JDK version is upgraded.
4. Comparison of Static and Dynamic Proxy
- Flexibility : Dynamic proxy is more flexible, does not need to have to implement the interface, can directly proxy the implementation class, and can not need to create a proxy class for each target class. In addition, static proxies, once the interface is newly added methods, the target object and the proxy object have to be modified, which is very troublesome!
- JVM level: Static proxies turn interfaces, implementation classes, and proxy classes into actual class files at compile time. Dynamic proxies, on the other hand, dynamically generate class bytecode at runtime and load it into the JVM.
5. Summary
This article introduces two implementations of the proxy pattern: static proxies and dynamic proxies. The article covers the difference between static proxy and dynamic proxy, the difference between static proxy and dynamic proxy, the difference between JDK dynamic proxy and Cglib dynamic proxy, and so on.