代理模式详解

1.代理模式的作用

能通过代理对象间接实现对目标对象的访问,在不改变源代码的情况下对目标对象的方法进行增强。

什么是通过代理对象间接实现对目标对象的访问?

举个生活中的例子:例如你买车是通过4s店(代理对象),而不是直接去车工厂(目标对象),你只需访问4s店。

那什么不改变源代码的情况下对目标对象的方法进行增强?

还是4s店和车工厂的例子:

你去4s店买车,4s店除了卖车给你,它还有一系列的服务,例如:推荐保险,上车牌等等;

但是你去工厂就只能买车,没有其他的附加服务;也就是说4s店(代理对象)在车工厂(目标对象)的卖车基础上进行了一系列增强,但是车厂还是一样的卖车,并没有被改变。

2.代理模式的分类

代理模式分为静态代理和动态代理。

2.1静态代理

2.1.1静态代理的实现

实现步骤

1.代理类与目标类实现同一接口

2.代理对象有目标对象的引用,在同名方法中调用目标对象的方法,并在前后根据需求进行增强。

实现代码

接口代码

java 复制代码
public interface AInterface {
    void say();
}

目标类代码

java 复制代码
public class AInterfaceImpl implements AInterface{
    @Override
    public void rap() {
        System.out.println("rap");
    }

    @Override
    public void basketball() {
        System.out.println("篮球");
    }
}

代理类代码

java 复制代码
public class AStaticProxy implements AInterface{

    //目标对象
    private AInterface aInterface;

    //注入目标对象
    public AStaticProxy(AInterface aInterface) {
        this.aInterface = aInterface;
    }
    @Override
    public void rap() {
        System.out.println("前置增强");
        aInterface.rap();
        System.out.println("后置增强");
    }

    @Override
    public void basketball() {
        System.out.println("前置增强");
        aInterface.basketball();
        System.out.println("后置增强");
    }
}

2.2.1静态代理的缺点

1.只能代理某一类接口

2.若进行多次增强,可能代理类暴增

3.修改接口,要同时维护目标类和代理类

4.若是代理类的增强逻辑一致,可能造成大量重复代码

5.增强代码和目标方法硬编码

2.2动态代理

针对静态代理的缺点,我们可能观察到,许多静态代理的缺点都来自于代理类;

如果我们将代理类干掉,就能解决静态代理的大部分缺点。

可是在静态代理中我们通过代理类的构造方法才能得到代理对象,若是没有代理类,怎么创建代理对象呢?

我们先观察一下到底静态代理是如何通过代理类得到代理对象的,这就涉及到java代码的运行原理了。

编译 类加载 反射得到构造方法

代理类-------> 代理类.class----------> JVM中的Class------------------------>创建得到代理对象

那么动态代理没有代理类,只能通过接口.class,很明显会在得到构造方法这里卡住,因为接口没有构造方法。

那么有没有办法能够既有接口的方法信息又有构造方法呢?

答案是:通过Proxy类的getProxyClass(classLoader,Interface)方法,我们只需传入将接口的信息作为参数传入,就能得到一个既有接口信息又有构造方法的Class对象,通过该Class得到构造方法,我们就能创建代理对象了。

那为什么Proxy类的getProxyClass方法那么神奇?

这里尚且不做深入了解,你可以理解为它就是 想办法把接口Class里面的方法信息拼接一个构造方法得到新的Class。

2.2.1动态代理的实现

实现步骤

1.编写一个工具类,里面提供一个静态方法getProxy(Object target,InvocationHandler i)

2.在getProxy()方法中,通过Proxy类的静态方法getProxyClass(classLoader,Interface),传入目标对象接口的参数,然后得到新的代理Class对象

3.通过代理Class对象调用getDeclaredConstructor(InvocationHandler.class)方法,传入Invocationhandler的Class对象得到构造方法对象

4.通过构造方法对象的newInstance(InvocationHandler)传入Invocationhandler对象创建得到代理对象

实现代码

接口代码

java 复制代码
public interface AInterface {
    void say();
}

目标类代码

java 复制代码
public class AInterfaceImpl implements AInterface{
    @Override
    public void rap() {
        System.out.println("rap");
    }

    @Override
    public void basketball() {
        System.out.println("篮球");
    }
}

代理工具类的代码

java 复制代码
public class MyProxy {
    /**
     * 获取代理对象
     * @param target 目标对象
     * @param invocationHandler 代理处理器
     * @return
     * @throws Exception
     */
    public static Object getProxy(Object target, InvocationHandler invocationHandler) throws Exception{
        //获得目标对象的Class对象
        Class clazz = target.getClass();

        //根据目标对象的Class对象得到代理对象的Class
        Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), clazz.getInterfaces());

        //根据代理对象Class获得代理对象的构造器
        Constructor declaredConstructor = proxyClass.getDeclaredConstructor(InvocationHandler.class);

        //根据代理对象的构造器创建代理对象
        Object o = declaredConstructor.newInstance(invocationHandler);

        //返回得到的代理对象
        return o;
    }
}

得到的代理对象的结构是怎么样的?

代理对象大致结构:
java 复制代码
public class MProxy implements AInterface{
    private InvocationHandler ih;
    
    public MProxy(InvocationHandler ih) {
        this.ih = ih;
    }
    @Override
    public void rap() {
        ih.invoke();
    }

    @Override
    public void basketball() {
        ih.invoke();
    }
}

2.2.2动态代理的缺点

只剩下增强代码和目标方法硬编码

静态代理的缺点有:

1.只能代理某一类接口

2.若进行多次增强,可能代理类暴增

3.修改接口,要同时维护目标类和代理类

4.若是代理类的增强逻辑一致,可能造成大量重复代码

5.增强代码和目标方法硬编码

动态代理如何优化的

对于静态代理的缺点1,动态代理只传入目标对象,目标对象可以是任意接口的,所以解决了

对于静态代理的缺点2、3、4,动态代理没有代理类,也解决了

但是对于静态代理的缺点5增强代码和目标方法硬编码,在动态代理的invoke方法

中还是没能避免,这个缺点在后来的aop(面向切面编程才得以解决),它将方法的逻辑进行拆分,无侵入式的对方法进行增强。

2.2.3动态代理的拓展

本文章的动态代理是JDK根据接口实现的,此外还有第三方的动态代理,如cglib,它实现的原理同JDK不同,它是基于继承来实现的,子类继承父类重写父类方法实现的增强

相关推荐
闲晨2 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity3 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天3 小时前
java的threadlocal为何内存泄漏
java
caridle3 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^3 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋33 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花3 小时前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端3 小时前
第六章 7.0 LinkList
java·开发语言·网络
Wx-bishekaifayuan3 小时前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava