前言:作为安全人员,代理大家用的都很多,那什么是java中的动态代理呢?事实上,java中的“动态”也就意味着使用了反射,因此动态代理是基于反射机制的一种代理模式。

简介:

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

 
首先以一个简单的代码实例看一下动态代理的应用
先声明一个接口subject,再声明一个realSubject类实现该接口
subject接口
public interface subject {
void simpleSubject();
}

realSubject实现类

public class realSubject implements subject{
public void simpleSubject(){
System.out.println("this is simpleSubject");
} }

每一个realSubject对象都拥有这个simpleSubject接口,但如果某个对象希望在运行的时候动态的附加一些hardSubject功能,应该怎么做呢?我们通过实现一个代理对象并在代理对象上附加hardSubject功能(或者附加一些信息)来实现。实现方式是通过Proxy.newProxyInstance生成一个代理对象,在生成代理对象的同时给这个代理对象绑定一个Handler,该handler即调用处理器(InvocationHandler)对象。

首先实现一个proxyHandler在原来的接口方法的前或后面来添加hardSubject功能(这里说功能或许不太准确)

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class proxyHandler implements InvocationHandler {
private realSubject subject;
public proxyHandler(realSubject subject){
this.subject = subject;
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("I'am the added hardSubject1");
method.invoke(subject);
System.out.println("I'am the added hardSubject2");
return null;
}
}

查看InvocationHandler接口的源码注解可以知道,它实现的功能是:在代理对象原本的接口方法被调用时,会绑定执行InvocationHandler中定义的方法。

接着我们编写一个测试类来实例一个代理对象

proxyTest 测试类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy; public class proxyTest {
public static void main(String[] args){
realSubject rs = new realSubject();
InvocationHandler h = new proxyHandler(rs);
Class clazz = rs.getClass();
subject s = (subject)Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),h);
s.simpleSubject();
System.out.println(s.getClass());
}
}

Proxy的newProxyInstance方法的三个参数,分别是realSubject的类装载器,接口和我们定义的proxyHandler实例对象

看一下源码:

 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(这个代理类是如何生成的我们暂且不论,只需要知道用到了被代理类的类加载器,实现的接口以及反射。深入需要再往下跟代码跟到ProxyGenerator.generateProxyClass来生成代理类,这里就只分析这段代码) 再通过代理类的Class对象cl使用getConstructor获取构造方法,最后通过构造方法反射获得一个代理类对象,可以看到在用newInstance生成代理类对象的时候我们传入了proxyHandler对象h

值得一提的是这个代理对象给的引用是subject接口类,这个对象其实是不属于realSubject和proxyHandler的,但因为它实现了subject接口,所以可以给它一个安全的类型转换到subject引用。但事实上我们运行这段代码,s.getClass()的输出是

也就是说它不属于任何我们看到的类,但它所属的类的父类是proxy类,这个代理对象属于$Proxy0类,这个代理类是放在内存中的,当有多个代理对象时,0会依次递增

最后我们来总结一下动态代理实现的步骤:

1.通过实现 InvocationHandler 接口创建自己的调用处理器;

2.通过为 Proxy 类指定 ClassLoader 和一组 interface 来创建动态代理类;

3.通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

4.通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

2020.4.1更新:

拿到$Proxy0类的class文件用jd-gui打开看一下生成的$Proxy0类的代码,看看到底是怎么做到当调用实现类的接口函数时,绑定执行了h中的invoke方法的

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 subject {
private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
} public final boolean equals(Object paramObject) {
try {
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
} public final void simpleSubject() {
try {
this.h.invoke(this, m3, null);
return;
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
} public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
} public final int hashCode() {
try {
return ((Integer)this.h.invoke(this, m0, null)).intValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
} static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("subject").getMethod("simpleSubject", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
} catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
} catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
}

从生成的代理类字节码反编译的结果中可以看出,当调用s.simpleSubject(),执行了h.invoke方法

(前面已经说过该代理类的生成是通过ProxyGenerator.generateProxyClass直接创建类的字节码再加载到jvm中,这也解释了为什么叫动态代理)

一个问题:

Class<?> cl = getProxyClass0(loader, intfs) 这行代码就已经拿到了生成代理类的Class对象,为什么不直接使用Class的newInstance方法创建对象?

答: 因为在前面反射的文章里就说过Class对象的newInstance方法只能调用该类的的无参构造函数,而这里可以看到该类的构造函数是有参构造函数,参数就是h

java安全初学之动态代理的更多相关文章

  1. Java Proxy和CGLIB动态代理原理

    动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...

  2. java反射机制与动态代理

    在学习HadoopRPC时.用到了函数调用.函数调用都是採用的java的反射机制和动态代理来实现的,所以如今回想下java的反射和动态代理的相关知识. 一.反射 JAVA反射机制定义: JAVA反射机 ...

  3. Java反射机制以及动态代理

    Java反射机制以及动态代理 Java反射机制 含义与功能 Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类 ...

  4. Java中的JDK动态代理

    所谓代理,其实就是相当于一个中间人,当客户端需要服务端的服务时,不是客户直接去找服务,而是客户先去找代理,告诉代理需要什么服务,然后代理再去服务端找服务,最后将结果返回给客户. 在日常生活中,就拿买火 ...

  5. Java核心技术点之动态代理

    本篇博文会从代理的概念出发,介绍Java中动态代理技术的使用,并进一步探索它的实现原理.由于个人水平有限,叙述中难免出现不清晰或是不准确的地方,希望大家可以指正,谢谢大家:) 一.概述 1. 什么是代 ...

  6. Java 第七天 动态代理

    代理类需实现InvocationHandler接口: public interface InvocationHandler { public Object invoke(Object proxy,Me ...

  7. Java设计模式系列之动态代理模式(转载)

    代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念. 代理模式示例代码 public interface Sub ...

  8. JAVA提高八:动态代理技术

    对于动态代理,学过AOP的应该都不会陌生,因为代理是实现AOP功能的核心和关键技术.那么今天我们将开始动态代理的学习: 一.引出动态代理 生活中代理应该是很常见的,比如你可以通过代理商去买电脑,也可以 ...

  9. 学习Spring必学的Java基础知识(2)----动态代理

    Spring AOP使用动态代理技术在运行期织入增强的代码,为了揭示Spring AOP底层的工作机理,有必要对涉及到的Java知识进行学习.Spring AOP使用了两种代理机制:一种是基于JDK的 ...

随机推荐

  1. Proud Merchants HDU - 3466 01背包&&贪心

    最近,我去了一个古老的国家.在很长一段时间里,它是世界上最富有.最强大的王国.结果,这个国家的人民仍然非常自豪,即使他们的国家不再那么富有.商人是最典型的,他们每个人只卖一件商品,价格是Pi,但是如果 ...

  2. Linux系统编程【3.1】——编写ls命令

    ls命令简介 老规矩,直接在终端输入:man ls (有关于man命令的简介可以参考笔者前期博客:Linux系统编程[1]--编写more命令) 可以看到,ls命令的作用是显示目录中的文件名,它带有可 ...

  3. SpringBoot整合shiro-MD5盐值加密

    为什么要进行密码加密? 在我们的日常生活中,许多人有着在不同网站上使用相同密码的坏习惯(包括我也是qaq),假如应用程序或服务器出现漏洞,数据被窃取,用户的明文密码直接被暴露给黑客.显然后果将不堪设想 ...

  4. C- c常见问题分析

    一.遇到undefined symbol怎么办: (1)首先解析未定义符号是什么:[利用c++filt命令] c++filt _ZN4Json5ValueixERKNSt7__cxx1112basic ...

  5. 字节笔试题 leetcode 69. x 的平方根

    更多精彩文章请关注公众号:TanLiuYi00 题目 解题思路 题目要求非负整数 x 的平方根,相当于求函数 y = √x 中 y 的值. 函数 y = √x  图像如下: 从上图中,可以看出函数是单 ...

  6. Win10永久禁用驱动程序强制签名

    在win10下用一个命令就可以禁用驱动程序强制签名 1.禁止强制签名,以管理员的身份运行cmd 执行以下命令 bcdedit.exe /set nointegritychecks on 恢复默认验证, ...

  7. C++ part7

    1.C++ 继承和组合 类的组合和继承一样,是复用的重要方式. 要优先使用组合而不是继承. 原因: 组合是黑箱复用,对局部类的内部细节不可见:继承是白箱复用,父类的内部细节可见,破坏封装性. 继承在编 ...

  8. python类属性和对象属性、类的普通方法和静态方法

    类属性和对象属性的定义 class LearnClass(): #类属性 cls_attr = None def __init__(self,arg): #对象属性 self.obj_attr = a ...

  9. 前端知名人士 All In One

    前端知名人士 All In One 前端名人堂(中国) https://node.fequan.com/lecturer/ JavaScript的过去.现在和未来 1995年,Brendan Eich ...

  10. Principle for iOS App Animation Design

    Principle for iOS App Animation Design Animate Your Ideas, Design Better Apps https://principleforma ...