Adolph
Engineer
Engineer
  • UID623
  • Fans4
  • Follows1
  • Posts72
Reads:3441Replies:0

Java and CGLIB Dynamic Proxies

Created#
More Posted time:Sep 5, 2016 9:33 AM
To discuss about the dynamic proxies, we should first clarify about statistic proxies. The so-called statistic proxies are the proxy classes implemented by the programmer in advance, and the class files already exist after compilation.
The implementation principle is to, using the Java proxy pattern, allow a proxy class to hold the instance of the delegate class, and implement the same interface as the delegate class in order to enhance the methods.
We mainly use it to enhance the methods, so that you can enhance some methods without modifying the source code, do whatever you want to do before and after implementation of the method, and don't even need to implement such a method, because in the invoke method of InvocationHandler, you can get the Method object corresponding to the method invoked directly. For example, you can add invocation logs, implement transaction control, cache the methods and so on.
The Spring container will replace the factory, and the Spring AOP will replace the JDK dynamic proxy, which makes it easier to implement the aspect-oriented programming. With the help of Spring, you can easily add or remove any dynamic proxy without affecting the source code.
This article gives three examples: statistic proxy, JDK dynamic proxy and CGLIB dynamic proxy.
I. Statistic proxy
Look at the following code after understanding the proxy pattern. There is nothing else to say about it.
package com.shanhy.demo.proxy;
public interface Account {
    public void queryAccount();
    public void updateAccount();
}


package com.shanhy.demo.proxy;
public class AccountImpl implements Account {
    @Override
    public void queryAccount() {
        System.out.println("View the account");
    }

    @Override
    public void updateAccount() {
        System.out.println("Modify the account");
    }
}


package com.shanhy.demo.proxy;
public class AccountProxy implements Account {
    private Account account;
    public AccountProxy(Account account) {
        super();
        this.account = account;
    }

    @Override
    public void queryAccount() {
        System.out.println("Implement proxy before");
        account.queryAccount();
        System.out.println("Implement proxy after");
    }

    @Override
    public void updateAccount() {
        System.out.println("Implement proxy before");
        account.updateAccount();
        System.out.println("Implement proxy after");
    }
}


package com.shanhy.demo.proxy;
public class AccountProxyTest {
    public static void main(String[] args) {
        //AccountProxy is a self-implemented proxy class, and we can find that a proxy class can only serve an interface.
        Account account = new AccountImpl();
        AccountProxy proxy = new AccountProxy(account);
        proxy.queryAccount();
        proxy.updateAccount();
    }
}


II. JDK dynamic proxy
The use of JDK dynamic proxy involves a Proxy class and an InvocationHandler interface.
Proxy has been designed very beautiful, but it is a little bit pity that it only supports interface proxy (namely, the proxy class must implement interfaces), and this pity is doomed because of its design.
package com.shanhy.demo.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class AccountProxyFactory implements InvocationHandler {

    private Object target;

    public Object bind(Object target){
        //JDK dynamic proxy is used here, which must be bound with interfaces, and our businesses may be implemented without involving the interfaces. So this defect is remedied by CGLIB.
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//      System.out.println(Proxy.isProxyClass(proxy.getClass()));
        boolean objFlag = method.getDeclaringClass().getName().equals("java.lang.Object");

        Object result = null;
        if(!objFlag)
            System.out.println("Implement proxy before");
        result = method.invoke(this.target, args);
        if(!objFlag)
            System.out.println("Implement proxy after");
        return result;
    }
}


package com.shanhy.demo.proxy;

public class AccountProxyTest {

    public static void main(String[] args) {
       //For the JDK proxy classes used below, one proxy can manage many interfaces.
        Account account1 = (Account)new AccountProxyFactory().bind(new AccountImpl());
        System.out.println(account1);
        account1.queryAccount();
}


III. CGLIB dynamic proxy
For the defect mentioned above that JDK only supports proxy of the delegate class with interface implemented, CGLIB solves this problem, so that its delegate class can also be a non-interface implemented class.
CGLIB requires use of ASM, so we need to introduce asm-3.3.jar and cglib-2.2.2.jar in the following examples.
package com.shanhy.demo.proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class AccountCglibProxyFactory implements MethodInterceptor{

    private Object target;

    public Object getInstance(Object target){
        this.target = target;
        return Enhancer.create(this.target.getClass(), this);

//      Enhancer enhancer = new Enhancer();//This class is used to generate a proxy object.
//      enhancer.setSuperclass(this.target.getClass());//Set the parent class.
//      enhancer.setCallback(this);//Set the callback object to itself.

//      return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
       //Exclude the methods such as toString in the Object class.
        boolean objFlag = method.getDeclaringClass().getName().equals("java.lang.Object");
        if(!objFlag){
            System.out.println("before");
        }
        Object result = null;
//      We usually use the proxy.invokeSuper(obj,args) method. This is easy to understand - it is the method to implement the original class. There is a another method: proxy.invoke(obj,args), which is a method to implement the subclass generated.
//      If the incoming object is a subclass, an out-of-memory exception will occur, because the subclass method enters the intercept method constantly, and this method again comes to invoke the subclass method, which leads to loop calls of the two methods.
        result = methodProxy.invokeSuper(obj, args);
//      result = methodProxy.invoke(obj, args);
        if(!objFlag){
            System.out.println("after");
        }
        return result;
    }
}


package com.shanhy.demo.proxy;

public class Person {

    public void show(){
        System.out.println("showing");
    }
}


package com.shanhy.demo.proxy;

public class AccountProxyTest {

    public static void main(String[] args) {
        //The following are the proxies using CGLIB.
        //1. Support the classes with interface implemented
        Account account2 = (Account)new AccountCglibProxyFactory().getInstance(new AccountImpl());
        account2.updateAccount();

        // 2. Support the classes with interface unimplemented
        Person person = (Person)new AccountCglibProxyFactory().getInstance(new Person());
        System.out.println(person);
        person.show();
    }
}
Guest