代理设计模式

一、概念

代理模式Proxy,就是在访问原始的对象的时候,引入一定的简介手段,附加更多的功能,这样保证不影响原始代理核心功能的基础上可以增加额外功能(AOP思想)。


/**
 * 定义一个政策接口
 */
public interface IPolicy {

    /**
     * 执行政策方法
     */
    void executePolicy();
}

/**
 * 实现政策接口,进行业务逻辑处理
 */
public class Policy implements IPolicy {
    @Override
    public void executePolicy() {
        System.out.println("执行政策!");
    }
}

根据需求,我们在政策执行之前需要进行校验,那么我们一般的做法就是继承Policy,重写父类方法

**
 * 继承的方式,添加普通类型政策的校验逻辑
 */
public class CommonPolicy extends Policy {

    private String policyId;

    /*省略get,set方法*/
    
    @Override
    public void executePolicy() {
        System.out.println("校验普通政策ID, 政策id = " + policyId);
        System.out.println("执行政策!");
    }
}

这样做其实是违背了“开闭原则”,虽然拓展,但对修改开放了,为了解决这个问题,我们可以采取代理模式,主要分为静态代理和动态代理

二、静态代理

代理者和原始对象继承或者遵从同一个接口,但是原始对象中值操作核心功能,而代理者则将附加功能写入,并在类内新建原始对象,然后调用其核心方法,这样就在核心功能继承上附加了额外功能,外部只需要调取代理。

在这里插入图片描述

/**
 * 改进:与Policy一样遵从同一套接口,同时将Policy实现类传入,调用其方法
 * 这样可以保证对原方法的修改关闭,而增加了拓展(切面增强功能,开闭原则)
 * JDK代理方式
 */
public class SpecialPolicy implements IPolicy {
    private String policyId;
    private Policy policy;

    /*get\set*/

    @Override
    public void executePolicy() {
        System.out.println("校验特殊政策ID, 政策id = " + policyId);
        policy.executePolicy();
    }
}

当然,我们还可以使用继承

/**
 * 改进:也可以使用继承来实现同样的功能(CgLib代理方式)
 */
public class SpecialPolicy2 extends Policy {
    private String policyId;
    private Policy policy;

    /*get\set*/

    @Override
    public void executePolicy() {
        System.out.println("校验特殊政策ID, 政策id = " + policyId);
        policy.executePolicy();
    }
}

但是这种方式还有个问题,如果政策量很大,那岂不是我的每个政策都要加一个“执行政策!”这个业务逻辑?!
有没有什么办法能自动生成代理对象,这就是动态代理思想


三、动态代理

本质就是利用Java反射技术将零散的目标类各个属性重组,并改写其中的方法,返回一个与原始类属性一模一样的代理类。

public class SomePolicy implements IPolicy {
    private String policyId;
    
	/*get/set*/
	
    public void executePolicy() {
        System.out.println("政策校验,policyId = " + policyId);
    }
}

/*使用JDK反射包中的代理接口*/
public class ProxyHandler implements InvocationHandler {
	
	//目标:被代理类
    private Object target;

    public Object getProxy(Object target) {
        this.target = target;

        /**
         * 将目标对象传入,反射获取目标对象的类加载器、所有接口
         * this=当前对象,该对象实现了InvocationHandler接口所以有invoke方法,通过invoke方法可以调用被代理对象的方法
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    /**
     * 代理类执行的方法
     * @param proxy “被”代理的对象(原始对象)
     * @param method 需要执行的方法
     * @param args 方法执行的参数
     * @return
     * @throws Throwable 异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;

        //AOP-before:业务增强
        result = method.invoke(target, args);
        //AOP-after:业务增强

        System.out.println("执行政策!");
        return result;
    }
}

我们测试一下:

@Test
public void testProxyHandler() {
    ProxyHandler proxyHandler = new ProxyHandler();
    SomePolicy somePolicy = new SomePolicy();
    somePolicy.setPolicyId(UUID.randomUUID().toString());

	//必须强转为接口对象,即后面somePolicy实现类的父对象
    IPolicy iPolicy = (IPolicy) proxyHandler.getProxy(somePolicy);
    iPolicy.executePolicy();
}

//打印结果
政策校验,policyId = 38c90df8-4dde-44ba-bacb-6990fc34bb05
执行政策!

这里要注意:
1、当调用“executePolicy()”方法的时候,其实调用的不是真正的方法,而是将方法名和参数传入到了InvocationHandlerinvoke()方法,进行代理增强
2、进行对象强转的时候,必须转为接口对象,而不能是实现对象,否则会抛出不能同级转换的异常
java.lang.ClassCastException: com.sun.proxy.$Proxy6 cannot be cast to com.flight.carryprice.rjmodel.impl.SomePolicy

三、代理模式应用

1、业务系统非功能性需求开发

业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类中统一处理,让程序员只需要关注业务方面的开发。

2、缓存应用

比如,针对获取用户个人信息的需求,我们可以开发两个接口,一个支持缓存,一个支持实时查询。如果是基于Spring 框架来开发的话,那就可以在 AOP 切面中完成接口缓存的功能。在应用启动的时候,我们从配置文件中加载需要支持缓存的接口,以及相应的缓存策略(比如过期时间)等。当请求到来的时候,我们在 AOP 切面中拦截请求,如果请求中带有支持缓存的字段,我们便从缓存(内存缓存或者 Redis 缓存等)中获取数据直接返回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值