Introduction to dynamic proxies

Dynamic proxy is a mechanism that dynamically creates proxy objects and handles proxy method calls at run time.

It is actually a proxy mechanism. Proxy can be regarded as an encapsulation of the calling target, directly through the proxy to achieve the call to the object code

Comparison with static proxies

Static agent

Write a proxy class in advance, each business class must correspond to a proxy class, inflexibility

  • ISubject, which is the interface to be visited or the object to be accessed
  • SubjectImpl, the concrete implementation class of the object being accessed
  • SubjectProxy, the proxy implementation class of the visited or accessed resource
  • Client: An abstract role representing the visitor. The Client will access objects or resources of type Isubject, accessed through the proxy class.
  • Both SubjectImpl and SubjectProxy implement the ISubject interface. SubjectProxy forwards requests to SubjectImpl, contains SubjectImpl objects, and can impose restrictions

The diagram above is essentially an explanation of the proxy pattern, and applies to both static and dynamic proxies. The difference is when and how proxy objects and proxy methods are generated.

Dynamic proxy is the automatic generation of proxy objects at runtime. One disadvantage is the time required to generate proxy objects and invoke proxy methods

Dynamic proxy implementation

There are two main implementations of dynamic proxies: JDK dynamic proxies, CGLIB bytecode mechanisms, and, of course, Javassist or ASM libraries, both of which are covered in the CGLIB section

JDK dynamic proxy

When it comes to JDK dynamic proxies, we have to mention reflection, which is how JDK dynamic proxies are implemented.

Reflection mechanism

Reflection is getting information about a Class at run time through the Class Class and the java.lang.Reflect Class library. For example, the java.lang.reflect class library provides information about classes from the Field,Method, and Constructor classes

JDK dynamic proxy implementation

Here is a simple example of implementing JDK dynamic proxies through reflection

/** @author pjmike * @create 2018-08-04 17:42 */ public class DynamicProxy {public static void main(String[] args) { IHelloImpl hello = new IHelloImpl(); MyInvocationHandler handler = new MyInvocationHandler(hello); / / obtain the target user agent object IHello proxyHello = (IHello) Proxy. NewProxyInstance (IHelloImpl. Class. GetClassLoader (), IHelloImpl.class.getInterfaces(), handler); // Invoke the proxy method proxyhello.sayHello (); } /** * interface IHello{void sayHello(); } /** * implements IHello {@override public void implements IHellosayHello() {
        System.out.println("Hello World"); } } class MyInvocationHandler implements InvocationHandler { private Object target; /** ** @param target target Object */ public MyInvocationHandler(Object target) {this.target = target; } /** * method of executing the target object ** @param proxy Proxy object * @param method Proxy method * @param args method parameter * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoke method");
        System.out.println("Method name : "+method.getName());
        Object result = method.invoke(target, args);
        returnresult; }}Copy the code

As you can see from the above example, Proxy object generation is done with proxy.newProxyInstance ()

public static Object newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h)Copy the code

The newProxyInstance() method takes the following three parameters

  • The ClassLoader is used to load dynamic proxy classes
  • An array that implements an interface. As you can see from this, you must have an interface class to use JDK dynamic proxies
  • An implementation of the InvocactionHandler interface

The dynamic proxy can redirect all calls to the calling handler, so a reference to an “actual” object is typically passed to the constructor of the calling handler, allowing the processor to request forwarding when it performs its task.

CGLIB is used to realize dynamic proxy

Cglib is an ASM-based bytecode generation library for generating and converting Java bytecode.

ASM is a lightweight but high-performance framework for bytecode manipulation. Cglib is an asM-based upper layer application that is useful for classes where the proxy does not implement an interface.

A simple example of a CGLIB dynamic proxy

Essentially, for classes that need to be propped, it simply dynamically generates a subclass to override non-final methods while binding hooks to call back custom interceptors.

Add CGLIB dependencies

<dependency> <groupId>cglib</ artifactId> <version>3.2.4</version> /dependency>Copy the code
package com.pjmike.proxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * CGLIB proxy implementation ** @author pjmike * @create 2018-08-06 16:55 */ public class CglibProxy {public static void Main (String[] args) {//Enhancer = new Enhancer(); Enhancer = new Enhancer(); enhancer.setSuperclass(PersonService.class); // Set the required interceptor for the callback, enhancer.setCallback(new MyMethodInterceptor()); / / by enhancer. The create () method to obtain a proxy object / / to proxy objects will be forwarded to all the final method call MethodInterceptor. Intercept method, PersonService PersonService = (PersonService) enhancer.create(); System.out.println(personService.sayHello("pjmike"));
    }
}

class PersonService {
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        returnmethodProxy.invokeSuper(obj, args); }}Copy the code

Like ASM, Javassist requires direct manipulation of bytecode, so it has a higher threshold and is generally used in the underlying implementation of the framework. Hibernate, for example, uses Javassist and Cglib underneath.

javassist

Javassist is a runtime compilation library that can dynamically generate or modify the bytecode of a class. It has two options for implementing dynamic proxies: javassist’s dynamic proxy interface and JavAssist bytecode.

Because Javassist is slow to provide a dynamic proxy interface, I’ll focus on javassist bytecode and leave its dynamic proxy interface out of the equation. For the performance comparison of these proxy schemes, see dynamic proxy scheme performance comparison.

Javassist consists of CtClass,CtMethod, and CtField classes, similar to Class,Method, and Field in JDK reflection.

Simple to use

Add the dependent

< the dependency > < groupId > org. Javassist < / groupId > < artifactId > javassist < / artifactId > < version > 3.21.0 - GA < / version > </dependency>Copy the code

code

package com.pjmike.proxy; import javassist.*; /** * javassist bytecode ** @author pjmike * @create 2018-08-07 0:07 */ public class JavassistByteCode {public static void main(String[] args) throws IllegalAccessException, CannotCompileException, InstantiationException, NotFoundException { ByteCodeAPI byteCodeApi = createJavassistBycodeDynamicProxy(); System.out.println(byteCodeApi.sayHello()); } public static ByteCodeAPI createJavassistBycodeDynamicProxy() throws CannotCompileException, IllegalAccessException, InstantiationException, NotFoundException {ClassPool pool = classpool.getDefault (); / / created dynamically CtClass cc = pool. MakeClass (ByteCodeAPI. Class. GetName () +"demo"); cc.addInterface(pool.get(ByteCodeAPI.class.getName())); // create property CtField field = ctfield.make ("private String a;", cc); cc.addField(field); // create method CtMethod = ctmethod.make ("public String sayHello() {return \"hello\"; }", cc); cc.addMethod(method); / / add the constructor cc. AddConstructor (CtNewConstructor. DefaultConstructor (cc)); Class<? > pc = cc.toClass(); ByteCodeAPI byteCodeApi = (ByteCodeAPI) pc.newInstance();returnbyteCodeApi; } } interface ByteCodeAPI { public String sayHello(); } // Result: Output helloCopy the code

Practical application of dynamic proxy

Dynamic proxies actually have many applications, such as implementation of Spring AOP, implementation of RPC frameworks, internal use of some third-party tool libraries, and so on. Here is a brief introduction to the use of dynamic proxies in spring AOP and RPC frameworks

Application one: Dynamic proxy implementation of Spring AOP

There are two main ways to implement Dynamic proxies in Spring AOP, JDK dynamic proxies and CGLIB bytecode generation.

By default, if Spring AOP implements an interface after discovering a target object, it uses the JDK dynamic proxy mechanism to generate a proxy object for it. If no interface is found, CGLIB is used to generate a dynamic proxy object instance for the target object

Application 2: Application in RPC framework

RPC is remote procedure call, its implementation used dynamic proxy, about the specific principle of RPC refer to you should know the principle of RPC and other articles

In fact, one of the problems RPC frameworks solve is: how do you call someone else’s remote service? Call a remote service as if it were a local service.

Dynamic proxies are needed to encapsulate data, transmit it over the network to remote services, and make the remote interface transparent. Therefore, transparent remote service invocation is to use dynamic proxy to encapsulate data at the proxy layer, network transmission and other operations.

summary

In practice, we often use off-the-shelf frameworks and open source libraries that include dynamic proxy applications. Only by truly understanding the knowledge of dynamic proxy, can we better understand its design in the framework, and is conducive to better use of the framework.

The resources

  • JDK dynamic proxy
  • Java Programming ideas
  • Introduction to CGLIB dynamic proxy
  • Javassist Tutorial-1