JDK dynamic proxy and Cglib dynamic proxy. JDK dynamic proxy must be based on the interface. Cglib does not have such a requirement. Earlier we discussed why JDK dynamic proxies must be based on interfaces, portals:

So is there really no limit to cGLIb dynamic proxies?

First, let’s implement a dynamic proxy using Cglib

Proxied object

public class Student {
    public void say(a){
        System.out.println("i'm student"); }}Copy the code

The proxy class

public class MyMethodInterceptor implements MethodInterceptor{

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before dosomething...");
        // Notice that the method call is not reflection.
        Object object = proxy.invokeSuper(obj, args);
        System.out.println("after dosomething...");
        returnobject; }}Copy the code

The test class

public static void main(String[] args) {
   System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "");
   Enhancer enhancer = new Enhancer();
   // Set the bytecode file of the target class
   enhancer.setSuperclass(Student.class);
   // Set the callback function
   enhancer.setCallback(new MyMethodInterceptor());
   // The create method formally creates the proxy class
   Student proxy = (Student)enhancer.create();
   proxy.say();
}
Copy the code

The results

before dosomething...
i'm student
after dosomething...
Copy the code

The dynamic proxy was found to have taken effect.

Dynamic proxies get the original class (proxied class), usually through inheritance and interface implementation, as follows

Student proxy = (Student)enhancer.create();
Copy the code

So we guessed that the Cglib proxy should be implemented through inheritance.

Fianl = fianl; fianl = fianl; fianl = fianl;

public final class Student {
    public void say(a){
        System.out.println("i'm student"); }}Copy the code

Then execute the previous test class, the execution result is as follows, it can be seen that there is indeed an error, and it indicates that the fianL class cannot be inherited, indicating that our analysis is correct.

Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class com.example.demo.cglib.Student
	at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:565)
	at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
	at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:332)
	at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
	at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
	at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
	at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
	at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
	at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
	at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
	at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
	at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
	at com.example.demo.cglib.Test.main(Test.java:16)
Copy the code

Let’s re-analyze the cglib source code to prove the above process.

Start with the enhancer.create() method, leaving only the key methods

public class Enhancer extends AbstractClassGenerator {...public Object create(a) {...return this.createHelper();
  }

  private Object createHelper(a) {
          this.preValidate(); ... Object result =super.create(key);
    returnresult; }}Copy the code

Then look at the super.create(key) method

public abstract class AbstractClassGenerator<T> implements ClassGenerator {
protected Object create(Object key) {...// Get the proxy object
       Object obj = data.get(this.this.getUseCache());
       return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj); }}public Object get(AbstractClassGenerator gen, boolean useCache) {
  if(! useCache) {return gen.generate(this);
  } else {
    Object cachedValue = this.generatedClasses.get(gen);
    returngen.unwrapCachedValue(cachedValue); }}protected Class generate(AbstractClassGenerator.ClassLoaderData data) {...// Generate bytes for the proxy class
    byte[] b = this.strategy.generate(this); ... }Copy the code

Finally, the following method is called

public byte[] generate(ClassGenerator cg) throws Exception {
  DebuggingClassWriter cw = this.getClassVisitor();
  this.transform(cg).generateClass(cw);
  // Generate bytes
  return this.transform(cw.toByteArray());
}
Copy the code

Now look at the cw. The toByteArray () method, you can see DebuggingClassWriter. DebugLocation can configure generated proxy class to the specified path.

public byte[] toByteArray() {
        return (byte[]) ((byte[])AccessController.doPrivileged(new PrivilegedAction() {
            public Object run(a) {
                byte[] b = ((ClassWriter)DebuggingClassWriter.access$001(DebuggingClassWriter.this)).toByteArray();
              / / if it is true, you can output the proxy class to DebuggingClassWriter. DebugLocation position
              if(DebuggingClassWriter.debugLocation ! =null) {
                    String dirs = DebuggingClassWriter.this.className.replace('. ', File.separatorChar);

                    try{(new File(DebuggingClassWriter.debugLocation + File.separatorChar + dirs)).getParentFile().mkdirs();
                        File file = new File(new File(DebuggingClassWriter.debugLocation), dirs + ".class");
                        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));

                        try {
                            out.write(b);
                        } finally {
                            out.close();
                        }

                        if(DebuggingClassWriter.traceCtor ! =null) {
                            file = new File(new File(DebuggingClassWriter.debugLocation), dirs + ".asm");
                            out = new BufferedOutputStream(new FileOutputStream(file));

                            try {
                                ClassReader cr = new ClassReader(b);
                                PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));
                                ClassVisitor tcv = (ClassVisitor)DebuggingClassWriter.traceCtor.newInstance(null, pw);
                                cr.accept(tcv, 0);
                                pw.flush();
                            } finally{ out.close(); }}}catch (Exception var17) {
                        throw newCodeGenerationException(var17); }}returnb; }})); }Copy the code

So, let’s modify the original test class as follows:

public static void main(String[] args) {
        // Generate a dynamic proxy class in the specified directory, we can decompile to see what is inside
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/ganhuojun/code/my_demo/target");
        Enhancer enhancer = new Enhancer();
        // Set the bytecode file of the target class
        enhancer.setSuperclass(Student.class);
        // Set the callback function
        enhancer.setCallback(new MyMethodInterceptor());
        // The create method formally creates the proxy class
        Student proxy = (Student)enhancer.create();
        proxy.say();
    }
Copy the code

The command output is as follows, and view the corresponding directory

CGLIB debugging enabled, writing to '/Users/ganhuojun/code/my_demo/target'
before dosomething...
i'm student
after dosomething...
Copy the code

Student$$EnhancerByCGLIB$$8b03b040.class, cglib implements dynamic proxying. The class must not be modified by FIanL

public class Student$$EnhancerByCGLIB$$8b03b040 extends Student implements Factory {
  // Omit the content... }Copy the code

Conclusion:

Cglib implements dynamic proxies, requiring classes that cannot be modified by FIANL. By extension, if a method is final, can it be executed by an agent?