设计模式:代理模式

  1. 引入问题
  2. 代理模式
  3. 静态代理
    1. 创建静态代理对象
    2. 实现类不需要再打印日志了
    3. 测试
    4. 运行逻辑
    5. 问题
  4. 动态代理
    1. 生产代理对象的工厂类
    2. 测试
    3. 运行逻辑

引入问题

需要写一个计算器的接口

public interface Calculator {
    
    int add(int i, int j);
    
    int sub(int i, int j);
    
    int mul(int i, int j);
    
    int div(int i, int j);
    
}

其实现类不仅要实现相关方法,还需要打印出相关日志

public class CalculatorLogImpl implements Calculator {
    
    @Override
    public int add(int i, int j) {
    
        System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
    
        int result = i + j;
    
        System.out.println("方法内部 result = " + result);
    
        System.out.println("[日志] add 方法结束了,结果是:" + result);
    
        return result;
    }
    
    @Override
    public int sub(int i, int j) {
    
        System.out.println("[日志] sub 方法开始了,参数是:" + i + "," + j);
    
        int result = i - j;
    
        System.out.println("方法内部 result = " + result);
    
        System.out.println("[日志] sub 方法结束了,结果是:" + result);
    
        return result;
    }
    
    @Override
    public int mul(int i, int j) {
    
        System.out.println("[日志] mul 方法开始了,参数是:" + i + "," + j);
    
        int result = i * j;
    
        System.out.println("方法内部 result = " + result);
    
        System.out.println("[日志] mul 方法结束了,结果是:" + result);
    
        return result;
    }
    
    @Override
    public int div(int i, int j) {
    
        System.out.println("[日志] div 方法开始了,参数是:" + i + "," + j);
    
        int result = i / j;
    
        System.out.println("方法内部 result = " + result);
    
        System.out.println("[日志] div 方法结束了,结果是:" + result);
    
        return result;
    }
}

针对带日志功能的实现类,我们发现有如下缺陷:

  • 对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力
  • 附加功能分散在各个业务功能方法中,不利于统一维护

解决思路:

  • 解决这两个问题,核心就是:解耦。我们需要把附加功能从业务功能代码中抽取出来。

代理模式

代理模式的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦

使用代理前:直接调用目标方法

使用代理后:调用目标方法钱需要进过代理,让代理去使用目标方法

静态代理

创建静态代理对象

public class CalculatorStaticProxy implements Calculator{

    //被代理目标对象传递过来
    private Calculator calculator;
    public CalculatorStaticProxy(Calculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public int add(int i, int j) {
        //输出日志
        System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);

        //调用目标对象的方法实现核心业务
        int addResult = calculator.add(i, j);

        System.out.println("[日志] add 方法结束了,结果是:" + addResult);
        return addResult;
    }

    @Override
    public int sub(int i, int j) {
        //输出日志
        System.out.println("[日志] sub 方法开始了,参数是:" + i + "," + j);

        //调用目标对象的方法实现核心业务
        int addResult = calculator.sub(i, j);

        System.out.println("[日志] sub 方法结束了,结果是:" + addResult);
        return addResult;
    }

    @Override
    public int mul(int i, int j) {
        //输出日志
        System.out.println("[日志] mul 方法开始了,参数是:" + i + "," + j);

        //调用目标对象的方法实现核心业务
        int addResult = calculator.mul(i, j);

        System.out.println("[日志] mul 方法结束了,结果是:" + addResult);
        return addResult;
    }

    @Override
    public int div(int i, int j) {
        //输出日志
        System.out.println("[日志] div 方法开始了,参数是:" + i + "," + j);

        //调用目标对象的方法实现核心业务
        int addResult = calculator.div(i, j);

        System.out.println("[日志] div 方法结束了,结果是:" + addResult);
        return addResult;
    }
}

实现类不需要再打印日志了

public class CalculatorImpl implements Calculator{

    @Override
    public int add(int i, int j) {

        int result = i + j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int sub(int i, int j) {

        int result = i - j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int mul(int i, int j) {

        int result = i * j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int div(int i, int j) {

        int result = i / j;

        System.out.println("方法内部 result = " + result);

        return result;
    }
}

测试

@Test
public void test1(){
    CalculatorStaticProxy staticProxy = new CalculatorStaticProxy(new CalculatorImpl());
    staticProxy.add(2,4);
}

日志:

[日志] add 方法开始了,参数是:2,4
方法内部 result = 6
[日志] add 方法结束了,结果是:6

运行逻辑

问题

静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理

提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。

动态代理

生产代理对象的工厂类

public class ProxyFactory {

    //目标对象
    private Object target;
    public ProxyFactory(Object target) {
        this.target = target;
    }

    //返回代理对象
    public Object getProxy() {
        /**
         * Proxy.newProxyInstance()方法
         * 有三个参数
         * 第一个参数:ClassLoader: 加载动态生成代理类的来加载器
         * 第二个参数: Class[] interfaces:目录对象实现的所有接口的class类型数组
         * 第三个参数:InvocationHandler:设置代理对象实现目标对象方法的过程
         */
        //第一个参数:ClassLoader: 加载动态生成代理类的来加载器
        ClassLoader classLoader = target.getClass().getClassLoader();
        //第二个参数: Class[] interfaces:目录对象实现的所有接口的class类型数组
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //第三个参数:InvocationHandler:设置代理对象实现目标对象方法的过程
        InvocationHandler invocationHandler =new InvocationHandler() {

            //第一个参数:代理对象
            //第二个参数:需要重写目标对象的方法
            //第三个参数:method方法里面参数
            @Override
            public Object invoke(Object proxy,
                                 Method method,
                                 Object[] args) throws Throwable {

                //方法调用之前输出
                System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));

                //调用目标的方法
                Object result = method.invoke(target, args);

                //方法调用之后输出
                System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);
                return result;
            }
        };
        return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
    }
}

测试

public static void main(String[] args) {
    //创建代理对象(动态)
    ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());
    Calculator proxy = (Calculator)proxyFactory.getProxy();
    //proxy.add(1,2);
    proxy.mul(2,4);
}

日志:

[动态代理][日志] mul,参数:[2, 4]
方法内部 result = 8
[动态代理][日志] mul,结果:8

运行逻辑


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1909773034@qq.com

×

喜欢就点赞,疼爱就打赏