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

 更新时间:2013年10月12日 09:46:48   作者:  
本篇文章是对java中的静态代理与动态代理进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助

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); 
        } 
   } 

相关文章

  • 如何通过javacv实现图片去水印(附代码)

    如何通过javacv实现图片去水印(附代码)

    这篇文章主要介绍了如何通过javacv实现图片去水印(附代码),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • idea启动springmvc项目时报找不到类的解决方法

    idea启动springmvc项目时报找不到类的解决方法

    这篇文章主要介绍了idea启动springmvc项目时报找不到类的解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • Component-Scan 不扫描jar里面的类问题

    Component-Scan 不扫描jar里面的类问题

    这篇文章主要介绍了Component-Scan 不扫描jar里面的类问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Java Web实现简易图书管理系统

    Java Web实现简易图书管理系统

    这篇文章主要为大家详细介绍了Java Web实现简易图书管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-09-09
  • MyBatis实现动态SQL更新的代码示例

    MyBatis实现动态SQL更新的代码示例

    本文博小编将带领大家学习如何利用 MyBatis 拦截器机制来优雅的实现这个需求,文中通过代码示例介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下
    2023-07-07
  • spring cloud 使用Eureka 进行服务治理方法

    spring cloud 使用Eureka 进行服务治理方法

    这篇文章主要介绍了spring cloud 使用Eureka 进行服务治理方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • springmvc前台向后台传值几种方式总结(从简单到复杂)

    springmvc前台向后台传值几种方式总结(从简单到复杂)

    今天小编就为大家分享一篇springmvc前台向后台传值几种方式总结(从简单到复杂),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08
  • 秒懂Kotlin之Java工程师快速掌握Kotlin的技巧

    秒懂Kotlin之Java工程师快速掌握Kotlin的技巧

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,被称之为 Android 世界的Swift,由 JetBrains 设计开发并开源。这篇文章主要介绍了秒懂Kotlin之Java工程师快速掌握Kotlin的技巧,需要的朋友可以参考下
    2021-09-09
  • Java中字符串截取方法详解及实际应用小结

    Java中字符串截取方法详解及实际应用小结

    java中截取字符串的常用方法是使用String类的substring方法,本文通过实例代码给大家介绍Java中字符串截取方法详解及实际应用小结,感兴趣的朋友跟随小编一起看看吧
    2024-12-12
  • Spring Boot 整合JPA 数据模型关联使用操作(一对一、一对多、多对多)

    Spring Boot 整合JPA 数据模型关联使用操作(一对一、一对多、多对多)

    这篇文章主要介绍了Spring Boot 整合JPA 数据模型关联操作(一对一、一对多、多对多),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07

最新评论