Proxy mode refers to providing a proxy object to an object that the user does not directly access to the original object but indirectly through the proxy object.
We can use the proxy pattern to implement aspect-oriented programming (AOP), which is used by dynamic agents to weave the slice functionality into the target method without intruding into the caller's business code.
or using proxy mode to implement remote procedure call (RPC), the caller invokes the proxy method as if it were a local method without having to worry about the proxy calling the remote method details.
The JDK provides a dynamic proxy implementation based on the reflection mechanism, while the widely used third-party library, Cglib, implements dynamic proxies based on the bytecode manipulation framework asm.
In this paper, we will briefly introduce two dynamic proxy usage methods.
Content
- Proxy mode
- Java Dynamic Agent
- Cglib Dynamic Agent
Proxy mode
Implementing the static proxy pattern is very simple, first we define an interface:
publicinterface MyInterface { voidfoo();}
Write the object that is being proxied:
publicclassimplements MyInterface { @Override publicvoidfoo() { System.out.println("foo"); }}
Classes that are proxied in proxy mode are often referred to as delegate classes.
To write a static proxy:
public myproxy implements myinterface {private MyInterface Subject myproxy (MyInterface subject) {this . subject = subject; } @Override public void () {long start = System.. currenttimemillis (); Subject. foo (); long elapsetime = System. currenttimemillis ()-Start; System. out . println ( "elapse time:" + elapsetime); }}
A static proxy is a proxy class that is generated at compile time, and the relative dynamic proxy is the bytecode of the runtime generation proxy class and loaded into the JVM.
The problem with static proxies is that you must understand the details of the interface when you write the proxy class- MyProxy
the definition that you must understand when writing MyInterface
.
In the case of an AOP framework, the framework does not know beforehand that interface information can only create proxy objects from class objects at run time, so writing such a framework must be supported by dynamic proxy mechanisms.
Java Dynamic Agent
The reflection mechanism of Java can create classes at run time, and the Java Standard Library provides a reflection-based proxy mechanism.
The proxy class should implement java.lang.reflect.InvocationHandler
an interface that has only one method: invoke
.
publicinvoke(Object proxy, Method method, Object[] args) throws Throwable;
The Invoke method of the proxy class is invoked when we access the delegate class method through the proxy. We can get the invocation information through the parameters of the Invoke method, and call the method of the delegate class using the reflection mechanism.
The three parameters of the Invoke method are:
Object porxy
: The dynamic proxy instance of the invoked method
Method method
: The method being called
Object[] args
: Parameters passed in when called
java.lang.reflect.Proxy.newProxyInstance
method is used to create a proxy object that uses the reflection mechanism to create a proxy class and load it into the JVM at run time. The method has three parameters:
ClassLoader loader
: Loading the Agent class loader
Class<?>[] interfaces
: The method to be implemented by the proxy class
InvocationHandler h
: When invoking the proxy class method, embed the Invocationhandler instance of the calling procedure
The direct description is more difficult to understand, let's look at the code:
Public classMyproxyImplementsInvocationhandler {PrivateObject subject; Public myproxy(Object subject) { This.subject= subject; }@Override PublicObjectInvoke(Object Proxy, Method method, object[] args)throwsThrowable {//1 returnMethod.Invoke(subject, args); }@SuppressWarnings("Unchecked") Public Static<T> TCreateproxy(T subject) {returnT Proxy.newproxyinstance(subject.GetClass().getClassLoader(),//2Subject.GetClass().getinterfaces(),New myproxy(subject)); } Public Static void Main(string[] args) {MyInterface obj =Createproxy(New MyService());//3Obj.Foo(); }}
MyInterface
and MyService
exactly the same as above:
publicinterface MyInterface { voidfoo();}publicclassimplements MyInterface { @Override publicvoidfoo() { System.out.println("foo"); }}
MyProxy
The class implements the InvocationHandler
interface and provides a createProxy
static method.
In the main
method we MyService
created a proxy for the object and called the method through the proxy foo
.
There are several details in the code that are worth knowing:
In the method, the main
obj.getClass().getName()
class name by obtaining the proxy class is: com.sun.proxy.$Proxy0
. This class name indicates that the class is a dynamically generated proxy class, and 0 indicates that it is the first dynamic proxy class in the current JVM.
- At note 2 We
newProxyInstance
created the proxy class, which is the class in the preceding section $Proxy0
:
loader
is the loader for the class being proxied and can also be used MyInterface.class.getClassLoader()
, both of which are appclassloader instances.
interfaces
Parameters are subject.getClass().getInterfaces()
all interfaces that represent the dynamic proxy class that $Proxy0
implement subject, but are $Proxy0
not subclasses of the subject class.
It is therefore main
used in methods MyInterface obj = createProxy(new MyService());
and cannot be used MyService obj = createProxy(new MyService());
.
- We use the
MyProxy
instance as Invocationhandler to intercept all method calls of the dynamic proxy class.
- At note 1 We implemented a
invoke
method to intercept all method invocations of the dynamic proxy object:
- The passed-in argument is an instance of the
Object proxy
dynamic proxy class, which main
is the method, which is an instance of the obj
$Proxy0
class. Note The Invocationhandler instance is not a dynamic proxy instance, and handler is a static class generated by the compilation.
- The passed-in argument
Method method
is the method object of the interface class, MyInterface.class.getMethod("foo")
not MyService
the method object of the delegate class
method.invoke(subject, args)
The method of the proxy instance is invoked in a reflective manner, and we can invoke
add additional code to the method to enhance the function of the delegate class
Cglib Dynamic Agent
Cglib is a powerful dynamic agent library, SPRINGAOP and DYNAOP frameworks use Cglib for method interception and enhancement, Hibernate uses Cglib for proxy affinity, and Jmock also uses Cglib to provide mock objects.
On the implementation, Cglib uses the high-performance lightweight bytecode manipulation framework ASM to dynamically generate proxy classes. It is worth mentioning that Cglib is faster than the JDK dynamic agent.
Cglib implements a dynamic proxy class as a subclass of a delegate class, so you can delegate classes that do not implement an interface or extend an interface. Because subclasses cannot overwrite the final method, they can only proxy non-final methods.
First use MAVEN to import cglib dependencies:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.6</version></dependency>
To write a cglib dynamic agent:
Public classCglibproxy {@SuppressWarnings("Unchecked") Public Static<T> TCreateproxy(T subject) {Enhancer Enhancer =New Enhancer(); Enhancer.Setsuperclass(subject.GetClass()); Enhancer.Setcallback(New Myinterceptor());return(T) enhancer.Create(); }Private Static classMyinterceptorImplementsMethodinterceptor {@Override PublicObjectIntercept(Object obj, method method, object[] args, Methodproxy proxy)throwsThrowable {returnProxy.Invokesuper(obj, args); } } Public Static void Main(string[] args) {MyService obj =Createproxy(New MyService()); Obj.Foo(); }}
Enhancer
Used to enhance the delegate class, which can intercept method calls to implement the proxy mechanism.
MethodInterceptor.intercept
method is used to intercept a method call, and several of its parameters are:
Object obj
: Dynamic Proxy Object
Method method
: The method that is intercepted, the method that is intercepted in this example main
is:MyService.class.getMethod("foo")
Object[] args
: Invoke argument
MethodProxy proxy
: The shortcut proxy used to invoke the delegate class method, not the dynamic proxy class.
The dynamic agent defined in the example does not add any additional functionality, and this special callback can be used net.sf.cglib.proxy.NoOp
instead:
publicstaticcreateProxy(T subject) { newEnhancer(); enhancer.setSuperclass(subject.getClass()); enhancer.setCallback(NoOp.INSTANCE); return (T) enhancer.create(); }
Enhancer can use Callbackfilter to configure filters for each method call:
classMyService {void Foo0() {System. out.println("0"); }void foo1() {System. out.println("1"); }} Public classCglibproxy {@SuppressWarnings("Unchecked") Public Static<T> TCreateproxy(T subject) {Enhancer Enhancer =New Enhancer(); Enhancer.Setsuperclass(subject.GetClass()); Callback callback0 = NoOp.INSTANCE; Callback callback1 = NoOp.INSTANCE; Enhancer.Setcallbacks(NewCallback[]{callback0, callback1}); Enhancer.Setcallbackfilter(New Mycallbackfilter());return(T) enhancer.Create(); }Private Static classMycallbackfilterImplementsCallbackfilter {@Override Public int Accept(method) {if("Foo0".equals(method.GetName())) {return 0; }Else{return 1; } } } Public Static void Main(string[] args) {MyService obj =Createproxy(New MyService()); Obj.Foo0(); Obj.foo1(); }}
After enhancer sets the Callbackfilter, the method is called before the method is intercepted to CallbackFilter.accept()
determine which callback instance to use for interception.
CallbackFilter.accept()
The return value is the subscript of the interceptor, and the even if accept method returns 0, and the call is enhancer.callbacks[0]
intercepted.
Java Dynamic Agent