欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

深入解析java中的静态代理与动态代理

程序员文章站 2023-12-21 10:07:58
java编码中经常用到代理,代理分为静态代理和动态代理。其中动态代理可以实现spring中的aop。 一、静态代理:程序运行之前,程序员就要编写proxy,然后进行编译,...

java编码中经常用到代理,代理分为静态代理和动态代理。其中动态代理可以实现spring中的aop。

一、静态代理:程序运行之前,程序员就要编写proxy,然后进行编译,即在程序运行之前,代理类的字节码文件就已经生成了

被代理类的公共父类

复制代码 代码如下:

package staticproxy;
public abstract class baseclass {
    public abstract void add();
}

被代理类
复制代码 代码如下:

package staticproxy;
public class a extends baseclass {
    public void add() {
        system.out.println("a add !");
    }
}

代理类
复制代码 代码如下:

package staticproxy;
public class proxy {
    baseclass baseclass;
    public void add() {
        baseclass.add();
    }
    public void setbaseclass(baseclass baseclass) {
        this.baseclass = baseclass;
    }
    public static void main(string[] args) {
        baseclass baseclass = new a();
        proxy proxy = new proxy();
        proxy.setbaseclass(baseclass);
        proxy.add();
    }
}

二、动态代理:实际的代码在编译期间并没有生成,而是在运行期间运用反射机制动态的生成

被代理类接口

复制代码 代码如下:

package jdkproxy;
public interface service {
    public void add();
    public void update();
}

被代理类a
复制代码 代码如下:

package jdkproxy;
public class aservice implements service {
    public void add() {
        system.out.println("aservice add>>>>>>>>>>>>>>>>>>");
    }
    public void update() {
        system.out.println("aservice update>>>>>>>>>>>>>>>");
    }
}

被代理类b
复制代码 代码如下:

package jdkproxy;
public class bservice implements service {
    public void add() {
        system.out.println("bservice add---------------");
    }
    public void update() {
        system.out.println("bservice update---------------");
    }
}

代理类
复制代码 代码如下:

package jdkproxy;
import java.lang.reflect.invocationhandler;
import java.lang.reflect.method;
public class myinvocationhandler implements invocationhandler {
    private object target;
    myinvocationhandler() {
        super();
    }
    myinvocationhandler(object target) {
        super();
        this.target = target;
    }
    public object invoke(object proxy, method method, object[] args)
            throws throwable {
        // 程序执行前加入逻辑
        system.out.println("before-----------------------------");
        // 程序执行
        object result = method.invoke(target, args);
        //程序执行后加入逻辑
        system.out.println("after------------------------------");
        return result;
    }
    public object gettarget() {
        return target;
    }
    public void settarget(object target) {
        this.target = target;
    }
}

测试类
复制代码 代码如下:

package jdkproxy;
import java.lang.reflect.proxy;
public class test {
    public static void main(string[] args) {
        service aservice = new aservice();
        myinvocationhandler handler = new myinvocationhandler(aservice);
        // proxy为invocationhandler实现类动态创建一个符合某一接口的代理实例
        service aserviceproxy = (service) proxy.newproxyinstance(aservice
                .getclass().getclassloader(), aservice.getclass()
                .getinterfaces(), handler);
        //由动态生成的代理对象来aserviceproxy 代理执行程序,其中aserviceproxy 符合service接口
        aserviceproxy.add();
        system.out.println();
        aserviceproxy.update();
        // 以下是对b的代理
        // service bservice = new bservice();
        // myinvocationhandler handler = new myinvocationhandler(bservice);
        // service bserviceproxy = (service) proxy.newproxyinstance(bservice
        // .getclass().getclassloader(), bservice.getclass()
        // .getinterfaces(), handler);
        // bserviceproxy.add();
        // system.out.println();
        // bserviceproxy.update();
    }
}

输出结果:
before-----------------------------
aservice add>>>>>>>>>>>>>>>>>>
after------------------------------
before-----------------------------
aservice update>>>>>>>>>>>>>>>
after------------------------------

其中上述标红的语句是产生代理类的关键代码,可以产生一个符合service接口的代理对象,newproxyinstance这个方法会做这样一件事情,他将把你要代理的全部接口,用一个由代码动态生成的类来实现,该类中所有的接口中的方法都重写为调用invocationhandler.invoke()方法。

下面详细介绍是如何实现代理对象的生成的

proxy的newproxyinstance方法,其中,为了看起来方便,已经将该方法中的异常处理语句删减

下下面public static object newproxyinstance(classloader loader,  class<?>[] interfaces,invocationhandler h) throws

复制代码 代码如下:

    public static object newproxyinstance(classloader loader,  class<?>[] interfaces,invocationhandler h) throws illegalargumentexception 
    { 
        if (h == null) { 
            throw new nullpointerexception(); 
        } 
        //生成指定的代理类
        class cl = getproxyclass(loader, interfaces); 
        constructor cons = cl.getconstructor(constructorparams); 
        // 生成代理类的实例,并把myinvocationhandler的实例传给它的构造方法,代理类对象实际执行都会调用myinvocationhandler的invoke方法,所以代理类对象中维持一个myinvocationhandler引用 
        return (object) cons.newinstance(new object[] { h }); 
    }  其中getproxyclass方法返回代理类的实例

proxy的getproxyclass方法
复制代码 代码如下:

public static class<?> getproxyclass(classloader loader, class<?>... interfaces) throws illegalargumentexception
{
    //前面省略很多缓存、异常处理、判断逻辑代码,为了使程序更加突出
    byte[] proxyclassfile =    proxygenerator.generateproxyclass(proxyname, interfaces);
    proxyclass = defineclass0(loader, proxyname,proxyclassfile, 0, proxyclassfile.length);
    proxyclasses.put(proxyclass, null);
    return proxyclass;
}

下面看proxygenerator的generateproxyclass方法,该方法最终产生代理类的字节码文件:
复制代码 代码如下:

public static byte[] generateproxyclass(final string name, class[] interfaces) 
   { 
       proxygenerator gen = new proxygenerator(name, interfaces); 
    // 这里动态生成代理类的字节码
       final byte[] classfile = gen.generateclassfile(); 
    // 如果savegeneratedfiles的值为true,则会把所生成的代理类的字节码保存到硬盘上 
       if (savegeneratedfiles) { 
           java.security.accesscontroller.doprivileged( 
           new java.security.privilegedaction<void>() { 
               public void run() { 
                   try { 
                       fileoutputstream file = 
                           new fileoutputstream(dottoslash(name) + ".class"); 
                       file.write(classfile); 
                       file.close(); 
                       return null; 
                   } catch (ioexception e) { 
                       throw new internalerror( 
                           "i/o exception saving generated file: " + e); 
                   } 
               } 
           }); 
       } 
    // 返回代理类的字节码 
       return classfile; 
   }

那么最终生成的代理类到底是什么样子呢,如下(省略了一下equals,hashcode,tostring等方法,只展示构造函数和add方法):
复制代码 代码如下:

public final class $proxy11 extends proxy implements service 
{      // 构造方法,参数就是刚才传过来的myinvocationhandler类的实例 
    public $proxy11(invocationhandler invocationhandler) 
    { 
        super(invocationhandler); 
    } 

    /**
     * 继承的add方法,重写,调用myinvocationhandler中的invoke方法
     */ 
    public final void add() 
    { 
        try 
        { 
            // 实际上就是调用myinvocationhandler中的invoke方法 
            super.h.invoke(this, m3, null); 
            return; 
        } 
        catch(error _ex) { } 
       catch(throwable throwable) 
        { 
            throw new undeclaredthrowableexception(throwable); 
        } 
   } 

上一篇:

下一篇: