• 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 bean scope 的几种类型
    耦合(软件工程)
    标签防止重复提交
    Struts2中的ognl标签
    struts2
    SQL PRIMARY KEY 约束:使用方法及撤销办法解析
    SQL UNIQUE 约束:使用方法及撤销办法剖析
    SQL NOT NULL 约束:语法及案例剖析
    SQL 约束(Constraints):语法及实例剖析
  • 原文地址:https://www.cnblogs.com/NoYone/p/8733868.html
Copyright © 2020-2023  润新知