proxy代理模式,让我们可以通过中间代理类去操作被代理类的一些处理,让操作者不直接操作对象类
就好比明星和经纪人的关系(参考别人博客的说法),除了明星自己的独特的演出,或者唱歌,需要调用对象本身,其余的拉赞助,接通告等其它的事情可以交给经纪人去做.
这里的经济人就相当于代理
为什么要用代理模式
当我们要对某一个方法进行增强时,比如对某个方法计算他的效率,打印开始结束日志等,我们可能会在每次调用一个方法的时候,去执行一次执行时间的统计,或者日志的打印
(比如多个明星,就需要跟着多个经纪人分开跑—>这里如果有个经济公司,统一管理这些明星)
package it.luke.proxy.v00;
public class Url_req {
public void filter(){
System.out.println("filter ...");
}
public static void main(String[] args) {
Url_req url_req = new Url_req();
long start = System.currentTimeMillis();
System.out.println(start);
url_req.filter();
System.out.println(System.currentTimeMillis() - start);
}
}
如果需要统计多个方法的话,就需要重复的对统计方法进行调用
package it.luke.proxy.v00;
public class Url_req {
public void filter(){
System.out.println("filter ...");
}
public void filter02(){
System.out.println("filter02 ...");
}
public static void main(String[] args) {
Url_req url_req = new Url_req();
long start = System.currentTimeMillis();
System.out.println(start);
url_req.filter();
System.out.println(System.currentTimeMillis() - start);
long start1 = System.currentTimeMillis();
System.out.println(start);
url_req.filter02();
System.out.println(System.currentTimeMillis() - start1);
}
}
如果是多个类的话,就需要分别对多个类的执行方法进行重复调用,代码变得庞大,而且繁琐
这个时候可以通过代理模式,将被调用的类,通过代理类进行调用
下面是一个简单的代理的实现
package it.luke.proxy.v01;
/**
* 一个请求url的对象,实现filter方法
*/
public class Url_req implements FilterInter {
@Override
public void filter() {
System.out.println("running with filter ...");
}
}
/**
* 通过代理类的方式,调用被代理类的方法
* 可以附加额外的一些方法
*/
class Proxy_url implements FilterInter{
//通过聚合的方式引入对象(代理多个明星)
Url_req url_req ;
public void setUrl_req(Url_req url_req) {
this.url_req = url_req;
}
/**
* 模拟经纪人拉明星1去演出
*/
@Override
public void filter() {
url_req.filter();
}
public static void main(String[] args) {
Proxy_url proxy_url = new Proxy_url();
//除了调用被代理类的方法,还可以调用其它的方法进行补充(经纪人还可以做一些其它的,整理行程...)
System.out.println("开始调用代理方法....");
//调用代理类,让代理类去调用被代理类的方法(经纪人拉着明星去演出)
proxy_url.filter();
//代理类的其余方法(经纪人叫车接送明星回去)
System.out.println("代理结束...");
}
}
以上是静态代理,需要聚合被代理类,然后实现被代理实现的接口,然后才能通过代理类去调用被代理类的方法
如果是针对多个不同的类的不同方法,可以通过实现不同的接口,对被代理类进行聚合
package it.luke.proxy.v01;
/**
* 一个请求url的对象,实现filter方法
*/
public class Url_req implements FilterInter {
@Override
public void filter() {
System.out.println("running with filter ...");
}
}
class Url_spo implements FilterInter {
@Override
public void filter() {
System.out.println("respone filter running ...");
}
}
/**
* 通过代理类的方式,调用被代理类的方法
* 可以附加额外的一些方法
*/
class Proxy_url implements FilterInter{
//通过聚合的方式引入对象(代理多个明星)
//明星1
Url_req url_req ;
//明星2
Url_spo url_spo ;
public void setUrl_spo(Url_spo url_spo) {
this.url_spo = url_spo;
}
public void setUrl_req(Url_req url_req) {
this.url_req = url_req;
}
/**
* 模拟经纪人拉明星去演出
*/
@Override
public void filter() {
url_req.filter();
url_spo.filter();
}
public static void main(String[] args) {
Proxy_url proxy_url = new Proxy_url();
//除了调用被代理类的方法,还可以调用其它的方法进行补充(经纪人还可以做一些其它的,整理行程...)
System.out.println("开始调用代理方法....");
//调用代理类,让代理类去调用被代理类的方法(经纪人拉着明星去演出)
proxy_url.filter();
//代理类的其余方法(经纪人叫车接送明星回去)
System.out.println("代理结束...");
}
}
小记:主要用的的都是多态的思想,让代理类实现和被代理类一样的接口,拿到相同的方法名,然后引入被代理类到代理对象中
什么是动态代理,和静态代理不同的是,静态代理的代理类是需要自己去实现被代理类的实现接口,通过实现接口去拿到和被代理类一样的方法名
而动态代理,是程序帮你生成代理类,和静态代理一样,需要被代理类的接口,拿到相同的方法名,然后将一些有经纪人进行操作的"杂物"封装到invoke 中,通过代理类去调用
通过调用java 提供的Proxy这个类,通过newProxyInstance,获取一个代理实例,要获取这个实例需要传三个参数,通过着三个参数可以定位到被代理的类,然后通过反射的方法,对被代理类进行处理.
//ClassLoader loader, 用来加载被代理类
//Class>[] interfaces, 被代理类的实现接口
//InvocationHandler h 行为处理器
InvocationHandler 行为处理器这个参数主要是要重写invoke方法,因为在动态代理生成后,底层再构建代理类的时候,会自动调用invoke这个方法.
可以通过"jdk.proxy.ProxyGenerator.saveGeneratedFiles" true 来查看保存在本地的生成代理文件
package it.luke.proxy.v02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理
*/
public class Dynamic_proxy {
/*
动态代理,顾名思义,自己进行调用代理,不在需要自己去指定被代理类的实现
*/
public static void main(String[] args) {
// System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
//通过设置这个参数,可以将程序动态生成的代理保存下来 不同的java 版本可能设置的参数不一样,有坑-->java8
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Url_req url_req = new Url_req();
//ClassLoader loader, 用来加载被代理类
//Class>[] interfaces, 被代理类的实现接口
//InvocationHandler h 行为处理器
FilterInter o = (FilterInter)Proxy.newProxyInstance(Url_req.class.getClassLoader(), new Class[]{FilterInter.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("methon " + method.getName() + "start");
Object invoke2 = method.invoke(url_req,args);
System.out.println("methon " + method.getName() + "end");
return invoke2;
}
});
o.filter();
}
}
代码小记:
先准备好被代理类的类加载器(确保被代理的时候能够正确的调用到被代理类的方法)
获取被代理类的方法实现接口(确保生成的代理类拥有和被代理类一样的方法名…底层通过匹配MethonName 来调用类加载器加载的类对象对应的方法—>用到反射)
准备好执行逻辑 invoke() 需要传入一个实现了InvocationHandler接口的对象,并重写invoke()方法(invoke在代理类生成的时候,同时生成在代理类中)
最后通过调用生成的代理对象,来实现对被代理对象的处理
为了更直观的查看到动态生成的代理对象,我们将动态生成的临时代理对象保存下来
System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import it.luke.proxy.v02.FilterInter;
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 FilterInter {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
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 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 void filter() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void filter_parm(Integer var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("it.luke.proxy.v02.FilterInter").getMethod("filter");
m4 = Class.forName("it.luke.proxy.v02.FilterInter").getMethod("filter_parm", Class.forName("java.lang.Integer"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
动态生成的代理类对象可以看到,在调用代理类中的filter的时候,调用了父类的h.invoke方法,其实就是前面实现InvocationHandler接口的时候定义的invoke()方法
底层是通过asm框架进行代理对象的构建,可以直接修改二进制码(待了解)
缺陷:通过jdk api进行动态代理,要求被代理类要实现接口,并且在接口中定义方法
除了jdk proxy 方法可以进行动态代理,java还提供了CGlib
jdk 是通过接收被代理类的接口的方式匹配到被代理类的方法
而CGlib是通过继承被代理类,通过重写的方式进行代理…