1 概述
在生活中,我们经常见到这样的场景,如:租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日志监听等,这些都是代理模式的实际体现。代理模式(Proxy
Pattern)的定义也非常简单,是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客服端和目标对象之间起到中介作用,代理模式属于结构型设计模式。使用
代理模式主要有两个目的:一保护目标对象,二增强目标对象。下面我们来看一下代理模式的类结构图:
Subject 是顶层接口,RealSubject 是真实对象(被代理对象),Proxy 是代理对象,代理对象持有被代理对象的引用,客户端调用代理对象方法,同时也调用被代理对象的方
法,但是在代理对象前后增加一些处理。在代码中,我们想到代理,就会理解为是代码增强,其实就是在原本逻辑前后增加一些逻辑,而调用者无感知。代理模式属于结构型
模式,有静态代理和动态代理。
2 静态代理
举个例子:人到了适婚年龄,父母总是迫不及待希望早点抱孙子。而现在社会的人在各种压力之下,都选择晚婚晚育。于是着急的父母就开始到处为自己的子女相亲,比子女
自己还着急。这个相亲的过程,就是一种我们人人都有份的代理。来看代码实现:
public interface Person {
void findLove();
}
public class Son implements Person {
@Override
public void findLove() {
System.out.println("儿子要求: 上海名媛");
}
}
public class Father implements Person {
private Son son;
public Father(Son son) {
this.son = son;
}
@Override
public void findLove() {
System.out.println("父母物色对象");
son.findLove();
System.out.println("双方同意交往,确立关系");
}
}
public class Test {
public static void main(String[] args) {
Father father=new Father(new Son());
father.findLove();
}
}
3 动态代理
动态代理和静态对比基本思路是一致的,只不过动态代理功能更加强大,随着业务的扩展适应性更强。如果还以找对象为例,使用动态代理相当于是能够适应复杂的业务场景。不仅仅只是父亲给儿子找对象,如果找对象这项业务发展成了一个产业,进而出现了媒婆、婚介所等这样的形式。那么,此时用静态代理成本就更大了,需要一个更加通用的解决方案,要满足任何单身人士找对象的需求。我们升级一下代码,先来看 JDK 实现方式:
3.1 JDK 实现方式
使用JDK的java.lang.reflect.Proxy类的newProxyInstance方法实现动态代理:
public interface Person {
void findLove();
}
public class Customer implements Person {
@Override
public void findLove() {
System.out.println("上海名媛");
}
}
public class JdkMeiPoInvocationHandler implements InvocationHandler {
private Object target;
public Object getInstance(Object target) {
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object obj = method.invoke(target, args);
after();
return obj;
}
private void before(){
System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
}
private void after(){
System.out.println("如果合适的话,就准备办事");
}
}
public class JdkTest {
public static void main(String[] args) {
Person person= (Person) new JdkMeiPoInvocationHandler().getInstance(new Customer());
person.findLove();
}
}
3.2 cglib
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
public class CglibMeipo implements MethodInterceptor {
public Object getInstance(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws
Throwable {
before();
Object obj = methodProxy.invokeSuper(o, objects);
after();
return obj;
}
private void before() {
System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
}
private void after() {
System.out.println("如果合适的话,就准备办事");
}
}
public class CglibCustomer {
public void findLove(){
System.out.println("上海名媛");
}
}
public class CglibTest {
public static void main(String[] args) {
CglibCustomer customer = (CglibCustomer) new CglibMeipo().getInstance(CglibCustomer.class);
customer.findLove();
}
}
4 静态代理和动态的区别
(1) 静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则。
(2) 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
(3) 若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。
5 代理模式的优缺点
使用代理模式具有以下几个优点:
(1) 代理模式能将代理对象与真实被调用的目标对象分离。
(2) 一定程度上降低了系统的耦合度,扩展性好。
(3) 可以起到保护目标对象的作用。
(4) 可以对目标对象的功能增强
缺点:
(1) 代理模式会造成系统设计中类的数量增加。
(2) 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
(3) 增加了系统的复杂度。