【代理模式】Java的静态代理与动态代理
代理模式
现实生活中,在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又如找女朋友、找保姆、找工作等都可以通过找中介完成。
程序中,代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理类主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,并将执行结果封装处理。通俗的来讲代理模式就是我们生活中常见的中介。
java为我们提供了基于接口的动态代理。介绍动态代理之前我们先说一下静态代理
代理模式的主要角色如下。
- 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
1.静态代理
创建一个接口,然后创建一个被代理的类实现接口中的抽象方法,再创建一个代理类,让代理类也实现这个接口,在代理类里创建一个被代理的类,在调用被代理类的方法之前先做一下处理
接口 Subject
public interface HelloWordInterface {
void sayHello();
}
被代理的类 Real Subject
public class HelloWord implements HelloWordInterface {
@Override
public void sayHello() {
System.out.println("hello laoniu");
}
}
代理类 Proxy
public class ProxyHello implements HelloWordInterface {
private HelloWordInterface hello = new HelloWord();
@Override
public void sayHello() {
System.out.println("帮他掰开嘴");
hello.sayHello();
System.out.println("say 完了,闭嘴");
}
}
测试
public class Main {
public static void main(String[] args) {
ProxyHello proxyHello = new ProxyHello();
proxyHello.sayHello();
}
}
输出
上面这个例子很简单,使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐
2.动态代理
2.1.实现
动态代理是jdk为我们提供的利用反射机制创建代理对象的方式,接口、被代理类不变。改造一下代码
代理类
public class ProxyHello implements InvocationHandler {
private HelloWordInterface hello; //要代理的接口,被代理类要实现这个接口
public ProxyHello(HelloWordInterface hello) {
this.hello = hello;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//如果调用的方法不是sayHello方法就不进行代理
if (!method.getName().equals("sayHello")){
return method.invoke(hello,args);
}
System.out.println("帮他掰开嘴");
Object o = method.invoke(hello,args); //method是拦截的方法
System.out.println("say 完了,闭嘴");
return o; // o是方法的返回值
}
}
测试类
public class Main {
public static void main(String[] args) {
HelloWordInterface helloWord = new HelloWord(); //被代理的对象创建
ProxyHello proxyHello = new ProxyHello(helloWord); //把被代理的对象放入代理对象让其进行代理
//生成代理对象
HelloWordInterface proxyInstance =
(HelloWordInterface)Proxy.newProxyInstance(proxyHello.getClass().getClassLoader(), //代理类的类加载器
HelloWord.class.getInterfaces(),//要代理的接口
proxyHello); //代理对象
//使用动态生成的代理对象进行调用
proxyInstance.sayHello();
}
}
测试
2.2分析
2.2.1 动态代理具体步骤:
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
2.2.2 jdk到底是怎样生成代理对象的?
通过查看Proxy.newProxyInstance()源码得知,最终调用了ProxyGenerator.generateProxyClass()来为我们生成了一个字节码文件.class
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
刨根问底,我们去看看我的代理对象到底长什么样子
改造一下测试类
public class Main {
public static void main(String[] args) {
HelloWordInterface helloWord = new HelloWord(); //被代理的对象创建
ProxyHello proxyHello = new ProxyHello(helloWord); //把被代理的对象放入代理对象让其进行代理
//生成代理对象
HelloWordInterface proxyInstance =
(HelloWordInterface)Proxy.newProxyInstance(proxyHello.getClass().getClassLoader(), //代理类的类加载器
HelloWord.class.getInterfaces(),//要代理的接口
proxyHello); //代理对象
//使用动态生成的代理对象进行调用
proxyInstance.sayHello();
//设置允许获取动态代理对象
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//保存动态代理对象
FileOutputStream out = null;
try {
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", HelloWord.class.getInterfaces());
//写入本地文件位置
out = new FileOutputStream("/Users/laoniu/"+ "$Proxy0.class");
out.write(classFile);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.flush();
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
我们把文件使用反编译软件打开查看,idea自带反编译,我直接把它拉入idea
反编译后的动态代理类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import com.niu.demo.HelloWordInterface;
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 HelloWordInterface {
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 sayHello() 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("com.niu.demo.HelloWordInterface").getMethod("sayHello");
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());
}
}
}
可以看到在构造动态代理类的时候把我们的代理类传了进去,然后再调用代理类的invoke方法
看一下我们的代理类是不是上面说的被调用invoke方法
到这里基本就明白jdk的动态代理是怎样实现的了
3.总结
jdk的动态代理要求业务类必须得实现业务接口,底层是通过生成业务接口的动态代理实现类来完成功能增强的
但是如果我们想使用动态代理却没有接口那该怎么做呢?我们可以使用cglib。
cglib不需要业务类实现接口,底层是通过衍生出当前业务类的子类对象来完成功能增强
动态代理非常常用,比如spring的aop就是基于动态代理来实现的。文中若是有什么错误的地方请大家指出来
还没有评论,来说两句吧...