Spring AOP的基石--Java动态代理

Posted NoYone

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring AOP的基石--Java动态代理相关的知识,希望对你有一定的参考价值。

一、代理模式简介以及静态代理动态代理区别

 1. 关系图

 

静态代理:可以看出有一接口Sourceable,两个实现类Source,Proxy,Client调用的Proxy。理解一下就是Client通过Proxy来调用这个method,而不是直接通过Source来调用。

这就像我们的电脑开代理的时候,通过代理上网,而不是直连网络。

从这张类图中也可以看出实现代理模式的两个要点:

  ① 代理类和被代理类必须要实现同一接口 (看下面代码好理解,就是为了面向接口编程)

  ② 代理类的核心是做功能的增强,真正的method还是被代理类提供

  (不好意思,下面这段代码体现的是装饰模式,知道真相的我眼泪掉下来,留着不改了,做个纪念)

装饰模式和代理模式的区别:代理模式在Proxy类中即需要new出被代理类,而装饰模式即是下面这样的在外面传入。

 public class ProxyDemo {
public static void main(String args[]){ RealSubject subject = new RealSubject(); Proxy p = new Proxy(subject); p.request(); } } interface Subject{ void request(); } class RealSubject implements Subject{ public void request(){ System.out.println("request"); } }
// 面向接口在这里有体现,subject
class Proxy implements Subject{ private Subject subject; public Proxy(Subject subject){ this.subject = subject;
    // 真正的代理模式会这样写,两种模式的区别自己体会。
    //this.subject = new RealSubject;
    //这样写之后在main函数中只要new Proxy()就好了 }
public void request(){ System.out.println("PreProcess"); subject.request(); System.out.println("PostProcess"); } }

2. 动态代理和静态代理区别

  • 静态代理:代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。
  • 动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成字节码,并加载到JVM中。

动态代理生成代理类类名以及字节码过程:

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
View Code

二、动态代理的实现以及实现原理

1. 声明共同接口

public interface Subject {
    void dating();
    void sing();
}

2. 设置被代理类,也叫委托类

public class WeiTuoLei implements Subject{
    @Override
    public void dating() {
        System.out.println("Time0");
    }

    @Override
    public void sing() {
        System.out.println("Sing a Song");
    }
}

class WeiTuoLei1 implements Subject{

    @Override
    public void dating() {
        System.out.println("Time1");
    }

    @Override
    public void sing() {
        System.out.println("Dance");
    }
}
View Code

3. 设置代理类的调用处理器(需实现InvocationHandler接口)

class ProxyHandler implements InvocationHandler{
    // 真正被代理的对象也叫做委托对象
    Subject subject;
    public ProxyHandler(Subject subject){
        this.subject = subject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        dosomethingbefore();
        Object invoke = method.invoke(subject, args);
        dosomethingafter();
        return invoke;
    }

    private void dosomethingbefore() {
        System.out.println("==========before=======");
    }

    private void dosomethingafter() {
        System.out.println("==========after=======");
    }
}
View Code

4. 测试

public class Main {
    public static void main(String[] args) {
        WeiTuoLei realSubject = new WeiTuoLei();  //1.创建委托对象
        ProxyHandler handler = new ProxyHandler(realSubject); //2.创建调用处理器对象
        //3.动态生成代理对象
        Subject o = (Subject)Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
     //JDK内部就是通过这个方法生成的代理对象,我把它拿出来,咱们就可以看到动态生成了一个什么样的代理对象。 ProxyUtils.generateClassFile(WeiTuoLei.
class,o.getClass().getName()); /** * Proxy.newProxyInstance等于如下动作 * //1. 根据类加载器和接口创建代理类 Class clazz = Proxy.getProxyClass(loader, interfaces); //2. 获得代理类的带参数的构造函数 Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); //3. 创建代理对象,并制定调用处理器实例为参数传入 Interface Proxy = (Interface)constructor.newInstance(new Object[] {handler}); */ //4.通过代理对象调用方法 o.sing(); } }

我的ProxyUtils(可以通过这个工具类看到动态生成的代理对象,其实就是把字节码保存了一下)

public class ProxyUtils {

    public static void generateClassFile(Class clazz, String proxyName) {
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                clazz.getName(), new Class[]{clazz});

        String path = clazz.getResource(".").getPath() + proxyName + ".class";
        File file = new File(path);
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(path);

        FileOutputStream out = null;

        try {
            out = new FileOutputStream(path);
            out.write(proxyClassFile);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
View Code

好,接下来我们来解剖一下它生成的这个代理类(字节码文件),用反编译工具看一下具体内容。

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

public final class WeiTuoLei extends Proxy implements WeiTuoLei {
    private static Method m1;
    private static Method m3;
    private static Method m9;
    private static Method m2;
    private static Method m4;
    private static Method m7;
    private static Method m6;
    private static Method m8;
    private static Method m10;
    private static Method m0;
    private static Method m5;

    public WeiTuoLei(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void dating() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void notify() throws  {
        try {
            super.h.invoke(this, m9, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void sing() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void wait(long var1) throws InterruptedException {
        try {
            super.h.invoke(this, m7, new Object[]{var1});
        } catch (RuntimeException | InterruptedException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    public final void wait(long var1, int var3) throws InterruptedException {
        try {
            super.h.invoke(this, m6, new Object[]{var1, var3});
        } catch (RuntimeException | InterruptedException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

    public final Class getClass() throws  {
        try {
            return (Class)super.h.invoke(this, m8, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void notifyAll() throws  {
        try {
            super.h.invoke(this, m10, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void wait() throws InterruptedException {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | InterruptedException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("DealDynamicProxy.WeiTuoLei").getMethod("dating");
            m9 = Class.forName("DealDynamicProxy.WeiTuoLei").getMethod("notify");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("DealDynamicProxy.WeiTuoLei").getMethod("sing");
            m7 = Class.forName("DealDynamicProxy.WeiTuoLei").getMethod("wait", Long.TYPE);
            m6 = Class.forName("DealDynamicProxy.WeiTuoLei").getMethod("wait", Long.TYPE, Integer.TYPE);
            m8 = Class.forName("DealDynamicProxy.WeiTuoLei").getMethod("getClass");
            m10 = Class.forName("DealDynamicProxy.WeiTuoLei").getMethod("notifyAll");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m5 = Class.forName("DealDynamicProxy.WeiTuoLei").getMethod("wait");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
View Code

看着代码很多,实际上就是把一个类里的各个方法通过反射拿了出来,我这里看不到代理类的两个方法,因为我没法传参,但是大致思想就是这样。需要注意的是各个方法里写的都是super.h.invoke,那么这个super.h就是继承了invocationHandler的那个类(被代理类)。具体invoke的调用时机,可以看下面这边博客:

https://blog.csdn.net/zcc_0015/article/details/22695647

以上是关于Spring AOP的基石--Java动态代理的主要内容,如果未能解决你的问题,请参考以下文章

详解 spring AOP 动态代理

Java动态代理学习Spring AOP基础之一

Java--Spring之AOP面向切面编程

Spring的AOP总结

Java动态代理+注解体现Spring AOP思想

Java动态代理+注解体现Spring AOP思想