问题  :

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

概述

代理模式是设计模式的一种,简单地说就是调用代理类的方法实际就是调用真实类的方法。这种模式在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单继承)所以我们只能是代理接口

参考资料

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

  1. Java代理(静态代理、JDK动态代理、CGLIB动态代理)

    Java中代理有静态代理和动态代理.静态代理的代理关系在编译时就确定了,而动态代理的代理关系是在运行期确定的.静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性. J ...

  2. Java代理模式/静态代理/动态代理

    代理模式:即Proxy Pattern,常用的设计模式之一.代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问. 代理概念 :为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委 ...

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

    >不用代理 有时候,我希望在一些方法前后都打印一些日志,于是有了如下代码. 这是一个处理float类型加法的方法,我想在调用它前打印一下参数,调用后打印下计算结果.(至于为什么不直接用+号运算, ...

  4. Java中的代理模式--静态代理和动态代理本质理解

    代理模式定义:为其他对象提供了一种代理以控制对这个对象的访问. 代理模式的三种角色: Subject抽象主题角色:抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求. Real ...

  5. Java动态代理与静态代理以及它能为我们做什么

    相信我们在网上和平时学习和工作中或多或少都接触过Java的代理模式,经常听到什么静态代理.动态代理的一些名词.但我们是否真的很清楚这些呢?至少我在面试时,发现很多人并不很清楚. 首先代理比较好理解,就 ...

  6. Atitit 代理CGLIB 动态代理 AspectJ静态代理区别

    Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为 ...

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

    一:代理模式 代理模式的作用是:为其他对象提供一种代理以控制这个对象的访问.在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 二:代理模式设计 ...

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

    定义 为其它的对象提供一种代理,以控制这个对象的访问 使用场景 当不想直接访问某个对象的时候,就可以通过代理 1.不想买午餐,同事帮忙带 2.买车不用去厂里,去4s店 3.去代理点买火车票,不用去车站 ...

  9. jdk的动态代理和静态代理你还写不出来嘛???

    一.什么叫jdk的代理? 用另外一个对象去代理实际对象的操作 分为动态代理和静态代理二.先说说静态代理 从字面意思来看就是不会改变的,只可以代理某个固定对象的. 静态代理就是通过实现和目标对象实现的同 ...

随机推荐

  1. SparkSQL大数据实战:揭开Join的神秘面纱

    本文来自 网易云社区 . Join操作是数据库和大数据计算中的高级特性,大多数场景都需要进行复杂的Join操作,本文从原理层面介绍了SparkSQL支持的常见Join算法及其适用场景. Join背景介 ...

  2. JUC中Executor基本知识

    Future And Callable 引用 http://www.cnblogs.com/dolphin0520/p/3949310.html http://www.iocoder.cn/JUC/ ...

  3. P3357 最长k可重线段集问题 网络流

    P3357 最长k可重线段集问题 题目描述 给定平面 x-O-yx−O−y 上 nn 个开线段组成的集合 II,和一个正整数 kk .试设计一个算法,从开线段集合 II 中选取出开线段集合 S\sub ...

  4. samba服务器的安装与配置(之前是因为没有把共享文件权限放开!)(windows7和centOS6)

    注:centOS6跟centOS7安装方法不一样哦.以下是centOS6的安装过程. 安装:yum -y install samba 启动:/etc/init.d/smb start 设置开机启动:c ...

  5. windows文件名太长无法删除的解决办法

    安装nodejs 的模块hexo后,由于香重新安装,在删除的时候却提示文件名太长无法删除,dos命令.回收站各种都无法搞定,后来找到解决办法: 1.进入这些文件的所在目录的上层目录,右键这些文件的所在 ...

  6. [Objective-C语言教程]内存管理(36)

    内存管理是任何编程语言中最重要的过程之一.它是在需要时分配对象的内存并在不再需要时取消分配的过程. 管理对象内存是一个性能问题; 如果应用程序不释放不需要的对象,则应用程序会因内存占用增加并且性能受损 ...

  7. PHP之旅 php数据类型

    1.php中int型,表示八进制或十六进制或者十进制时的不同,八进制前面加0,十六进制前面加0x <?php $dec_int=99; $oct_int=066; $hex_int=0X1a; ...

  8. Great Expectations

    Dear friend, This game is created based on Dicken's Great Expectations. To colorful the contents, I ...

  9. c# java数据类型不同点

    导读:C#和Java是当今最火热的两门面向对象编程语言,很多程序都是既开发Java,也涉足C#.不得不说这两门编程语言有很多共同点,这里主要比较一下Java和C#数据类型的不同之处,这些小的区别有时甚 ...

  10. Array对象的判定

    /* 关于JS对象类型的判断,最复杂的在于RegExp和Array了,判定RegExp的情形不较少,而Array就比较多了,下面就是判断Array的方法 */ //方法一:利用instanceof来判 ...