简介
用意:
为其余对象提供一种代理以管制整个对象的拜访。
次要解决:
在间接拜访对象时带来的问题,比如说:要拜访的对象在近程的机器上。在面向对象的零碎中,有些对象因为某些起因(比方对象创立开销很大,或者某些操作须要安全控制,或者须要过程外的拜访),间接拜访会给使用者或者系统结构带来很多麻烦,咱们能够在拜访此对象时加上一个对此对象的拜访层。
长处:
- 职责清晰
- 高扩展性
- 智能化
毛病:
- 因为在客户端和实在主题之间减少了代理对象,因而有些类型的代理模式可能会造成申请的处理速度变慢。
- 实现代理须要额定的工作,有些代理模式实现起来非常复杂。
应用场景:
- 近程代理
- 虚构代理
- Copy-on-Write 代理
- 爱护代理
- Cache 代理
- 防火墙代理
- 同步化代理
- 智能援用代理
实现
动态代理
动态代理在应用时,须要定义接口或者父类,被代理对象与代理对象一起实现雷同的接口或者是继承雷同父类。
接口
interface Movable {void move();
}
被代理类,实现该接口 move 办法
public class Tank implements Movable {
/**
* 模仿坦克挪动了一段儿工夫
*/
@Override
public void move() {System.out.println("Tank moving claclacla...");
try {Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {e.printStackTrace();
}
}
}
代理类同样实现该接口,增加 Movable 属性,实现 move 办法外部调用传入的被代理类的 move 办法,在调用前后能够增加一些业务代码
class TankTimeProxy implements Movable {
Movable m;
public TankTimeProxy(Movable m) {this.m = m;}
@Override
public void move() {long start = System.currentTimeMillis();
m.move();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
最初应用 main 办法调用代理类的 move 办法,如果须要多个代理类,能够多层嵌套应用
class Demo{public static void main(String[] args) {Tank t = new Tank();
TankTimeProxy ttp = new TankTimeProxy(t);
// TankLogProxy tlp = new TankLogProxy(ttp);
ttp.move();}
}
动态代理总结:
- 能够做到在不批改指标对象的性能前提下,对指标性能扩大。
- 毛病:因为代理对象须要与指标对象实现一样的接口,所以会有很多代理类,类型太多。同时,一旦接口减少办法,指标对象与代理对象都要保护。
动静代理
特点:
- 代理对象,不须要实现接口
- 代理对象的生成,是利用 JDK 的 API,动静的内存中构建代理对象(没有接口的 cglib 代理)
被代理类,实现该接口 move 办法
public class Tank implements Movable {
/**
* 模仿坦克挪动了一段儿工夫
*/
@Override
public void move() {System.out.println("Tank moving claclacla...");
try {Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {e.printStackTrace();
}
}
}
JDK 代理:
代理类所在的包:java.lang.reflect.Proxy
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
- ClassLoader loader:指定以后指标对象应用类加载器
- Class<?>[] interfaces:指标对象实现的接口的类型
- InvocationHandler h:事件处理,执行指标对象的办法时会触发事件处理器,会把以后执行指标对象的办法作为参数传入
接口
interface Movable {void move();
}
被代理类,实现该接口 move 办法
public class Tank implements Movable {
/**
* 模仿坦克挪动了一段儿工夫
*/
@Override
public void move() {System.out.println("Tank moving claclacla...");
try {Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {e.printStackTrace();
}
}
}
创立一个代理类的调用处理程序
class TimeProxy implements InvocationHandler {
// 代理类中的实在对象
Movable m;
public TimeProxy(Movable m) {this.m = m;}
public void before() {System.out.println("method start..");
}
public void after() {System.out.println("method stop..");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();
Object o = method.invoke(m, args);
after();
return o;
}
}
Demo 调用,生成指定接口的代理类实例
public static void main(String[] args) {Tank tank = new Tank();
Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
new Class[]{Movable.class}, //tank.class.getInterfaces()
new TimeProxy(tank) );
m.move();}
上述演示的时生成指定接口的代理类示例,然而 JDK 中时,通过 JDK 代理或者 cglib 代理在运行时主动生成到内存中的。
JDK 动静代理原理
生成动静代理文件
办法一:
- 必须在 main 办法中执行,间接用 junit 的 test 办法调用无奈生成
- JDK1.8 之后在 main 办法最后面减少 System.getProperties().put(“jdk.proxy.ProxyGenerator.saveGeneratedFiles”,”true”);
- JDK1.8 及其之前在 main 办法最后面减少 System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,”true”);
办法二:
- 运行时退出 jvm 参数 -Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
- 运行时退出 jvm 参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
如何确定是哪个参数, 查看 ProxyGenerator 类内容,如下:
而后执行后就会在当前目录下 com 包中生成代理类的 class 文件。
而后。。。
这就是源码过程了,先上图
截图发现图片太多就算了。。。
Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
new Class[]{Movable.class},
new TimeProxy(tank));
/*
* Look up or generate the designated proxy class and its constructor.
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return proxyCache.sub(intf).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build());
Constructor<?> build() {Class<?> proxyClass = defineProxyClass(module, interfaces);
final Constructor<?> cons;
try {cons = proxyClass.getConstructor(constructorParams);
} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);
return null;
}
});
return cons;
}
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);
final byte[] classFile = gen.generateClassFile();
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
methods.add(pm.generateMethod());
最最最初是通过 asm 来实现的
Cglib 动静代理
- 创立 Enhancer 对象
- 用 Enhancer 对象设置代理类的父类(被代理类)
- 创立回调对象(回调类实现 MethodInterceptor 接口)
- 用 Enhancer 对象设置回调对象
- 用 Enhancer 对象创立代理对象
创立一个拦截器,相当于 JDK 的 InvocationHander
class TimeMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println(o.getClass().getSuperclass().getName());
System.out.println("before");
Object result = null;
result = methodProxy.invokeSuper(o, objects);
System.out.println("after");
return result;
}
}
创立动静代理
/**
* CGLIB 实现动静代理不须要接口
*/public class Main {public static void main(String[] args) {Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Tank.class);
enhancer.setCallback(new TimeMethodInterceptor());
Tank tank = (Tank)enhancer.create();
tank.move();}
}
生成 cglib 代理的 class,上面两个任选其一
System.setProperty("cglib.debugLocation", "./cg");
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cg");
或者在 idea -> run -> edit configurations 中设置 vm options
-Dcglib.debugLocation=./cg
其余对于代理
正向代理:
- 晓得指标地址,但无奈间接拜访,须要通过代理才能够拜访。例如 IP 代理机场等。
- 正向代理的过程,暗藏了实在的客户端。客户端的申请服务都被代理服务器代替来申请。
反向代理:
- 不晓得指标地址,代理服务器就相当于指标服务器,由代理服务器承受所有申请后,转发给外部其余服务器。例如拜访百度 www.baidu.com 然而不晓得理论拜访是哪台服务器。
最全学习笔记大厂真题 + 微服务 +MySQL+ 分布式 +SSM 框架 +Java+Redis+ 数据结构与算法 + 网络 +Linux+Spring 全家桶 +JVM+ 高并发 + 各大学习思维脑图 + 面试汇合