java 基础 --- 动态代理和静态代理

问题  :

  • 代理的应用场景是什么
  • 动态代理的底层原理是什么,为什么只能继承接口

概述

代理模式是设计模式的一种,简单地说就是调用代理类的方法实际就是调用真实类的方法。这种模式在AOP (切面编程)中非常常见,使用的场景比如事务,网络代理等。他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。本文将会介绍代理的两种方式 : 静态代理和动态代理。

  • 静态代理 :由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
  • 动态代理 : 代理类在程序运行时创建的代理方式被成为动态代理。

代码解析

静态代理

静态代理例子

public interface ISProxyInterface {
   void doSomething();
}

public class STargetObject implements ISProxyInterface{
    public void doSomething(){
        System.out.println("静态代理真实类:do some thing");
    }

}

/**
 *  静态代理类
 */
public class StaticProxy implements  ISProxyInterface {
    private STargetObject target;

    public StaticProxy(STargetObject target) {
        this.target = target;
    }

    public void doSomething(){
        System.out.println("调用之前");
        target.doSomething();
        System.out.println("调用之后");

    }
}

public class ProxyMain {
    public static void main(String[] args) {
        //静态代理测试
        STargetObject target = new STargetObject();
        StaticProxy proxy = new StaticProxy(target);
        proxy.doSomething();

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

        //动态代理测试
        TargetInterface Dtarget = new DTargetObject();
        TargetHandler handler = new TargetHandler(Dtarget);
        TargetInterface Dproxy = (TargetInterface) Proxy.newProxyInstance(Dtarget.getClass().getClassLoader(), Dtarget.getClass().getInterfaces(), handler);
        Dproxy.doSomeThing();
    }
}

可以看到,静态代理和真实类都拥有同样的接口,这让它看起来似乎和真实类一样,当调用这个接口时,实际走的是真实类的方法。

动态代理

动态代理例子。

public interface TargetInterface {
    void doSomeThing();
}

/**
*  动态代理处理类
*/
public class TargetHandler implements InvocationHandler {

    private Object mObject;

    public TargetHandler(Object object) {
        mObject = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用之前");
        Object result = method.invoke(mObject, args);
        System.out.println("调用之后");
        return result;
    }
}

public class DTargetObject implements TargetInterface {

    @Override
    public void doSomeThing() {
        System.out.println("动态代理类 : do some thing !");
    }
}

动态代理原理解析

我们看到在ProxyMain 中调用了Proxy.newProxyInstance这个方法,生成了一个代理,而这个代理类中正是我们需要的。要想知道这中间发生了什么,就要看看 Proxy.newProxyInstance 做了什么事。

看这个方法的实现前我们先看一下,这个方法的注解 :

Throws:
	IllegalArgumentException
	    - if any of the restrictions on the parameters that may be passed to getProxyClass  are violated

	SecurityException - if a security manager, s, is present and any of the following conditions is met:
		- the given loader is null and the caller‘s class loader is not null and the invocation of s.checkPermission with RuntimePermission("getClassLoader") permission denies access;
		- for each proxy interface, intf, the caller‘s class loader is not the same as or an ancestor of the class loader for intf and invocation of s.checkPackageAccess() denies access to intf;
		- any of the given proxy interfaces is non-public and the caller class is not in the same runtime package as the non-public interface and the invocation of s.checkPermission with ReflectPermission("newProxyInPackage.{package name}") permission denies access.

    NullPointerException
    	- if the interfaces array argument or any of its elements are null, or if the invocation handler, h, is null
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);  //封装或是生产一个代理类

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});   //产生了一个实例返回
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

我们最应该关注的是 Class<?> cl = getProxyClass0(loader, intfs);这句,这里产生了代理类,后面代码中的构造器也是通过这里产生的类来获得,可以看出,这个类的产生就是整个动态代理的关键,由于是动态生成的类文件,我这里不具体进入分析如何产生的这个类文件,只需要知道这个类文件时缓存在java虚拟机中的。我们可以通过打印出来这个临时的类是如何的来了解动态代理的原理。在ProxyMain 中添加如下代码 :

        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", DTargetObject.class.getInterfaces());
        String path = "E:/DTargetObject.class";
        try(FileOutputStream fos = new FileOutputStream(path)) {
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类class文件写入成功");
        } catch (Exception e) {
            System.out.println("写文件错误");
        }

打开路径下就会得到一个class 的文件,我们再用 java decompiler(反编译工具)来查看class 文件。

import Proxy.Dynatic.TargetInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0
  extends Proxy
  implements TargetInterface
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;

  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws
  {
    super(paramInvocationHandler);             //构造方法,InvocationHandler 和我们的代理处理类是不是很像,就是它!
    //而我们的InvocationHandler 又持有一个真实的对象,所以我们这里可以预测下面会调用 invoke方法
  }

  public final boolean equals(Object paramObject)
    throws
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void doSomeThing()     //看到了我们熟悉的方法
    throws
  {
    try
    {
      this.h.invoke(this, m3, null);    //刚刚从构造函数传过去的InvocationHandler
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  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("Proxy.Dynatic.TargetInterface").getMethod("doSomeThing", new Class[0]);   //这是我们想调用的方法
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

从上面的源码我们可以知道 :

  • JDK 动态代理的原理底层是反射.
  • 由于 $Proxy 已经继承了 Proxy ,(java单继承)所以我们只能是代理接口

参考资料

原文地址:https://www.cnblogs.com/Benjious/p/10431522.html

时间: 2024-08-09 01:38:33

java 基础 --- 动态代理和静态代理的相关文章

【Java】代处理?代理模式 - 静态代理,动态代理

>不用代理 有时候,我希望在一些方法前后都打印一些日志,于是有了如下代码. 这是一个处理float类型加法的方法,我想在调用它前打印一下参数,调用后打印下计算结果.(至于为什么不直接用+号运算,见[Java]Float计算不准确) package com.nicchagil.study.java.demo.No09代理.No01不用代理; import java.math.BigDecimal; public class FloatCalculator { public float add(fl

Atitit 代理CGLIB&#160;动态代理&#160;AspectJ静态代理区别

Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为代表 1 1.2. JDK动态代理是模拟接口实现的方式,cglib是模拟子类继承的方式1 1.3. CGLIB代理模式的缺点 在static和final方法上应用横切关注点也是无法做到的.2 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 

Java代理模式——静态代理模式

一:代理模式 代理模式的作用是:为其他对象提供一种代理以控制这个对象的访问.在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 二:代理模式设计到的角色 抽象角色:声明真是对象和代理对象的共同接口(抽象类或接口). 代理角色:代理对象角色内部含有对真是对象的引用,从而可以操作真是对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能够代替真是对象.同时,代理对象可以在执行真实对象的操作时,附加其他操作,相当于对真是对象进行封装. 真实

代理模式-静态代理与动态代理

简介 首先感谢沽泡学院 tom 老师 代理模式是一种结构型模式 代理模式就是代理对象帮被代理对象处理一些问题, 类似中介, 客户只要结果, 中介怎么搞是他的事儿, 他可能再处理过程中赚外快什么的 代理模式的应用: spring中的aop, 日常工作中记录日志, 统计时间,权限控制等 这里我们使用一个客户端代理访问google举例, 具体细节可能不合适, 意会.意会.意会... 静态代理 /** * 一个服务器接口, 服务器有很多功能, 可以用来路由, 建站等... */ public inter

JAVA设计模式——代理(静态代理)

定义 为其它的对象提供一种代理,以控制这个对象的访问 使用场景 当不想直接访问某个对象的时候,就可以通过代理 1.不想买午餐,同事帮忙带 2.买车不用去厂里,去4s店 3.去代理点买火车票,不用去车站 UML 介绍下上面的UML图的意思,以购买火车票为例: 定义代理的接口,买票 (1)IProxy 代理的对象,完成代理的任务,设置具体的代理,调用具体的代理方法等 (2)ProxyObject 被代理对象,处理具体的业务逻辑 (3)RealObject 代码实现 上面我们已经很清晰的明白了他们之间

关于动态代理和静态代理的区别

静态代理: 简单来说就是:通过真实的实现类A和proxy代理实现同一个接口,然后在proxy代理里引入A对象的引用. 这样做的目的,就是为了可以实现一些其他的功能,但是不会让真实类变得膨胀. 优点: 代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合),对于如上的客户端代码,newUserManagerImpl()可以应用工厂将它隐藏,如上只是举个例子而已. 缺点: 1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法.这样就出现了大量的代码重复.如果

[z]Java代理(jdk静态代理、动态代理和cglib动态代理)

一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 1 2 3 4 5 6 7 8 9 10 11 12 13 /**  * 业务接口  * @author pc  *  */ public interface UserService {          // 增加一个用户     public void addUser();     // 编辑账户     pub

Java代理(jdk静态代理、动态代理和cglib动态代理)

一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 /** * 业务接口 * @author pc * */ public interface UserService { // 增加一个用户 public void addUser(); // 编辑账户 public void editUser(); } 2.业务实现类 /** * 业务实现类 * @author pc

Java基础-动态代理

在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的. InvocationHandler 每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用