代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
静态代理
静态代理是指预先确定了代理与被代理者的关系,在程序员运行之前,代理类.class文件就已经被创建了。
我们玩游戏经常遇见代打的情况,就用游戏的代打举例:
首先定义一个玩游戏的接口
package factory.proxy.staticproxy;
public interface IPlayGame {
public void play();
public void upGrade();
}
普通玩家玩游戏,实现玩游戏接口
package factory.proxy.staticproxy;
public class Player implements IPlayGame {
@Override
public void play() {
System.out.println("玩游戏。。。");
}
@Override
public void upGrade() {
System.out.println("打怪升级。。。");
}
}
代打玩游戏,要传入被代理人
package factory.proxy.staticproxy;
public class ProxyPlayer implements IPlayGame {
IPlayGame player = null;
public ProxyPlayer(IPlayGame player){
this.player = player;
}
@Override
public void play() {
player.play();
}
@Override
public void upGrade() {
player.upGrade();
}
}
客户端测试
package factory.proxy.staticproxy;
public class Client {
public static void main(String[] args) {
Player player = new Player();
ProxyPlayer proxyPlayer = new ProxyPlayer(player);
proxyPlayer.play();
proxyPlayer.upGrade();
}
}
代理模式可以有选择的在工作前后做一些必要的事情,比如我们代打游戏肯定收钱,那可以在代理类中加一个收钱的方法,放在代理方法中处理。
动态代理
动态代理本质上仍然是代理,情况与上面介绍的完全一样,只是代理与被代理人的关系是动态确定的,例如玩家平常没选代打,在玩游戏的时候才选代打,映射到编程领域为这个关系是在运行时确定的。
动态代理比起静态代理有什么好处呢?上面的静态代理例子中,如果要再换一个玩家的话,那就要重新创建玩家类与代理类了,扩展麻烦,不灵活,而用动态代理的话,那只要一个代打类即可,代打同时代打好几个玩家的号。
在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接口、另一个则是 Proxy类,这个类和接口是实现我们动态代理所必须用到的。
InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。
用动态代理实现上述过程
- 构建一个打游戏接口
package factory.proxy.dynamicproxy;
public interface IPlayGame {
public void play();
public void upGrade();
}
- 玩家类
package factory.proxy.dynamicproxy;
public class Player implements IPlayGame {
@Override
public void play() {
System.out.println("玩游戏。。。");
}
@Override
public void upGrade() {
System.out.println("升级。。。");
}
}
- 构建一个动态代理类
package factory.proxy.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxy implements InvocationHandler {
private Object target;//被代理的对象
public DynamicProxy(Object obj){
this.target = obj;
}
public Object getProxy(){//获得当前代理
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
System.out.println("当前进行到哪一步:" + method.getName());
Object result=method.invoke(target,args);
return result;
}
}
- 客户端使用
package factory.proxy.dynamicproxy;
public class Client {
public static void main(String[] args) {
Player player = new Player();
IPlayGame playGame = (IPlayGame) new DynamicProxy(player).getProxy();
playGame.play();
playGame.upGrade();
}
}
结果:
"E:\IntelliJ IDEA 2019.3.3\jbr\bin\java.exe" "-javaagent:E:\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=13505:E:\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath D:\moyiworkspace\ACMPractise\bin factory.proxy.dynamicproxy.Client
当前进行到哪一步:play
玩游戏。。。
当前进行到哪一步:upGrade
升级。。。
JDK动态代理实现的原理
首先Jdk的动态代理实现方法是依赖于接口的,首先使用接口来定义好操作的规范。然后通过Proxy类产生的代理对象调用被代理对象的操作,而这个操作又被分发给InvocationHandler接口的 invoke方法具体执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
此方法的参数含义如下
proxy:代表动态代理对象
method:代表正在执行的方法
args:代表当前执行方法传入的实参
返回值:表示当前执行方法的返回值
CGLIB代理
cglib是一个java字节码的生成工具,它动态生成一个被代理类的子类,子类重写被代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
导入jar包
//被代理类
public class Product {
public void getPrice(double d) {
System.out.println("价格是:" + d);
}
}
//代理
public class Client {
public static void main(String[] args) {
Product product = new Product();
Product cglib = (Product) Enhancer.create(product.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if("getPrice".equals(method.getName())) {//增强
objects[0] = (double) objects[0] * 0.8;
}
return method.invoke(product, objects);
}
});
cglib.getPrice(1000);
}
}
输出800.
常见的代理模式有以下几种类型:
远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又叫做大使(Ambassador)。
虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
防火墙(Firewall)代理:保护目标不让恶意用户接近。
同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。