入门Java代理模式

代理模式

概述:

二十三种设计模式种一种,属于结构模式。他的作用是提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法的核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起,有益于统一维护。

在我们写代码的过程中,因为面试对象语言是纵向继承机制,所以我们封装代码只能封装一段连续的代码。给核心功能前后添加的辅助代码是没有办法封装的。如何能够封装这些在核心功能前后执行的辅助代码呢?这时就需要代理类了。我们通过代理类来帮我们执行目标对象的方法。我们调用时需要调用代理类来执行功能,在代理类中添加辅助功能的代码。辅助功能的代码帮我们调用目标对象对应的方法。

需求:

现在我们有一个计算器。我们想在计算器计算之前打印方法参数日志,在计算完成之后打印结果日志。计算器计算是我们的核心功能,日志打印是我们的辅助功能。但是我们没有办法将日志功能提出来封装,因为它不是一段连续的代码。

//未使用代理前的逻辑代码
    @Override
    public int add(int i, int j) {
        System.out.println("日志 方法: 入参 i , j" + i + " , " + j); //不属于核心代码
        int result = i + j;
        System.out.println("内部计算出来的结果:" + result);
        System.out.println("日志 方法: 结果" + result);//不属于核心代码
        return result;
    }

准备工作:

计算器接口

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 CalculatorImpl implements Calculator {
    @Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("内部计算出来的结果:" + result);
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("内部计算出来的结果:" + result);
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result = i * j;
        System.out.println("内部计算出来的结果:" + result);
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
        System.out.println("内部计算出来的结果:" + result);
        return result;
    }
}

静态代理:

思路:

​ 使用静态代理时,代理类需要调用和目标类一样的方法,所以代理类需要继承和目标相同的接口。

​ 代理对象需要帮助我们调用目标对象的方法。所以代理类种肯定要有一个目标类的实列变量才能调用。

代理类实现:

/**
* 实现和目标实列相同的接口
**/
public class CalculatorProxy implements Calculator{

    // TODO 目标实列
    private CalculatorImpl target;
    
    public CalculatorProxy(CalculatorImpl target) {
        this.target = target;
    }
  
    @Override
    public int add(int i, int j) {
         System.out.println("日志 add方法: 入参 i " + i + " ,  j " + j);
         int result = target.add(i, j);
         System.out.println("日志 add方法: 结果" + result);
         return result;
    }

    @Override
    public int sub(int i, int j) {
         System.out.println("日志 sub方法: 入参 i , j" + i + " , " + j);
         int result = target.sub(i, j);
         System.out.println("日志 sub方法: 结果" + result);
         return result;
    }

    @Override
    public int mul(int i, int j) {
        return 0;
    }

    @Override
    public int div(int i, int j) {
        return 0;
    }
}

测试使用静态代理

public class ProxyTest {

    @Test
    public void test01(){
        CalculatorProxy calculatorProxy = new CalculatorProxy(new CalculatorImpl());
        calculatorProxy.add(2, 1);
        System.out.println("-------");
        calculatorProxy.sub(2, 1);
    }

}

控制台输出:
日志 add方法: 入参 i 2 ,  j 1
内部计算出来的结果:3
日志 add方法: 结果3
-------
日志 sub方法: 入参 i , j2 , 1
内部计算出来的结果:1
日志 sub方法: 结果1

经过上述的代码,我们就知道了静态代理的工作流程。我们需要一个和目标类实现相同接口的代理对象,调用代理方法时,在代理方法内部调用目标方法,从而实现目标方法和辅助方法的解耦。

动态代理(jdk动态代理)

分析静态代理存在的问题,每当有一个目标类就需要一个代理类,导致代码变得冗余了。

所以我们就需要动态代理。

思路:所谓动态是指代理类动态了,我们可以根据目标类的类型动态的创建符合目标类型的代理类。所以目标类型应该是Object类型,同时使用工厂模式获取代理对象是比较合理的设计。

JDK的反射包种有一个Proxy的代理类,我们使用Proxy.newProxyInstance()创建代理实列。

newProxyInstance():创建一个代理实例

* 其中有三个参数:

* 1、classLoader:加载动态生成的代理类的类加载器

* 2、interfaces:目标对象实现的所有接口的class对象所组成的数组

* 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法

动态代理类实现:

public class ProxyFactory {

//我们要加载的目标类肯定是一个自定义的类,而我们这个代理工厂也是一个自定义的类,所以使用同一个类加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        //获取目标类实现的所有接口
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //设置代理对象实现目标方法的过程
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                Object result = null;
                try {
                    System.out.println("[动态代理][日志] " + method.getName() + ",参 数:" + Arrays.toString(args));
                    result = method.invoke(target, args);
                    System.out.println("[动态代理][日志] " + method.getName() + ",结 果:" + result);
                } catch (Exception e) {
                    System.out.println("[动态代理][日志] "+method.getName()+",异 常:"+e.getMessage());
                    e.printStackTrace();
                }finally {
                    System.out.println("[动态代理][日志] "+method.getName()+",方法 执行完毕");
                }
                return result;
            }
        };
        return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}

测试:

    @Test
    public void test02() {
        ProxyFactory2 proxyFactory2 = new ProxyFactory2(new CalculatorImpl());
        //这里返回的是要给Object,但是Object是不知道我们具体要调用的方法的,所以肯定要类型转换,转换成目标类的接口最合适
        Calculator proxy = (Calculator) proxyFactory2.getProxy();
        proxy.add(1, 3);
    }

//控制台输出:
[动态代理][日志] add,参 数:[1, 3]
内部计算出来的结果:4
[动态代理][日志] add,结 果:4
[动态代理][日志] add,方法 执行完毕

还有一种代理叫做CGlib动态代理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值