1.介绍
代理模式是23种模式中的一种,属于结构型设计模式。这种模式的作用就是要创建一个中间对象(相当于中介或者代理对象),通过操作中间对象来间接调用目的对象的方法,字段等,真正做到0接触,而且还可以提供额外的服务(比如增加日志,修改请求中的参数等等)Spring框架中的AOP就使用了代理模式,这是符合OCP开闭原则的:在尽量不改动原有代码的基础上,增加新的功能,使得代码的扩展性更强。
代理模式在代码实现上有2种方式:
静态代理
动态代理
2.静态代理
2.1介绍
代理模式就是对代理思想的一种设计模式实现。代理模式是一种比较好的理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。 代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。
2.2实现
以买火车票的例子来举例
接口
public interface Subject {
void request();
}
接口实现类
public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("火车站官方买票");
}
}
接口代理实现类
//代理类
public class Proxy implements Subject{
private RealSubject realSubject;
@Override
public void request() {
if(realSubject == null){
realSubject = new RealSubject();
}
preRequest();
realSubject.request();
postRequest();
}
void preRequest(){
System.out.println("买票前向高铁管家发送买票请求");
}
void postRequest(){
System.out.println("买完票高铁管家返回买票请求");
}
}
调用测试
public class testProxy {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.request();
// RealSubject realSubject = new RealSubject();
// realSubject.request();
}
}
返回结果
买票前向高铁管家发送买票请求
火车站官方买票
买完票高铁管家返回买票请求
Process finished with exit code 0
2.3优缺点
- 优点
业务类只需要关注业务逻辑本身,保证了业务类的重用性。
客户端只需要知道代理,无需关注具体实现。
高铁管家只要有大量的票满足客户需求 ,而我只需要买到票就行,不需要知道这个票怎么来的
- 缺点
由于代理类和被代理类都实现了主题接口,它们都有相同的方法,导致大量代码重复。同时如果主题接口新增了一个方法,那么代理类与被代理类也都需要实现这个方法,增加了维护代码的复杂度。
如果代理类要为其他真实角色提供委托服务的话,就需要实现其他的接口,当规模变大时也会增加代码复杂度。
如果高铁管家不仅提供卖票服务,还提供打游戏、卖房子、卖电影票、卖彩票、陪聊天、陪玩游戏等等一系列服务,那么他将变得无比庞杂,没有人敢轻易动他(指代码)修改会很麻烦
其实相当于直接调用这段代码会显得更方便
RealSubject realSubject = new RealSubject();
realSubject.request();
3.动态代理
动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( CGLIB 动态代理机制)。 从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。 说到动态代理,Spring AOP、RPC 框架应该是两个不得不的提的,它们的实现都依赖了动态代理。 动态代理在我们日常开发中使用的相对较小,但是在框架中的几乎是必用的一门技术。学会了动态代理之后,对于我们理解和学习各种框架的原理也非常有帮助。 就 Java 来说,动态代理的实现方式有很多种,比如 JDK 动态代理、CGLIB 动态代理等等
3.1动态代理实现
在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心。 要实现动态代理的话,还必须需要实现InvocationHandler 来自定义处理逻辑。 当我们的动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。
public class MhHandler implements InvocationHandler {
private Object object;
//参数是,准备被代理的对象
public MhHandler(Object object){
this.object=object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName=method.getName();
System.out.println("您好,你正在调用的方法是"+methodName);
method.invoke(object,args);//调用被代理对象的方法
System.out.println("再见,调用方法"+methodName+"完毕");
return null;
}
}
invoke() 方法有下面三个参数:
- proxy :动态生成的代理类
- method : 与代理类对象调用的方法相对应
- args : 当前 method 方法的参数
public static void main(String[] args) {
// Proxy proxy = new Proxy();
// proxy.request();
// RealSubject realSubject = new RealSubject();
// realSubject.request();
Proxy1 proxy = new Proxy1();
InvocationHandler ih=new MhHandler(proxy);
//类加载器,代理类,对象
Subject us=(Subject) Proxy.newProxyInstance(
proxy.getClass().getClassLoader(),//loader :类加载器,用于加载代理对象。
proxy.getClass().getInterfaces(),//interfaces : 被代理类实现的一些接口;
ih);//h : 实现了 InvocationHandler 接口的对象;
us.request();
}
运行结果
您好,你正在调用的方法是request
买票前向高铁管家发送买票请求
火车站官方买票
买完票高铁管家返回买票请求
再见,调用方法request完毕
Process finished with exit code 0
也就是说:通过Proxy 类的 newProxyInstance() 创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler 接口的类的 invoke()方法。 可以在 invoke() 方法中自定义处理逻辑,比如在方法执行前后做什么事情 扩展了原接口实现类的功能。