问题  :

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

概述

代理模式是设计模式的一种,简单地说就是调用代理类的方法实际就是调用真实类的方法。这种模式在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. Luogu1438 无聊的数列(单点查询)&&加强版(区间查询)

    题目链接:戳我 线段树中差分和前缀和的应用 其实对于加上等差数列的操作我们可以分成这样三步-- update(1,1,n,l,l,k); if(r>l) update(1,1,n,l+1,r,d ...

  2. Syncthing源码解析 - 在Gogland中对Syncthing的各个模块进行调试?

    Syncthing的模块很多,各自负责不同的功能,如何能够对各个模块进行调试?Syncthing开发者早就想到这个问题了,允许开发者对任意模块进行单独调试,也允许同时对所有模块调试,调试方式是打印各个 ...

  3. Java-File类获取目录下文件名-遍历目录file.listFiles

    package com.hxzy.IOSer;import java.io.*;/*File 类获取功能 * List * ListFile * */public class Demo06 { pub ...

  4. jmeter—解决响应乱码问题

    问题:    当响应数据或响应页面没有设置编码时,jmeter会按照jmeter.properties文件中,sampleresult.default.encoding 设置的格式解析默认ISO-88 ...

  5. css 实现关闭按钮 X

    .close::before { content: "\2716";} 然后就显示出来了 这里有个更直接的例子 <!DOCTYPE html> <html lan ...

  6. luncene 查询字符串的解析-QueryParser类

    搜索流程中的第二步就是构建一个Query.下面就来介绍Query及其构建. 当用户输入一个关键字,搜索引擎接收到后,并不是立刻就将它放入后台开始进行关键字的检索,而应当首先对这个关键字进行一定的分析和 ...

  7. AngularJS源码解析3:RootScope的创建过程

    RootScopeProvider简介 RootScopeProvider是angularjs里面比较活跃的一个provider.它主要用来生成实例rootScope,它代表angularjs应用的根 ...

  8. Vmware下Kali设置桥接网络无法上网

    1.检查是否设置桥接 2.编辑>首选项>虚拟网络编辑器>选对本机上网的网卡 3.检查上网的网卡>适配器属性栏有没有 Vmware Bridge Protocol 桥接的服务. ...

  9. Windows系统下如何在cmd命令窗口中切换Python2.7和Python3.6

    针对在同一系统下我们可能安装多个版本的Python,毕竟Python2.7与Python3.6还是有不同的需求,但是在用Cmd命令窗口是我们可能默认的系统变量环境是其中一个版本,当我们需要在cmd命令 ...

  10. 考试题T3

    题意分析 这题一看没有什么思路 幸好我们机房的红太阳\(ghj1222\)切了这道题 首先我们考虑风跑一个来回之后人怎么样 就是跑了一个区间 也就是风跑了若干个来回之后 人跑了若干个区间 所以我们考虑 ...