Java静态代理和动态代理
代理类的两个优点:
- 可以隐藏委托类的实现
- 可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。
1静态代理
UML图如下:
ServiceInterface: 服务接口类
ServiceImpl:服务具体实现类,也就是被代理类,即委托类
Proxy:代理类。
ServiceImpl和Proxy都继承自同一个接口,proxy类通过包含一个ServiceImpl的引用从而对客户提供服务,Proxy类自身也可以扩展doSomething()的实现,从而达到代理的意义
/**
* 代理类
*/
public class Proxy implements ServiceInterface {
private ServiceImpl server;
public BusinessAgent(ServiceImpl server){
this.server=server;
}
public void doSomething {
System,out.println("say hello");
System,out.println("smile");
this.server.doSomething();
System,out.println("say goob bye");
}
}
静态代理的局限在于:必须在运行前就编写好代理类
2.动态代理
2.1 动态代理的使用
假设存在这样一个需求:存在一个ServiceImpl类,它包含了上百个方法,我们需要在每次调用ServiceImpl中的任何一个方法之前打印一条日志。
如果还是用静代理的方式实现的话,那么我们就要重新实现这上百个方法,并且还包含了大量冗余。因此动态代理排上用处了
动态代理其实就是通过Java的反射机制,对委托类的调用进行一个封装扩展。
class ServiceHandler implements InvocationHandler {
//委托类对象;
private ServiceImpl server;
public ServiceHandler(ServiceImpl server) {
this.server = server;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info(method.getName())
Object result = method.invoke(server, args);
return result;
}
}
如上所示,ServiceHandler中包含了一个委托类的引用,客户端通过动态代理对象来调用方法时,最终其实是调用到invoke()方法,invoke()方法中通过反射来调用委托类的具体方法,动态代理的所有扩展操作也都是在invoke()方法中。
如何获取动态代理对象呢?
ServiceInterface serverProxy = (ServiceInterface) Proxy.newProxyInstance(ServiceInterface.class.getClassLoader(), new Class[]{ServiceInterface.class}, new ServiceHandler(new ServiceImpl()));
serverProxy.doSomething();
java.lang.reflect.Proxy: 这是生成代理类的主类
//创建代理对象
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
需要三个参数:
- 用来加载代理类的classloader
- 代理类需要实现的interface
- handler对象(初始化时需要传入委托类的实例)
总结一下动态代理:
- 代理对象是在程序运行时产生的,而不是编译期
- 对代理对象的所有接口方法调用都会转发到InvocationHandler.invoke()方法,在invoke()方法里我们可以加入任何逻辑
- 优点:Java动态代理可以避免静态代理带来的代码冗余的问题。
- 缺点:Java动态代理只能针对接口创建代理,不能针对类创建代理 (因为每个动态代理类都要继承Proxy类,且Java是单继承,因此没法对针对类创建代理了)
2.2 动态代理的原理
动态代理类是在运行时生成的,并不是编译时生成。
在调用newProxyInstance生成动态代理对象之前,加入下面一段代码可以将生成的动态代理class文件保存下来
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
生成的动态代理class文件如下:
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import me.zm.ServiceInterface;
public final class $Proxy0 extends Proxy implements ServiceInterface {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void doSomething() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("me.zm.ServiceInterface").getMethod("doSomething");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到生成的动态代理类继承自proxy类并且实现了ServiceInterface接口。而doSomething方法的实现其实是调用了handler的invoke方法。
除此之外,还可以看到动态代理对象的equals、hashCode、toString这三个方法也都是直接调用了handler的实现
参考:
http://www.importnew.com/27772.html
https://blog.csdn.net/kisimple/article/details/43709505
上一篇: JavaScript中判断的优雅写法示例
下一篇: Spring.NET AOP初体验和实践
推荐阅读
-
门面模式和代理模式区别(2种模式对比分析)
-
详解Spring的两种代理方式:JDK动态代理和CGLIB动态代理
-
极光大数据:B站月均DAU超2,000万,游戏代理和联运或成盈利关键
-
Java 源码剖析如何实现动态代理
-
.net core3.0部署Linux服务器 使用Docker容器和Nginx反代理教程
-
jsp中include指令静态导入和动态导入的区别详解
-
JavaScript静态作用域和动态作用域实例详解
-
Spring学习之动态代理(JDK动态代理和CGLIB动态代理)
-
Mybaits 源码解析 (十一)----- 设计模式精妙使用:静态代理和动态代理结合使用:@MapperScan将Mapper接口生成代理注入到Spring
-
Java静态和非静态成员变量初始化过程解析