zoukankan      html  css  js  c++  java
  • java代理通俗简单解析

    1         代理

    1.1            代理的概念和作用

    代理的概念很好理解,就像黄牛代替票务公司给你提供票,经纪人代理艺人和别人谈合作。Java的代理是指实现类作为代理类的属性对象,代理类提供方法给外部调用,代理类内部再去调用实现类的方法,实现具体的业务。也就是代理类作为对外接口人,实现类不直接对外。这就是java代理的概念。

    代理的作用是当你需要增加一些而外的操作,而又不想去修改实现类的。可以通过代理来实现,在代理类中增加附件的操作。例如需要增加权限过滤,但是业务类已经开发好,不想将权限和业务混在一起,想让每个类的功能尽可能单一,各司其职。,此时我们就可以做一个该类的代理类,由该代理类做权限判断,如果安全则调用实际类的业务开始处理。

    代理分为静态代理和动态代理,动态代理有分为jdk动态代理和cglib动态代理。

    1.2            静态代理

    先定义一个接口Subject,然后实现类RealSubject和代理类Proxy都继承这个接口,实现接口的方法。代理类构造函数的形参是接口引用Subject subject,实现类对象作为代理类构造函数的实参传入,保存到代理类的接口类型的Subject属性中。用户调用代理类的方法,代理类方法内部通过接口引用再去调用实现类的方法。

    //定义接口

    interface Subject {

        void request();

    }

    //实现类实现接口

    class RealSubject implements Subject {

        public void request(){

            System.out.println("RealSubject");

        }

    }

    //代理类实现接口

    class Proxy implements Subject {

        private Subject subject;

        public Proxy(Subject subject){//代理构造函数接口形参,以实现类对象为实参

            this.subject = subject;

        }

        public void request(){

            System.out.println("begin");

            subject.request();//调用实现类的方法

            System.out.println("end");

        }

    }

    //调用实例

    public class ProxyTest {

        public static void main(String args[]) {

            RealSubject subject = new RealSubject();//创建实现类对象

            Proxy p = new Proxy(subject);//创建代理类对象

            p.request();//调用代理类的方法,内部再去调用实现类的request方法

        }

    }

    1.3            Jdk动态代理

    1.3.1             Jdk动态代理实现

    Jdk动态代理实际上是利用了java的反射机制。利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口定义代理类的实现。将实现类的接口信息传入到代理类的静态构造函数Proxy.newProxyInstance中,代理类的构造函数内部通过反射的机制,获取实现类的方法,代理类的代理方法对实现类的方法进行了包装。调用代理类的方法,内部调用实现类的方法。实际上静态代理和动态代理本质上是一样的,只是动态代理利用的反射的机制获取实现类的方法。

    (1)//定义实现类继承的接口

    public interface Service { 

        //目标方法

        public abstract void add(); 

    }

    (2)//实现类并继承接口,实现方法add

    public class UserServiceImpl implements Service { 

        public void add() { 

            System.out.println("This is add service"); 

        } 

    }

    (3)利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口定义代理类的实现。重写InvocationHandler的invoke方法。

    class MyInvocatioHandler implements InvocationHandler {

        private Object target;//接口属性

        public MyInvocatioHandler(Object target) {//构造函数,实参实现类对象

            this.target = target;

        }

    //重写代理类InvocationHandler的invoke方法

        @Override

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            System.out.println("-----before-----");

            Object result = method.invoke(target, args);

            System.out.println("-----end-----");

            return result;

        }

        // 生成代理对象方法

    public Object getProxy() {

    //获取类加载器

            ClassLoader loader = Thread.currentThread().getContextClassLoader();

        //获取实现类的方法接口,提供反射机制的方法名称

     Class<?>[] interfaces = target.getClass().getInterfaces();

    //调用Proxy的静态方法创建代理对象

            return Proxy.newProxyInstance(loader, interfaces, this);

        }

    }

    (4)使用动态代理实例

    public class ProxyTest {

    public static void main(String[] args) {

    //创建实现类对象

            Service service = new UserServiceImpl();

    //实现类对象以实参形式传入代理类,保存在tartget属性中

            MyInvocatioHandler handler = new MyInvocatioHandler(service);

    //获取代理对象

            Service serviceProxy = (Service)handler.getProxy();

                  //通过代理对象调用方法

            serviceProxy.add();//执行方法

        }

    }

    执行结果:

    -----before-----

    This is add service

    -----end-----

    1.3.2             Jdk动态代理反推过程

    从步骤(4)开始在逆向推到一遍。先创建一个实现类对象service,作为入参传给        new MyInvocatioHandler(service);构造函数,保存在target属性中。通过getProxy()方法创建代理对象。Proxy.newProxyInstance(loader, interfaces, this);入参分别是loader加载类对象,interfaces实现类对象target的方法信息,this是MyInvocatioHandler指针。在newProxyInstance函数内部通过反射机制,根据interfaces传入的方法名称等信息,获取实现类的方法method。同时代理类内部对实现类的每个方法都对应实现了代理方法。serviceProxy.add();调用了代理类的代理方法,代理方法内部通过调用MyInvocatioHandler的invoke方法,返回invoke(Object proxy, Method method, Object[] args)去调用实现类的Method。invoke方法里面就可以增加其他的操作,比如说日志打印;其中Method参数是通过反射机制,根据interfaces信息获取到的实现类的方法。和静态映射的原理差不多,只是将代理类进行了包装。

    1.3.3             jdk代理类源码解析

    为了更清楚的理解动态代理,通过以下方式把代理类字节码生成class文件。

    byte[] classFile = ProxyGenerator.generateProxyClass("com.sun.proxy.$Proxy.1", service.getClass().getInterfaces());
    FileOutputStream out = new FileOutputStream("com.sun.proxy.$Proxy.1.class");
    out.write(classFile);
    out.flush();

    使用 反编译工具 jad jad com.sun.proxy.$Proxy.1 看看代理类如何实现,反编译出来的java代码如下:

    //代理类继承extends类,和实现类一样也实现了Service接口

    public final class $proxy1 extends Proxy implements Service {
    //构造函数
        public $proxy1(InvocationHandler invocationhandler) {
            super(invocationhandler);
        }
    //代理类的代理方法
        public final boolean equals(Object obj) {
            try {
                return ((Boolean)super.h.invoke(this, m1, new Object[] {
                    obj
                })).booleanValue();
            }
            catch(Error _ex) { }
            catch(Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
     
        public final String toString() {
            try {
                return (String)super.h.invoke(this, m2, null);
            }
            catch(Error _ex) { }
            catch(Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    //这里调用的是实现类add代理方法
        public final void add() {
            try {
    //内部调用的是MyInvocatioHandler的invoke方法
                super.h.invoke(this, m3, null);
                return;
            }
            catch(Error _ex) { }
            catch(Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
     
        public final int hashCode() {
            try {
                return ((Integer)super.h.invoke(this, m0, null)).intValue();
            }
            catch(Error _ex) { }
            catch(Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    //定义静态Method变量
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    //通过反射机制和interfaces提供的信息获取实现类的方法
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                    Class.forName("java.lang.Object")
                });
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
    //通过反射机制获取实现类的方法
                m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            }
            catch(NoSuchMethodException nosuchmethodexception) {
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());
            }
            catch(ClassNotFoundException classnotfoundexception) {
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());
            }
        }
    }

    1.4            不用实现接口的Cglib代理

    1.4.1             Cglib代理的简单实现

    静态代理和jdk动态代理都需要通过实现一个接口,只能对该类接口中定义的方法实现代理,这在实际编程中有一定的局限性。使用cglib[Code Generation Library]实现动态代理,并不要求实现类必须实现接口。

    使用步骤

    (1)先定义实现类,实现方法add

    public class UserServiceImpl { 

        public void add() { 

            System.out.println("This is add service"); 

        }

    }

    (2)实现MyMethodInterceptor实现MethodInterceptor接口,定义方法的拦截器intercept方法,该对象会被传入代理类中,相当于回调函数的作用,代理类内部调用intercept方法,intercept方法的四个参数:1)代理对象;2)实现类方法;3)方法参数;4)代理方法的MethodProxy对象。

    public class MyMethodInterceptor implements MethodInterceptor {

        public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {

            System.out.println("Before:" + method); 

            Object object = proxy.invokeSuper(obj, arg);

            System.out.println("After:" + method);

            return object;

        }

    }

    3)利用Enhancer类生成代理类;创建Enhancer enhancer对象,将实现类作为入参传入enhancer,作为代理类的父类。设置拦截器MyMethodInterceptor 到enhancer内部,作为回调。

    Enhancer enhancer = new Enhancer();//创建Enhancer类对象

    enhancer.setSuperclass(UserServiceImpl.class); //传入实现类的class,为反射提供信息 ,同时实现类是Enhancer的父类

    enhancer.setCallback(new MyMethodInterceptor());  //设置拦截器对象,通过拦截器回调。Enhancer是CGLib的字节码增强器,可以方便的对类进行扩展,内部调用GeneratorStrategy.generate方法生成代理类。传入了实现类作为父类,传入MyMethodInterceptor对象,作为拦截回调。

    UserServiceImpl userService = (UserServiceImpl)enhancer.create();//创建代理类对象,因为代理类继承了实现类,所以可以用父类引用接收子类对象。

    4userService.add();方法的执行输出结果。

    Before: add

    This is add service

    After: add

    1.4.2             代理类反向编译源码解析

    对(UserServiceImpl)enhancer.create();返回的代理类进行反向编译,得到代理类的源码如下。

    这样可能还是不能理解,代理类内部是怎么实现的;

    import net.sf.cglib.core.Signature;

    import net.sf.cglib.core.ReflectUtils;

    import net.sf.cglib.proxy.MethodProxy;

    import java.lang.reflect.Method;

    import net.sf.cglib.proxy.MethodInterceptor;

    import net.sf.cglib.proxy.Callback;

    import net.sf.cglib.proxy.Factory;

    //

    // Decompiled by Procyon v0.5.30

    //

    //(1)enhancer代码生成器自动生成了类UserService$$EnhancerByCGLIB$$394dddeb,继承了实现类UserService,实现了接口Factory。

    public class UserService$$EnhancerByCGLIB$$394dddeb extends UserService implements Factory

    {

        private boolean CGLIB$BOUND;

        private static final ThreadLocal CGLIB$THREAD_CALLBACKS;

        private static final Callback[] CGLIB$STATIC_CALLBACKS;

        private MethodInterceptor CGLIB$CALLBACK_0;//这里保存步骤(4)中通过enhancer.setCallback(new MyMethodInterceptor());传入的对象

        private static final Method CGLIB$add$0$Method;

        private static final MethodProxy CGLIB$add$0$Proxy;

        private static final Object[] CGLIB$emptyArgs;

        static void CGLIB$STATICHOOK2() {

            CGLIB$THREAD_CALLBACKS = new ThreadLocal();

            CGLIB$emptyArgs = new Object[0];

    //通过反射机制获取代理类

            final Class<?> forName = Class.forName("UserService$$EnhancerByCGLIB$$394dddeb");

            final Class<?> forName3;

    //查找到实现类的add方法

            CGLIB$add$0$Method = ReflectUtils.findMethods(new String[] { "add", "()V" }, (forName3 = Class.forName("UserService")).getDeclaredMethods())[0];

    //创建MethodProxy对象,每个被代理的方法都对应一个MethodProxy对象,methodProxy.invokeSuper方法最终调用实现类的add方法

            CGLIB$add$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "add", "CGLIB$add$0");//将代理类的两个代理方法传入

    }

    //最终调用的方法是父类,也就是实现类的方法add

        final void CGLIB$add$0() {

            super.add();

        }

        public final void add() {

          MethodInterceptor cglib$CALLBACK_0;

         cglib$CALLBACK_0=this. CGLIB$CALLBACK_0;

    //先判断拦截器对象MethodInterceptor是否为空,不为空则执行回调intercept;

            if (cglib$CALLBACK_0 != null) {

                cglib$CALLBACK_0.intercept((Object)this, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Method, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$emptyArgs, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Proxy);

                return;

            }

            super.add();

        }

        static {

            CGLIB$STATICHOOK2();

        }

    }

    1.4.3             Cglib代理的整体流程

    详细步骤如下

    (1)    定义实现类UserServiceImpl和拦截器MyMethodInterceptor,拦截器实现intercept方法;

    (2)    创建Enhancer enhancer对象,通过enhancer.setSuperclass(UserServiceImpl.class); 传入实现类的class,作为代理类的父类。通过enhancer.setCallback(new MyMethodInterceptor());  //设置拦截器对象,保存在代理类的cglib$CALLBACK_0属性中。提供回调代理类会调用MyMethodInterceptor的函数intercept函数返回调用;

    (3)    Enhancer是CGLib的字节码增强器,生成代理类的代码,代理类继承了实现类UserServiceImpl,代理类实现了两个代理方法final void CGLIB$add$0()和public final void add()方法;这里不知道为什么要这样设计。CGLIB$add$0()是最终调用的方法;

    (4)    创建代理类对象UserServiceImpl userService = (UserServiceImpl)enhancer.create();userService.add();调用代理类的在步骤三中的public final void add();方法;先判断cglib$CALLBACK_0对象是否为空。不为空,则执行MethodInterceptor的回调函数cglib$CALLBACK_0.intercept(Object obj, Method method, Object[] arg, MethodProxy proxy);

    (5)    Intercept回调函数实现实际上调用了参数MethodProxy proxy的proxy.invokeSuper方法;

    public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {

            System.out.println("Before:" + method); 

            Object object = proxy.invokeSuper(obj, arg);

            System.out.println("After:" + method);

            return object;

    }

    (6)    proxy又是从哪里来的呢,步骤(4)中传入的实参是这样实现的

    //创建MethodProxy对象,每个被代理的方法都对应一个MethodProxy对象,methodProxy.invokeSuper方法最终调用实现类的add方法

            CGLIB$add$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "add", "CGLIB$add$0");//将代理类的两个代理方法传入

    (7)    每个被代理的方法都对应一个MethodProxy对象,methodProxy.invokeSuper方法最终调用实现类的add方法proxy.invokeSuper的内部的实现如下

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {

        try {

            init();

            FastClassInfo fci = fastClassInfo;

            return fci.f2.invoke(fci.i2, obj, args);

        } catch (InvocationTargetException e) {

            throw e.getTargetException();

        }

    }

    实现方法中创建了一个FastClassInfo fci对象,然后调用了fci.f2.invoke(fci.i2, obj, args);

    (8)单看invokeSuper方法的实现,似乎看不出实现类add方法调用,在MethodProxy实现中,通过FastClassInfo维护了实现类和代理类的FastClass。

    private static class FastClassInfo {

        FastClass f1;

        FastClass f2;

        int i1;

        int i2;

    }

    以add方法的methodProxy为例,f1指向实现类对象,f2指向代理类对象,i1和i2分别是方法add和CGLIB$add$0在对象中索引位置。那么fci.f2.invoke(fci.i2, obj, args);的含义就是调用代理对象的invoke方法,传入参数是代理类的实现方法CGLIB$add$0、代理对象、函数参数;

    (9)这样有回到了代理类中的方法CGLIB$add$0()

    final void CGLIB$add$0() {

            super.add();

    }

    (10)super是父类的意思,指向的是实现类,然后执行实现类的add方法。绕了一个好大的圈终于绕回来了。不知道什么原因要这样搞。

    1.4.4             FastClass实现机制

    FastClass其实就是对Class对象进行特殊处理,提出下标概念index,通过索引保存方法的引用信息,将原先的反射调用,转化为方法的直接调用,从而体现所谓的fast,下面通过一个例子了解一下FastClass的实现机制。

    1、定义原类

    class Test {

        public void f(){

            System.out.println("f method");

        }

        public void g(){

            System.out.println("g method");

        }

    }

    2、定义Fast类

    class FastTest {

        public int getIndex(String signature){

            switch(signature.hashCode()){

            case 3078479:

                return 1;

            case 3108270:

                return 2;

            }

            return -1;

        }

        public Object invoke(int index, Object o, Object[] ol){

            Test t = (Test) o;

            switch(index){

            case 1:

                t.f();

                return null;

            case 2:

                t.g();

                return null;

            }

            return null;

        }

    }

    在FastTest中有两个方法,getIndex中对Test类的每个方法根据hash建立索引,invoke根据指定的索引,直接调用目标方法,避免了反射调用。所以当调用methodProxy.invokeSuper方法时,实际上是调用代理类的CGLIB$add$0方法,CGLIB$add$0直接调用了实现类的add方法。

    自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:

    https://www.cnblogs.com/bclshuai/p/11380657.html

    百度云盘下载地址:

    链接:https://pan.baidu.com/s/1swkQzCIKI3g3ObcebgpIDg

    提取码:mc8l

    微信公众号获取最新的软件和视频介绍

    QStockView

  • 相关阅读:
    NVIDIA Jetson TX2刷机
    安装python2和3在centos7里面的问题
    js和DOM结合实现评论功能 (可以添加,删除)
    js实现计时
    js获取星期日期
    js登录界面演示
    下拉列表演示
    html表单练习
    一个底层w32汇编的小例子,演示 原创
    invoke和call的区别
  • 原文地址:https://www.cnblogs.com/bclshuai/p/10651959.html
Copyright © 2011-2022 走看看