文档章节

java代理通俗简单解析

o
 osc_gu9d45li
发布于 2019/04/03 22:18
字数 3522
阅读 12
收藏 0

精选30+云产品,助力企业轻松上云!>>>

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

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
JAVA设计模式-动态代理(Proxy)示例及说明

在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理。 一,概念   代理设计模式的目的就是在不直接...

osc_xxbg6mig
2018/09/25
9
0
java反射详解和动态代理

最近闲来无事,刚好看到以前的java反射笔记,这里整理一下,有不足之处还望指出;   在学习java反射机制之前,我们先提出这么一个问题:在Java中,能不能在运行时针对一个Java类,获取它的...

osc_vh89klm7
2019/05/27
2
0
类加载的执行过程

前言一个Java文件从编码完成到最终执行,一般主要包括两个过程 编译 运行 编译,即把我们写好的java文件,通过javac命令编译成字节码,也就是我们常说的.class文件。 运行,则是把编译声称的...

osc_op3x65ho
01/18
2
0
Java SE 6 新特性: Instrumentation 新功能

Instrumentation 简介 利用 Java 代码,即 java.lang.instrument 做动态 Instrumentation 是 Java SE 5 的新特性,它把 Java 的 instrument 功能从本地代码中解放出来,使之可以用 Java 代码...

Sub
2013/08/14
109
0
Instrumentation 实践详解

利用 Java 代码,即 java.lang.instrument 做动态 Instrumentation 是 Java SE 5 的新特性,有了这样的功能,开发者就可以实现更为灵活的和 了,这样的特性实际上提供了,使得开发者无需对 ...

陶邦仁
2014/12/30
2.1K
0

没有更多内容

加载失败,请刷新页面

加载更多

平时使用的Lszrz到底是什么协议?说说Xmodem/Ymodem/Zmodem

XMODEM, YMODEM, and ZMODEM 由于平时使用rz/sz较多,r/s好理解,一个send一个receive。但是由不太清楚z是什么意思,故有此文。 sx/rx, sb/rb (b=batch)和sz/rz分别实现了xmodem,ymodem和z...

独钓渔
59分钟前
17
0
真正的强智能时代已经到来。道翰天琼认知智能机器人平台API大脑。

最近,我常说人工智能的寒冬快要来了,提醒业界要做好思想准备,但同时我也说:冬天来了,春天就不会远了…… 2019年6月我写了篇文章《深度学习的问题究竟在哪?》,说到深度学习的一个主要问...

jackli2020
今天
24
0
什么是控制型人格,控制型人格的筛查测试

一、 什么是控制性人格 拥有控制型人格的人,他们会尽力的隐藏自己的意图,但是又会使用很微妙的方式来利用周围人的弱点,进而占取便宜时,使他们能够得到自己想要的东西。这类人的控制欲非常...

蛤蟆丸子
今天
14
0
【Spring】Spring AOP 代理对象生成逻辑源码分析

1. spring aop案例(POJO注入) 1.0 被代理接口 TargetInterface /** * 被代理的接口 * @author Yang ZhiWei */public interface TargetInterface { void show(); String show......

ZeroneLove
今天
36
0
聊聊dubbo-go的gracefulShutdownFilter

序 本文主要研究一下dubbo-go的gracefulShutdownFilter gracefulShutdownFilter dubbo-go-v1.4.2/filter/filter_impl/graceful_shutdown_filter.go type gracefulShutdownFilter struct {......

go4it
今天
30
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部