设计模式中有六大原则和二十三设计模式。
其中六大原则分别为:单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特原则。
二十三设计模式:单例模式、Builder 模式、原型模式、工厂方法模式、抽象工厂模式、策略模式、状态模式、责任链模式、解释器模式、命令模式、观察者模式、备忘录模式、迭代器模式、模版方法模式、访问者模式、中介模式、代理模式、组合模式、适配器模式、装饰模式、享元模式、外观模式、桥接模式。
现在我们就介绍下代理模式(Proxy Pattern)。
定义
对其他对象提供一种代理以控制对这个对象的访问。
解释:就是不想把自己暴露给别人,委托他人帮自己实现某些功能。
网上说的增强,在我看来其实就是保留原有的内容,增加自己的逻辑(他也可以重写父类方法实现,不删除super方法)。
使用场景
当无法或不想直接访问某个对象时,或者某个对象不方便访问时,可以通过代理对象,间接访问。想要实现代理,需要委托者和代理者实现相同的接口。最典型的代理模式就是Spring 的AOP(切面)
例如:小明把小刚打了,小刚把小明告了,这时,小刚请了一个律师代理他处理这件事。如果小刚自己处理的话,需要提交申请、进行举证、辩护等诉讼流程。委托律师后,小刚把这些材料委托给律师,律师就可以代替小刚把事情处理了。在处理这些流程时,律师为了诉讼成功,增加了一些自己的东西(这就是我认为的【增强】)。
代理模式实现:
代理模式实现方式:静态代理和动态代理、动态代理又分为jdk代理和Cglib 代理。
静态代理
静态代理就是定义两个实现类A和B,他们都实现了接口C,B类构造器传入A,这时B就可以代替A 做一些事情了。
-
UML 图
示例代码
诉讼流程接口(定义业务接口)
package com.example.pattern.proxy.static
import org.slf4j.Logger
import org.slf4j.LoggerFactory
/**
* 诉讼流程
*/
interface LitigationProcessInterface {
// 提交诉讼申请
fun submitLawsuit();
// 举证
fun proof();
// 辩护
fun defend();
fun finish(){
val logger: Logger = LoggerFactory.getLogger(this::class.java)
logger.info("----- LitigationProcessInterface::finish 结束");
}
}
- 小刚提供的材料(被代理对象)
package com.example.pattern.proxy.static
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class XiaoGang : LitigationProcessInterface {
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
override fun submitLawsuit() {
logger.info("----- XiaoGang::::::submitLawsuit 小刚提交诉讼申请");
}
override fun proof() {
logger.info("----- XiaoGang::::::proof 小刚举证");
}
override fun defend() {
logger.info("----- XiaoGang::::::defend 小刚辩护");
}
}
- 律师代理(代理对象)
package com.example.pattern.proxy.static
import org.slf4j.Logger
import org.slf4j.LoggerFactory
/**
* 律师
*/
class Lawyer(private val xiaoGang: XiaoGang) : LitigationProcessInterface {
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
override fun submitLawsuit() {
logger.info("----- Lawyer::submitLawsuit1 律师代替小刚诉讼申请前")
// 小刚提交的诉讼申请
xiaoGang.submitLawsuit()
logger.info("----- Lawyer::submitLawsuit2 律师代替小刚诉讼申请后")
}
override fun proof() {
logger.info("----- Lawyer::proof1 律师在小刚举证内容前")
// 小刚提交的举证
xiaoGang.proof()
logger.info("----- Lawyer::proof2 律师在小刚举证内容后")
}
override fun defend() {
logger.info("----- Lawyer::defend1 律师在小刚辩护内容前")
// 小刚的辩护
xiaoGang.defend();
logger.info("----- Lawyer::defend2 律师在小刚辩护内容后")
}
}
- 流程实现(实现)
package com.example.pattern.proxy.static
object Client {
@JvmStatic fun main(args: Array) {
val xiaoGang:LitigationProcessInterface = XiaoGang();
val lawyer = Lawyer(xiaoGang as XiaoGang)
lawyer.submitLawsuit()
lawyer.proof()
lawyer.defend()
lawyer.finish()
}
}
- Log 信息:
12:22:58.491 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::submitLawsuit1 律师代替小刚诉讼申请前
12:22:58.496 [main] INFO com.example.pattern.proxy.static.XiaoGang - ----- XiaoGang::::::submitLawsuit 小刚提交诉讼申请
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::submitLawsuit2 律师代替小刚诉讼申请后
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::proof1 律师在小刚举证内容前
12:22:58.496 [main] INFO com.example.pattern.proxy.static.XiaoGang - ----- XiaoGang::::::proof 小刚举证
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::proof2 律师在小刚举证内容后
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::defend1 律师在小刚辩护内容前
12:22:58.496 [main] INFO com.example.pattern.proxy.static.XiaoGang - ----- XiaoGang::::::defend 小刚辩护
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::defend2 律师在小刚辩护内容后
12:22:58.497 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- LitigationProcessInterface::finish 结束
静态代理虽然效率挺高,但是它是需要和业务结合使用,实现类都需要实现业务的接口才可以实现功能,这也局限了一个代理对象需要实现一次,如果有另一个代理对象,那么还需要实现另一个代理对象,这时使用起来就不是很方便了。那么动态代理就解决了这个问题。
以上是使用接口(interface)方式实现的功能,其实也可以使用 abstract 的方式实现。只不过是implements 改成了extends .
动态代理
上面的静态代理是由程序员或者工具生成的代码进行编译,就是说在运行代码时,编译文件就已经存在了。而动态代理是与之相反的,它是通过反射机制动态生成代理对象。
Java 已经给我们提供了一个便捷的代理接口 InvocationHandler 。只要实现它就可以了。
- 示例代码
- 诉讼流程接口(定义业务接口)
package com.example.pattern.proxy.dynamic;
/**
* 诉讼流程
*/
public interface LitigationProcessInterface {
// 提交诉讼申请
void submitLawsuit();
// 举证
void proof();
// 辩护
void defend();
// 结束
void finish();
}
- 小刚提供的材料(被代理对象)
package com.example.pattern.proxy.dynamic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 小刚需要提供的内容
*/
public class XiaoGang implements LitigationProcessInterface {
private Logger logger = LoggerFactory.getLogger( this.getClass());
public void submitLawsuit() {
logger.info("----- XiaoGang::::::submitLawsuit 小刚提交诉讼申请");
}
public void proof() {
logger.info("----- XiaoGang::::::proof 小刚举证");
}
public void defend() {
logger.info("----- XiaoGang::::::defend 小刚辩护");
}
public void finish() {
logger.info("----- XiaoGang::::::finish 结束");
}
}
- 代理对象
package com.example.pattern.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 代理类
*/
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object o) {
this.object = o;
}
/**
* 反射
*
* @param proxy 代理实例
* @param method 方法
* @param args 方法参数
* @return 结果
* @throws Throwable 异常
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(this.object, args);
}
}
- 实现
package com.example.pattern.proxy.dynamic;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
LitigationProcessInterface xiaoGang = new XiaoGang();
DynamicProxy dynamicProxy= new DynamicProxy(xiaoGang);
//获取被代理对象小刚的 ClassLoader
ClassLoader xiaoGangClassLoader = xiaoGang.getClass().getClassLoader();
// 动态构造一个代理律师
LitigationProcessInterface lawyer = (LitigationProcessInterface) Proxy.newProxyInstance(xiaoGangClassLoader, new Class[]{LitigationProcessInterface.class}, dynamicProxy);
lawyer.submitLawsuit();
lawyer.proof();
lawyer.defend();
lawyer.finish();
}
}
- Log
13:23:01.047 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::submitLawsuit 小刚提交诉讼申请
13:23:01.050 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::proof 小刚举证
13:23:01.050 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::defend 小刚辩护
13:23:01.050 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::finish 结束
以上代码和静态代理很像,但是当增加另一个代理对象时,静态代理需要重新实现一个类(LitigationProcessInterface),而动态代理则不需要,只要再实例下代理就可以了。
其实单独定义一个代理对象 DynamicProxy 也是有局限的,它不能够增加额外的东西(也就是所谓的增强),若需要增加额外的东西,需要每个都要重新定义一下,这样和静态代理没有什么区别。
那么我们也可以直接 new 一个 InvocationHandler 对象,这样就可以增加额外的东西了。
package com.example.pattern.proxy.dynamic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
final Logger logger = LoggerFactory.getLogger( Client.class);
final LitigationProcessInterface xiaoGang = new XiaoGang();
// 方法二
InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("submitLawsuit".equals(method.getName())){
logger.info("----- Lawyer::submitLawsuit1 律师代替小刚诉讼申请前");
method.invoke(xiaoGang, args);
logger.info("----- Lawyer::submitLawsuit2 律师代替小刚诉讼申请后");
} else if ("proof".equals(method.getName())){
logger.info("----- Lawyer::proof1 律师在小刚举证内容前");
method.invoke(xiaoGang, args);
logger.info("----- Lawyer::proof2 律师在小刚举证内容后");
} else if ("defend".equals(method.getName())){
logger.info("----- Lawyer::defend1 律师在小刚辩护内容前");
method.invoke(xiaoGang, args);
logger.info("----- Lawyer::defend2 律师在小刚辩护内容后");
} else if ("finish".equals(method.getName())){
logger.info("----- Lawyer::finish1 结束前");
method.invoke(xiaoGang, args);
logger.info("----- Lawyer::finish2 结束后");
}
return null;
}
};
//获取被代理对象小刚的 ClassLoader
ClassLoader xiaoGangClassLoader = xiaoGang.getClass().getClassLoader();
// 动态构造一个代理律师
LitigationProcessInterface lawyer = (LitigationProcessInterface) Proxy.newProxyInstance(xiaoGangClassLoader, new Class[]{LitigationProcessInterface.class}, invocationHandler);
lawyer.submitLawsuit();
lawyer.proof();
lawyer.defend();
lawyer.finish();
}
}
- Log
13:50:04.564 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::submitLawsuit1 律师代替小刚诉讼申请前
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::submitLawsuit 小刚提交诉讼申请
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::submitLawsuit2 律师代替小刚诉讼申请后
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::proof1 律师在小刚举证内容前
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::proof 小刚举证
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::proof2 律师在小刚举证内容后
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::defend1 律师在小刚辩护内容前
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::defend 小刚辩护
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::defend2 律师在小刚辩护内容后
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::finish1 结束前
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::finish 结束
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::finish2 结束后
这种Log 是不是和第一种很像了,而它不在需要创建多个代理对象。
Cglib代理
上面两种代理,都是需要实现特定的接口,才能实现代理功能,或者增强功能。而Spring 提供的Cglib 包则简化了实现接口,只要是单独的对象就可以实现代理。
- 定义被代理对象
package com.example.pattern.proxy.enhancer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 小刚需要提供的内容
*/
public class XiaoGang {
private Logger logger = LoggerFactory.getLogger(this.getClass());
public void submitLawsuit() {
logger.info("----- XiaoGang::::::submitLawsuit 小刚提交诉讼申请");
}
public void proof() {
logger.info("----- XiaoGang::::::proof 小刚举证");
}
public void defend() {
logger.info("----- XiaoGang::::::defend 小刚辩护");
}
public void finish() {
logger.info("----- XiaoGang::::::finish 结束");
}
}
- 代理对象
package com.example.pattern.proxy.enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ProxyInterceptor implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("----- ProxyFactory:::::: intercept 开始 ");
//执行目标对象的方法
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("----- ProxyFactory:::::: intercept 结束 ");
return result;
}
}
- 实现
package com.example.pattern.proxy.enhancer;
import org.springframework.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
//继承被代理类
enhancer.setSuperclass(XiaoGang.class);
//设置回调
enhancer.setCallback(new ProxyInterceptor());
// enhancer.setCallback(new MethodInterceptor() {
// public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// // 可以做增强
// return null;
// }
// });
// enhancer.setCallbackFilter(new CallbackFilter() {
// public int accept(Method method) {
// return 0;
// }
// });
//设置代理类对象
XiaoGang xiaoGang = (XiaoGang) enhancer.create();
//在调用代理类中方法时会被我们实现的方法拦截器进行拦截
xiaoGang.submitLawsuit();
xiaoGang.proof();
xiaoGang.defend();
xiaoGang.finish();
}
}
- LOG
----- ProxyFactory:::::: intercept 开始
14:22:11.317 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::submitLawsuit 小刚提交诉讼申请
----- ProxyFactory:::::: intercept 结束
----- ProxyFactory:::::: intercept 开始
14:22:11.320 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::proof 小刚举证
----- ProxyFactory:::::: intercept 结束
----- ProxyFactory:::::: intercept 开始
14:22:11.320 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::defend 小刚辩护
----- ProxyFactory:::::: intercept 结束
----- ProxyFactory:::::: intercept 开始
14:22:11.320 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::finish 结束
----- ProxyFactory:::::: intercept 结束
该种代理,比起动态代理更加简单便捷。如果要想扩展方法,则直接new MethodInterceptor 就可以实现,类似于动态代理new InvocationHandler 。
总结
学会使用设计模式,可以让我们的代码写的更加好看漂亮。在我们的编程过程中,很多时候我们写的代码,就使用了设计模式,只不过是不清楚罢了。
经过查看博客和书籍,对代理模式有了初步的了解。如果写的不好,请广大网友指正。
demo地址:https://github.com/wdmxzf/java-example/tree/pattern