zoukankan      html  css  js  c++  java
  • Java动态代理学习【Spring AOP基础之一】

      Spring AOP使用的其中一个底层技术就是Java的动态代理技术。Java的动态代理技术主要围绕两个类进行的 

      java.lang.reflect.InvocationHandler
      java.lang.reflect.Proxy

      首先从代码层面说明Java动态代理是如何实现的,

      业务逻辑接口:

      

    /**
     * 创建一个人的接口,其中有一个吃的方法
     */
    public interface Person {
        public void eat();
    }
    

      创建一个实现该业务接口的类:

    /**
     * 人接口的实现,实现吃饭的方法
     */
    public class PersonImpl implements Person {
        @Override
        public void eat() {
            System.out.println("吃主食、吃菜、喝汤...");
        }
    }
    

      此时,如果正常情况如果想要调用Person这个接口,直接new它的实现类然后调用eat方法即可,但是如果想要实现一个Person的代理,并且在eat方法前后进行一些工作就需要使用Proxy和InvovationHandler;

      InvocationHandler接口的实现类,(代理类中额外逻辑的实现就需要在这里进行)

    /**
     * 代理类的额外逻辑实现类
     */
    public class InvocationHandlerImpl implements InvocationHandler {
        Person person;
        public InvocationHandlerImpl(Person person) {
            this.person = person;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if(method.getName().equalsIgnoreCase("eat")) {
                System.out.println("洗手...");
                method.invoke(person, args);
                System.out.println("刷牙");
            }
            return person;
        }
    }
    

      

      接下来就是使用Proxy生成代理类,Proxy需要业务逻辑接口,代理类额外实现逻辑InvocationHandler实现类

    /**
     * 代理类产生并且测试
     */
    public class ProxyGenerate {
        @Test
        public void proxyTest() {
            Person person = new PersonImpl();
            InvocationHandler invocationHandler = new InvocationHandlerImpl(person);
            Person personProxy = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, invocationHandler);
            personProxy.eat();
        }
        
    
    }
    

      

      输出内容如下:

      代理类已经成功的完成代理。

      AOP的功能就是在一个方法前、后、异常抛出等地方添加逻辑,与上面的过程是一样的,所以这个技术就被用来实现Spring AOP的一个技术,但是这个只针对接口实现,如果想要给一个类添加AOP的逻辑,这个Proxy动态代理的技术暂时无法适用,Spring AOP适用了CGLIB,支持类的动态代理,但是不属于这一次的讨论范畴。

      知道怎么用了之后是不是对Proxy这个感觉很神奇,想知道是如何实现的呢,可以深入了解一下newProxyInstance这个方法:

      

       找到上述内容,可以看到这个是动态生成代理类字节数组的方法,所以我们可以通过这个方法了解到动态生成的代理类的结构:

    /**
     * 代理类产生并且测试
     */
    public class ProxyGenerate {
        @Test
        public void proxyWatch() {
            byte[] classByte = ProxyGenerator.generateProxyClass("PersonDynamicProxy", new Class[]{Person.class});
            try {
                FileOutputStream var1 = new FileOutputStream("PersonDynamicProxy" + ".class");
                var1.write(classByte);
                var1.close();
            } catch (IOException var2) {
                throw new InternalError("I/O exception saving generated file: " + var2);
            }
        }
    }
    

      执行上述代码会生成动态代理对象的.class文件

      反编译(jd-gui,idea)之后就可以看到动态代理类的结构:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import com.smart.beanfactory.Person;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class PersonDynamicProxy extends Proxy implements Person {
        private static Method m1;
        private static Method m3;
        private static Method m0;
        private static Method m2;
    
        public PersonDynamicProxy(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final void eat() throws  {
            try {
                super.h.invoke(this, m3, (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)).intValue();
            } 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);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
                m3 = Class.forName("com.smart.beanfactory.Person").getMethod("eat", new Class[0]);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    

      可以看出来,动态代理类首先通过反射的技术,将源接口中的方法均设置为其静态属性(Method),然后针对每个方法进行重写。

      重写的逻辑是使用invocationHandler接口的invoke方法实现,每个invoke方法在绑定相应的反射出来的静态属性即可。

      举例,比如上面Person接口的eat被反射未m3的静态属性中,重写eat()方法是调用方法为invoke(this, m3, args),其中invoke中会有额外添加的一些逻辑,

  • 相关阅读:
    爬虫必看,每日JS逆向之爱奇艺密码加密,今天你练了吗?
    每日JS逆向练习之斗鱼登录密码加密,今天你练了吗?
    兄弟们,我打算抠100个网站JS加密代码召唤,一个也跑不掉,这次轮到小虎牙
    这个爬虫JS逆向加密任务,你还不来试试?逆向入门级,适合一定爬虫基础的人
    兄弟,你爬虫基础这么好,需要研究js逆向了,一起吧(有完整JS代码)
    四十一:快速排序(递归)
    第四十次发博不知道用什么标题好
    第三十九次发博不知道发什么好
    第三十八次发博不知道用什么标题好
    第三十七次发博不知道用什么标题好
  • 原文地址:https://www.cnblogs.com/weixliu/p/7353260.html
Copyright © 2011-2022 走看看