You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
5.2 KiB
186 lines
5.2 KiB
---
|
|
代理模式
|
|
---
|
|
|
|
#### 目录
|
|
|
|
1. 思维导图
|
|
2. 概述
|
|
3. 静态代理
|
|
4. 动态代理
|
|
5. 其他方式
|
|
6. 参考
|
|
|
|
#### 思维导图
|
|
|
|
![](https://i.loli.net/2019/01/08/5c3490e9f13df.png)
|
|
|
|
#### 概述
|
|
|
|
代理模式也称为委托模式,属于结构型设计模式。它为对象提供一种代理以控制对这个对象的访问。
|
|
|
|
当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。
|
|
|
|
#### 静态代理
|
|
|
|
```java
|
|
//定义一个操作的接口
|
|
public interface Operate {
|
|
void doSomething();
|
|
}
|
|
```
|
|
|
|
```java
|
|
//具体化一个操作者
|
|
public class Operator implements Operate {
|
|
@Override
|
|
public void doSomething() {
|
|
System.out.println("I'm doing something");
|
|
}
|
|
}
|
|
```
|
|
|
|
```java
|
|
//操作者的代理类
|
|
public class OperatorProxy implements Operate {
|
|
private Operator operator;
|
|
|
|
@Override
|
|
public void doSomething() {
|
|
if (operator == null) {
|
|
operator = new Operator();
|
|
}
|
|
beforeDoSomething();
|
|
operator.doSomething();
|
|
afterDoSomething();
|
|
}
|
|
|
|
private void beforeDoSomething() {
|
|
System.out.println("Before do something");
|
|
}
|
|
|
|
private void afterDoSomething() {
|
|
System.out.println("After do something");
|
|
}
|
|
}
|
|
```
|
|
|
|
```java
|
|
//客户端通过操作者的代理去执行操作者要做的事
|
|
public class StaticProxyTest {
|
|
public static void main(String[] args) {
|
|
OperatorProxy proxy = new OperatorProxy();
|
|
proxy.doSomething();
|
|
}
|
|
}
|
|
```
|
|
|
|
静态代理让调用者不在持有具体操作者的引用,而是将一切操作都交给代理者去完成。但是缺点也是显而易见的,每当增加一个代理方法都得改代理类,而且一个代理类之代理一个具体的操作。
|
|
|
|
#### 动态代理
|
|
|
|
Java 动态代理是通过 InvocationHandler 来实现的。
|
|
|
|
```java
|
|
public class InvocationHandlerImpl implements InvocationHandler {
|
|
|
|
private Operate operate;
|
|
|
|
public InvocationHandlerImpl(Operate operate) {
|
|
this.operate = operate;
|
|
}
|
|
|
|
@Override
|
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
|
System.out.println("Before call");
|
|
method.invoke(operate,args);
|
|
System.out.println("After call");
|
|
return null;
|
|
}
|
|
}
|
|
```
|
|
|
|
```java
|
|
public class DynamicProxyTest {
|
|
public static void main(String[] args) {
|
|
Operate operate = new Operator();
|
|
InvocationHandlerImpl impl = new InvocationHandlerImpl(operate);
|
|
Operate operateProxy = (Operate) Proxy.newProxyInstance(operate.getClass().getClassLoader(), operate.getClass().getInterfaces(), impl);
|
|
operateProxy.doSomething();
|
|
}
|
|
}
|
|
```
|
|
|
|
可以看到,动态代理是利用反射实现的,而且只能代理接口方法,对于非接口方法表示无能为力。
|
|
|
|
#### 其它方式
|
|
|
|
既然动态代理是通过运行时生成类来实现的,那么还有没有别的方式运行时动态生成类呢?当然有。
|
|
|
|
##### Javassist
|
|
|
|
官网:[http://www.javassist.org/](http://www.javassist.org/)
|
|
|
|
```java
|
|
public class AssistTest {
|
|
public static void main(String[] args) throws Exception {
|
|
ClassPool classPool = ClassPool.getDefault();
|
|
CtClass ctClass = classPool.makeClass("javassist.AutoGenerateClass");
|
|
CtMethod ctMethod = CtNewMethod.make("public void show(){}", ctClass);
|
|
ctMethod.insertBefore("System.out.println(\"Hello World!\");");
|
|
ctClass.addMethod(ctMethod);
|
|
ctClass.writeFile();
|
|
|
|
Class clazz = ctClass.toClass();
|
|
Method method = clazz.getMethod("show");
|
|
method.invoke(clazz.newInstance());
|
|
}
|
|
}
|
|
```
|
|
|
|
##### CGLib
|
|
|
|
```java
|
|
//具体化代理类操作
|
|
public class MethodInterceptorImpl implements MethodInterceptor {
|
|
@Override
|
|
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
|
|
System.out.println("Before call ");
|
|
methodProxy.invokeSuper(o, objects);
|
|
System.out.println("After call");
|
|
return null;
|
|
}
|
|
}
|
|
```
|
|
|
|
```java
|
|
public class CGLibTest {
|
|
public static void main(String[] args) {
|
|
Operator operator = new Operator();
|
|
MethodInterceptorImpl impl = new MethodInterceptorImpl();
|
|
|
|
//初始化加强器对象
|
|
Enhancer enhancer = new Enhancer();
|
|
//设置代理类
|
|
enhancer.setSuperclass(operator.getClass());
|
|
//设置代理回调
|
|
enhancer.setCallback(impl);
|
|
Operator operatorProxy = (Operator) enhancer.create();
|
|
operatorProxy.doSomething();
|
|
}
|
|
}
|
|
```
|
|
|
|
可以看到,CGLib 的实现方式和 Java 的动态代理实现方式很像,但是 CGLib 是可以代理所有方法的。
|
|
|
|
简单总结来说,Java 方法实现的代理类和委托类是兄弟关系,而 CGLib 方法实现的代理类和委托类是父子关系。
|
|
|
|
##### ASM
|
|
|
|
CGLib 底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。
|
|
|
|
#### 参考
|
|
|
|
[https://juejin.im/post/5c2039e451882561431a372a](https://juejin.im/post/5c2039e451882561431a372a)
|
|
|
|
[https://mp.weixin.qq.com/s/cTJ_IankiFOdwZZLq3mHQA](https://mp.weixin.qq.com/s/cTJ_IankiFOdwZZLq3mHQA) |