什么是代理模式
代理模式又称委托模式是指为其他对象提供一种代理以控制对这个对象的访问。通俗的讲代理模式就是通过一个代理类来替代真实业务类接受访问。访问者并不需要知道真实业务的具体实现逻辑,都是在代理类中进行。避免了访问者和真是业务的是非纠缠,提供高了扩展性。在代理模式概况可以分为普通代理,强制代理,动态代理三种。
普通代理:
代理模式有自己的通用模板,基本分为三部分:
- Subject抽象主题角色:可以是接口也可以是抽象类,定义最初的业务类型
- RealSubject具体主题角色:也是就被代理的角色,实现真是业务操作
- Proxy代理主题角色:就是委托类或者代理类,被访问者,负责具体实现哪个真实业务,以及包括在执行真实业务前后的预处理和善后的工作。
举例说明:
需求:小明比较懒不想出门,于是想起能不能让别人跑腿去买西瓜。
设计:主业务接口Customer,无论是真实消费者还是跑腿消费者都是消费者,所以ProxyCustomer和RealCustomer都会实现该接口。
代码示例:
/**
* 接口类
* 主要定义购物方法
*/
public interface Customer {
public void pay();
}
/**
* 真正的消费者
*/
public class RealCustomer implements Customer{
private String name;
public RealCustomer(String name){
this.name = name;
}
@Override
public void pay() {
System.out.println(name + " 买西瓜");
}
}
/**
* 代理类
* 跑腿者
*/
public class ProxyCustomer implements Customer{
private Customer reaCustomer = null;
//默认的代理者
public ProxyCustomer(){
reaCustomer = new ProxyCustomer();
}
public ProxyCustomer(String name){
this.reaCustomer = new RealCustomer(name);
}
@Override
public void pay() {
before();
reaCustomer.pay();
after();
}
private void before(){
System.out.println("支付买西瓜的钱");
}
private void after(){
System.out.println("支付跑腿费 3元");
}
}
//测试类
public class TestProxy {
public static void main(String[] args) {
ProxyCustomer proxy = new ProxyCustomer("小明");
proxy.pay();
}
}
执行结果:
支付买西瓜的钱
小明 买西瓜
支付跑腿费 3元
在上面的代码案例中,直接返回的是ProxyCustomer,但是真实业务实现买西瓜的是realCustomer,毕竟小明才是真正的消费者。并且代理类增加了预处理收到收到消费金,最后又在消费完成后收取了跑腿费。
强制代理:
故名思意,强制代理的意思的就是必须使用代理类,真实业务类禁止完成,那把上面的代码改造一下。
package com.proxy.demo2;
/**
* 真正的消费者
*/
public class RealCustomer implements Customer {
private Customer proxyCustomer = null;
private String name;
public RealCustomer(String name){
this.name = name;
}
@Override
public void pay() {
if(!this.isProxy()){
System.out.println(name + "让你的代理来");
}else{
System.out.println(name + " 买西瓜");
}
}
//获取代理类
public Customer getProxy(){
this.proxyCustomer = new ProxyCustomer(name);
return proxyCustomer;
}
//判断是否是代理类
private boolean isProxy(){
if(proxyCustomer == null){
return false;
}
return true;
}
}
//测试类
public class TestProxy {
public static void main(String[] args) {
RealCustomer customer = new RealCustomer("小明");
customer.pay();
}
}
//执行结果:
小明让你的代理来
RealCustomer新增了获取代理来判断是否是代理,如果是代理就正常执行,如果不是则需要获取代理,下面测试通过获取代理来执行程序:
//测试类
public class TestProxy {
public static void main(String[] args) {
RealCustomer customer = new RealCustomer("小明");
Customer proxy = customer.getProxy();
proxy.pay();
}
}
//执行结果:
支付买西瓜的钱
小明让你的代理来
支付跑腿费 3元
动态代理:
动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。相对来说,上面的普通代理和强制代理都指定了代理类所以属于静态代理。
实现一个简单的动态代理:
需要增加一个动态代理实现类CustomerHandler 实现了InvocationHandler。InvocationHandler是JDK提供的动态代理接口,对被代理类的方法进行代理。重写invoke方法,最终调用的是method.invoke方法来实现方法调用。
/**
* 动态代理实现类
*/
public class CustomerHandler implements InvocationHandler {
//被代理者
Class cla = null;
//被代理的实例
Object obj = null;
//选择要代理的对象
public CustomerHandler(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(this.obj, args);
return invoke;
}
}
//测试类
/**
* 测试类
*/
public class TestProxy {
public static void main(String[] args) {
//客户类
Customer customer = new RealCustomer("小明");
//自己实现方法调用
CustomerHandler handler = new CustomerHandler(customer);
//类加载器
ClassLoader classLoader = customer.getClass().getClassLoader();
//生成代理类
Customer proxyCustomer = (Customer) Proxy.newProxyInstance(classLoader, new Class[]{Customer.class}, handler);
proxyCustomer.pay();
}
}
//执行结果:
小明买西瓜
上面的例子中并没有显示的声明代理类,但是却完成了代理的工作,这就是简单实现动态代理。
当然动态代理并不是这么简单,必然应用最广的AOP编程思想其实原理就是利用动态代理。那如何实现切面编程呢?那把上面的例子进行改造,实现一个简单的切面编程。
如下代码是生成代理的关键方法,那是不是可以进一不进行封装,实现切入点和执行一个通知
//生成代理类
Customer proxyCustomer = (Customer) Proxy.newProxyInstance(classLoader, new Class[]{Customer.class}, handler);
封装一个动态代理类DynamicProxy,实现连接点判断和生成代理类。自定义CustomerDynamicProxy继承DynamicProxy,将消费类的代码进行封装。最后改造结果:
/**
* 动态代理类
*/
public class DynamicProxy<T> {
public static <T> T newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
//寻找连接点,在AOP框架中使用注解实现
if (true) {
//执行前置通知
new BeforeAdvice().exec();
}
return (T) Proxy.newProxyInstance(loader, new Class[]{Customer.class}, h);
}
}
/**
* 消费的动态代理类
*/
public class CustomerDynamicProxy extends DynamicProxy{
//获取代理类
public static <T> T newProxyInstance(Customer customer) {
//hander
CustomerHandler handler = new CustomerHandler(customer);
//获取接口数组
Class<?>[] interfaces = customer.getClass().getInterfaces();
//类加载器
ClassLoader classLoader = customer.getClass().getClassLoader();
return newProxyInstance(classLoader, interfaces, handler);
}
}
/**
* 测试类
*/
public class TestProxy {
public static void main(String[] args) {
//客户类
Customer customer = new RealCustomer("小明");
Customer proxyCustomer = CustomerDynamicProxy.newProxyInstance(customer);
proxyCustomer.pay();
}
}
//执行结果:
执行前置通知
小明买西瓜
经过上面的代理完善,简单实现了切面,动态代理实现更合理,扩展性更强。
总结:
代理模式实现的真正意图是在不改变原始类或者叫被代理类代码的情况下扩展附加功能。代理模式实现的形式可以分为两种代理类和被代理类实现相同的接口,还可以代理类直接继承被代理类。
代理模式的特点和优点:
- 职责清晰:真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰。
- 高扩展性:具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。
- 智能化:动态代理无需静态生成代理类,而且运用在AOP中,更加灵活多变。
适用场景:
在非功能场景下,像监控、鉴权等,需要与真正的业务功能解耦,这时候就可以适用代理模式,让业务开发更专注业务;当然在RPC调用时,Spring的AOP等开源框架中,也使用了代理模式。
参考《设计模式之禅》秦小波著