第4章:Java中代理模式的实现

2021/5/31 14:50:52

本文主要是介绍第4章:Java中代理模式的实现,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

       书接上文《第3章:JavaSpringAOP概念详解》https://qlygmwcx.blog.csdn.net/article/details/117279470 我们今天先来看看到底是什么样的需求促使代码的升级和演变,从而叙述下一篇博客中关于Spring中AOP如何使用。

       首先我们说一下代理模式,代理模式分为两种,一种是静态代理,一种是动态代理,通过Java中代理模式的基本实现,我们从此处接入到Spring框架的AOP中。

       两种代理从虚拟机加载类的角度来讲,本质上都是一样的,都是在原有类的行为基础上,加入一些多出的行为,甚至完全替换原有的行为。

       静态代理采用的方式就是我们手动的将这些行为换进去,然后让编译器帮我们编译,同时也就将字节码在原有类的基础上加入一些其他的东西或者替换原有的东西,产生一个新的与原有类接口相同却行为不同的类型。

1. 基础功能计算器

1.1 版本1,简单计算

    MyCalculator calc=new MyCalculator();
    System.out.println(calc.add(10,10));;

package calc;

public interface ICalculator {
    /**
     * 相加
     * @param i
     * @param j
     * @return
     */
    public Integer add(Integer i,Integer j);

    /**
     * 相减
     * @param i
     * @param j
     * @return
     */
    public Integer sub(Integer i,Integer j);

    /**
     * 相乘
     * @param i
     * @param j
     * @return
     */
    public Integer mul(Integer i,Integer j);

    /**
     * 相除
     * @param i
     * @param j
     * @return
     */
    public Integer div(Integer i,Integer j);
}

public class MyCalculator implements ICalculator {

    public MyCalculator() {
        System.out.println("MyCalculator初始化");
    }

    @Override
    public Integer add(Integer i, Integer j) {
        Integer result = i + j;
        return result;
    }

    @Override
    public Integer sub(Integer i, Integer j) {
        Integer result = i - j;
        return result;
    }

    @Override
    public Integer mul(Integer i, Integer j) {
        Integer result = i * j;
        return result;
    }

    @Override
    public Integer div(Integer i, Integer j) {
        Integer result = i / j;
        return result;
    }
}

在这里插入图片描述

1.2 对于所有计算我们需要加上日志信息,包括[计算开始前]、[计算开始后]、[计算异常信息]、[计算开始后的返回信息]

    MyCalculator calc=new MyCalculator();
    System.out.println(calc.add(10,10));
    System.out.println(calc.sub(10,10));
    System.out.println(calc.mul(10,10));
    System.out.println(calc.div(10,10));
    System.out.println(calc.div(10,0));


public class MyCalculator implements ICalculator {

    public MyCalculator() {
        System.out.println("MyCalculator初始化");
    }

    @Override
    public Integer add(Integer i, Integer j) {
        Method method = null;
        Integer result = 0;
        try {
            method = MyCalculator.class.getMethod("add", Integer.class, Integer.class);
            //Thread.currentThread().getStackTrace()[2].getClass().getEnclosingMethod()

            LogUtil.start(method, i, j);
            result = i + j;
        } catch (Exception e) {
            LogUtil.logException(method, e);
        } finally {
            LogUtil.logFinally(method);
        }

        LogUtil.stop(method, result);
        return result;
    }

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

        Method method = null;
        Integer result = 0;
        try {
            method = MyCalculator.class.getMethod("sub", Integer.class, Integer.class);
            //Thread.currentThread().getStackTrace()[2].getClass().getEnclosingMethod()

            LogUtil.start(method, i, j);
            result = i - j;
        } catch (Exception e) {
            LogUtil.logException(method, e);
        } finally {
            LogUtil.logFinally(method);
        }

        LogUtil.stop(method, result);
        return result;
    }

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

        Method method = null;
        Integer result = 0;
        try {
            method = MyCalculator.class.getMethod("mul", Integer.class, Integer.class);
            //Thread.currentThread().getStackTrace()[2].getClass().getEnclosingMethod()

            LogUtil.start(method, i, j);
            result = i * j;
        } catch (Exception e) {
            LogUtil.logException(method, e);
        } finally {
            LogUtil.logFinally(method);
        }

        LogUtil.stop(method, result);
        return result;
    }

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

        Method method = null;
        Integer result = 0;
        try {
            method = MyCalculator.class.getMethod("div", Integer.class, Integer.class);
            //Thread.currentThread().getStackTrace()[2].getClass().getEnclosingMethod()

            LogUtil.start(method, i, j);
            result = i / j;
        } catch (Exception e) {
            LogUtil.logException(method, e);
        } finally {
            LogUtil.logFinally(method);
        }

        LogUtil.stop(method, result);
        return result;
    }
}


public class LogUtil {

    /**
     * 函数开始执行信息
     * @param method
     * @param pars
     */
    public static void start(Method method, Object... pars){
        System.out.println(String.format("函数[%s]开始执行,参数为:%s",method.getName(), Arrays.asList(pars)));
    }

    /**
     * 函数结束执行信息
     * @param method
     * @param pars
     */
    public static void stop(Method method, Object objReturn){
        System.out.println(String.format("函数[%s]开始完毕,返回值为:%s",method.getName(),objReturn));
    }

    /**
     * 函数异常信息
     * @param method
     * @param pars
     */
    public static void logException(Method method,Exception e){
        System.out.println(String.format("函数[%s]执行异常,异常信息为:%s",method.getName(),e.getMessage()));
    }

    /**
     * 函数完毕信息
     * @param method
     * @param pars
     */
    public static void logFinally(Method method){
        System.out.println(String.format("函数[%s]开始完毕",method.getName()));
    }
}
MyCalculator初始化
函数[add]开始执行,参数为:[10, 10]
函数[add]开始完毕
函数[add]开始完毕,返回值为:20
20
函数[sub]开始执行,参数为:[10, 10]
函数[sub]开始完毕
函数[sub]开始完毕,返回值为:0
0
函数[mul]开始执行,参数为:[10, 10]
函数[mul]开始完毕
函数[mul]开始完毕,返回值为:100
100
函数[div]开始执行,参数为:[10, 10]
函数[div]开始完毕
函数[div]开始完毕,返回值为:1
1
函数[div]开始执行,参数为:[10, 0]
函数[div]执行异常,异常信息为:/ by zero
函数[div]开始完毕
函数[div]开始完毕,返回值为:0
0

1.3 静态代理模式

    MyCalculator calc=new MyCalculator();
    System.out.println(calc.add(10,10));
    System.out.println(calc.sub(10,10));
    System.out.println(calc.mul(10,10));
    System.out.println(calc.div(10,10));
    try{
        System.out.println(calc.div(10,0));
    }
    catch (Exception e)
    {
        System.out.println(e.getMessage());
    }

    MyCalculatorStaticProxy staticProxy=newMyCalculatorStaticProxy(calc);
    System.out.println(staticProxy.add(10,10));
    System.out.println(staticProxy.sub(10,10));
    System.out.println(staticProxy.mul(10,10));
    System.out.println(staticProxy.div(10,10));
    System.out.println(staticProxy.div(10,0));

public class MyCalculator implements ICalculator {
    public MyCalculator() {
        System.out.println("MyCalculator初始化");
    }

    @Override
    public Integer add(Integer i, Integer j) {
        return i + j;
    }

    @Override
    public Integer sub(Integer i, Integer j) {
        return i - j;
    }

    @Override
    public Integer mul(Integer i, Integer j) {
        return i * j;
    }

    @Override
    public Integer div(Integer i, Integer j) {
        return i / j;
    }
}

package calc;

import java.lang.reflect.Method;

/**
 * @Classname MyCalculatorStaticProxy
 * @Description MyCalculator静态代理模式
 * @Date 2021/5/31 13:00
 * @Created by xiaocai
 */
public class MyCalculatorStaticProxy implements ICalculator {

    MyCalculator myCalculator=null;

    public MyCalculatorStaticProxy(MyCalculator myCalculator)
    {
        this.myCalculator=myCalculator;
    }

    @Override
    public Integer add(Integer i, Integer j) {
        Method method = null;
        Integer result = 0;
        try {
            method = MyCalculator.class.getMethod("add", Integer.class, Integer.class);
            //Thread.currentThread().getStackTrace()[2].getClass().getEnclosingMethod()

            LogUtil.start(method, i, j);
            result = myCalculator.add(i,j);
        } catch (Exception e) {
            LogUtil.logException(method, e);
        } finally {
            LogUtil.logFinally(method);
        }

        LogUtil.stop(method, result);
        return result;
    }

    @Override
    public Integer sub(Integer i, Integer j) {
        Method method = null;
        Integer result = 0;
        try {
            method = MyCalculator.class.getMethod("sub", Integer.class, Integer.class);
            //Thread.currentThread().getStackTrace()[2].getClass().getEnclosingMethod()

            LogUtil.start(method, i, j);
            result = myCalculator.sub(i,j);
        } catch (Exception e) {
            LogUtil.logException(method, e);
        } finally {
            LogUtil.logFinally(method);
        }

        LogUtil.stop(method, result);
        return result;
    }

    @Override
    public Integer mul(Integer i, Integer j) {
        Method method = null;
        Integer result = 0;
        try {
            method = MyCalculator.class.getMethod("mul", Integer.class, Integer.class);
            //Thread.currentThread().getStackTrace()[2].getClass().getEnclosingMethod()

            LogUtil.start(method, i, j);
            result = myCalculator.sub(i,j);
        } catch (Exception e) {
            LogUtil.logException(method, e);
        } finally {
            LogUtil.logFinally(method);
        }

        LogUtil.stop(method, result);
        return result;
    }

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

        Method method = null;
        Integer result = 0;
        try {
            method = MyCalculator.class.getMethod("div", Integer.class, Integer.class);
            //Thread.currentThread().getStackTrace()[2].getClass().getEnclosingMethod()

            LogUtil.start(method, i, j);
            result = myCalculator.div(i,j);
        } catch (Exception e) {
            LogUtil.logException(method, e);
        } finally {
            LogUtil.logFinally(method);
        }

        LogUtil.stop(method, result);
        return result;
    }
}

MyCalculator初始化
20
0
100
1
/ by zero
函数[add]开始执行,参数为:[10, 10]
函数[add]开始完毕
函数[add]开始完毕,返回值为:20
20
函数[sub]开始执行,参数为:[10, 10]
函数[sub]开始完毕
函数[sub]开始完毕,返回值为:0
0
函数[mul]开始执行,参数为:[10, 10]
函数[mul]开始完毕
函数[mul]开始完毕,返回值为:0
0
函数[div]开始执行,参数为:[10, 10]
函数[div]开始完毕
函数[div]开始完毕,返回值为:1
1
函数[div]开始执行,参数为:[10, 0]
函数[div]执行异常,异常信息为:/ by zero
函数[div]开始完毕
函数[div]开始完毕,返回值为:0
0

静态代理模式:

  1. 可以做到在不修改目标对象的功能前提下,对目标功能扩展.
  2. 缺点:

       因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
如何解决静态代理中的缺点呢?答案是可以使用动态代理方式

1.4 动态代理模式

    public static void main(String[] args) {
        dynamicProxy();
    }

    private static void dynamicProxy() {
        MyCalculator calc=new MyCalculator();
        ICalculator dynamicProxyCalc= CalculatorProxy.getCalculator(calc);
        System.out.println(dynamicProxyCalc.add(10,10));
        System.out.println(dynamicProxyCalc.sub(10,10));
        System.out.println(dynamicProxyCalc.mul(10,10));
        System.out.println(dynamicProxyCalc.div(10,10));
        System.out.println(dynamicProxyCalc.div(10,0));
    }


public class MyCalculator implements ICalculator {

    public MyCalculator() {
        System.out.println("MyCalculator初始化");
    }

    @Override
    public Integer add(Integer i, Integer j) {
        return i + j;
    }

    @Override
    public Integer sub(Integer i, Integer j) {
        return i - j;
    }

    @Override
    public Integer mul(Integer i, Integer j) {
        return i * j;
    }

    @Override
    public Integer div(Integer i, Integer j) {
        return i / j;
    }
}

package calc;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Classname CalculatorProxy
 * @Description Calculator动态代理模式
 * @Date 2021/5/31 13:51
 * @Created by xiaocai
 */
public class CalculatorProxy {

    public static ICalculator getCalculator(final ICalculator calculator){
        //获取被代理对象的类加载器
        ClassLoader loader =calculator.getClass().getClassLoader();

        //被代理对象的所有接口
        Class<?>[] interfaces=calculator.getClass().getInterfaces();

        //用来执行被代理类所需要执行的方法
        InvocationHandler hander=new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = 0;
                try {
                    LogUtil.start(method, args);
                    result=method.invoke(args);
                } catch (Exception e) {
                    LogUtil.logException(method, e);
                } finally {
                    LogUtil.logFinally(method);
                }

                LogUtil.stop(method, result);
                return result;
            }
        };

        Object objReturn= Proxy.newProxyInstance(loader,interfaces,hander);

        return (ICalculator)objReturn;
    }
}
MyCalculator初始化
函数[add]开始执行,参数为:[10, 10]
函数[add]执行异常,异常信息为:object is not an instance of declaring class
函数[add]开始完毕
函数[add]开始完毕,返回值为:0
0
函数[sub]开始执行,参数为:[10, 10]
函数[sub]执行异常,异常信息为:object is not an instance of declaring class
函数[sub]开始完毕
函数[sub]开始完毕,返回值为:0
0
函数[mul]开始执行,参数为:[10, 10]
函数[mul]执行异常,异常信息为:object is not an instance of declaring class
函数[mul]开始完毕
函数[mul]开始完毕,返回值为:0
0
函数[div]开始执行,参数为:[10, 10]
函数[div]执行异常,异常信息为:object is not an instance of declaring class
函数[div]开始完毕
函数[div]开始完毕,返回值为:0
0
函数[div]开始执行,参数为:[10, 0]
函数[div]执行异常,异常信息为:object is not an instance of declaring class
函数[div]开始完毕
函数[div]开始完毕,返回值为:0
0

Process finished with exit code 0

2. 代理模式解释

我们使用代理模式解决了上述问题,从静态代理的使用上来看,我们一般是这么做的。

  1. 代理类一般要持有一个被代理的对象的引用。

  2. 对于我们不关心的方法,全部委托给被代理的对象处理。

  3. 自己处理我们关心的方法。

这种代理是死的,不会在运行时动态创建,因为我们相当于在编译期,也就是你按下CTRL+S的那一刻,就给被代理的对象生成了一个不可动态改变的代理类。

基于Java的Proxy实现的动态代理有一个强制性要求,就是被代理的类必须实现了某一个接口,或者本身就是接口,就像我们的Connection。

  • ASM:https://zhuanlan.zhihu.com/p/94498015?utm_source=wechat_timeline ASM是Java字节码操控框架,她能呗用来动态生成类或者增强既有类的功能。ASM可以直接产生二进制class文件,也可以在类加载入Java虚拟机之前动态改变类行为。Java class被存储在严格格式定义的class文件里。

  • Proxy必须要接口,如果没有接口不能使用,这种方式用JDK提供的reflect包下的类,但是生产环境中我们无法保证每一个类都有实现的接口。

  • CGLib则没有Proxy的约束条件,即不要求类必须要实现接口。

  • Spring中存在逻辑就是说,当类总存在继承时直接使用Proxy,如果没有继承则使用CGLib来实现。

这么做的。

  1. 代理类一般要持有一个被代理的对象的引用。

  2. 对于我们不关心的方法,全部委托给被代理的对象处理。

  3. 自己处理我们关心的方法。

这种代理是死的,不会在运行时动态创建,因为我们相当于在编译期,也就是你按下CTRL+S的那一刻,就给被代理的对象生成了一个不可动态改变的代理类。

基于Java的Proxy实现的动态代理有一个强制性要求,就是被代理的类必须实现了某一个接口,或者本身就是接口,就像我们的Connection。

  • ASM:https://zhuanlan.zhihu.com/p/94498015?utm_source=wechat_timeline ASM是Java字节码操控框架,她能呗用来动态生成类或者增强既有类的功能。ASM可以直接产生二进制class文件,也可以在类加载入Java虚拟机之前动态改变类行为。Java class被存储在严格格式定义的class文件里。

  • Proxy必须要接口,如果没有接口不能使用,这种方式用JDK提供的reflect包下的类,但是生产环境中我们无法保证每一个类都有实现的接口。

  • CGLib则没有Proxy的约束条件,即不要求类必须要实现接口。

  • Spring中存在逻辑就是说,当类总存在继承时直接使用Proxy,如果没有继承则使用CGLib来实现。

  • 在早期CGLib的效率高于Proxy,而随着JDK的不断升级效率差距慢慢变小了。



这篇关于第4章:Java中代理模式的实现的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程