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单继承)所以我们只能是代理接口
参考资料
java 基础 --- 动态代理和静态代理的更多相关文章
- Java代理(静态代理、JDK动态代理、CGLIB动态代理)
Java中代理有静态代理和动态代理.静态代理的代理关系在编译时就确定了,而动态代理的代理关系是在运行期确定的.静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性. J ...
- Java代理模式/静态代理/动态代理
代理模式:即Proxy Pattern,常用的设计模式之一.代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问. 代理概念 :为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委 ...
- 【Java】代处理?代理模式 - 静态代理,动态代理
>不用代理 有时候,我希望在一些方法前后都打印一些日志,于是有了如下代码. 这是一个处理float类型加法的方法,我想在调用它前打印一下参数,调用后打印下计算结果.(至于为什么不直接用+号运算, ...
- Java中的代理模式--静态代理和动态代理本质理解
代理模式定义:为其他对象提供了一种代理以控制对这个对象的访问. 代理模式的三种角色: Subject抽象主题角色:抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求. Real ...
- Java动态代理与静态代理以及它能为我们做什么
相信我们在网上和平时学习和工作中或多或少都接触过Java的代理模式,经常听到什么静态代理.动态代理的一些名词.但我们是否真的很清楚这些呢?至少我在面试时,发现很多人并不很清楚. 首先代理比较好理解,就 ...
- Atitit 代理CGLIB 动态代理 AspectJ静态代理区别
Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为 ...
- Java代理模式——静态代理模式
一:代理模式 代理模式的作用是:为其他对象提供一种代理以控制这个对象的访问.在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 二:代理模式设计 ...
- JAVA设计模式——代理(静态代理)
定义 为其它的对象提供一种代理,以控制这个对象的访问 使用场景 当不想直接访问某个对象的时候,就可以通过代理 1.不想买午餐,同事帮忙带 2.买车不用去厂里,去4s店 3.去代理点买火车票,不用去车站 ...
- jdk的动态代理和静态代理你还写不出来嘛???
一.什么叫jdk的代理? 用另外一个对象去代理实际对象的操作 分为动态代理和静态代理二.先说说静态代理 从字面意思来看就是不会改变的,只可以代理某个固定对象的. 静态代理就是通过实现和目标对象实现的同 ...
随机推荐
- RxJava / RxAndroid
RxJava 是什么 RxJava 是函数响应式编程框架,它用观察者设计模式. 常用来做异步数据处理,在安卓中用来代替传统的 AsyncTask + Handler 的组合结构. RxJava 架构简 ...
- php中的XML DOM(10)
1.PHP DOM (1) Php中的DOM跟javascript不一样,属性不用另外增加一个节点 2.主要类 DOMDocument :文档类 DOMNodeList :节点列表类 DOMNode ...
- windows挂载网络盘
@echo offset filename=%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%set filename="DataBak-%filename%"ne ...
- (四)SSO之CAS框架单点登录,自定义验证登录方式
应需求的变化,在登录cas的时候,默认根据用户名和密码进行验证,如果加上用户名,密码和一个系统标识进行验证呢?该如何做呢? 我们知道cas默认的登录界面中,输入的用户名和密码,再配置一下deploye ...
- [iOS笔试600题]一、语法篇(共有147题)
[A]1. @property 的作用是申明属性及真特性?[判断题] A.正确 B.错误 [A]2. @synthesize的作用是自动笠成属性的访问器(getter/setter)方法?[判断题] ...
- 注解中用于@target的方法annotation/--ElementType.METHOD,ElementType.TYPE对应方法,类接
@Target: @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages.types(类.接口.枚举.Annotation类型).类型成员(方法.构造 ...
- ps与grep组合命令使用
管道命令 我们在做运维的时候,经常会使用这个命令ps -ef | grep nginx. ps -ef 表示显示所有进程的消息. | 是管道命令.通常需要借助管道命令”|”多个命令的组合,形式如下: ...
- RN 47 中的 JS 线程及 RunLoop
RCBridge 初始化时声明了一个 CADisplayLink _jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector ...
- the type initializer for 'system.drawingcore.gdiplus' threw an exception
Centos 7 yum install libgdiplus-devel reboot之后生效 apt install libgdiplus cp /usr/lib/libgdiplus.so ~/ ...
- iOS学习笔记(3)--初识UINavigationController(无storyboard)
纯代码创建导航控制器UINavigationController 在Xcode6.1中创建single view application的项目,删除Main.storyboard文件,删除info.p ...