代理模式(Java实现)

代理模式是常见的设计模式之一,顾名思义,代理模式就是代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理。(为真实对象提供代理,然后供其他对象通过代理访问真实对象)

分为

  • 静态代理

  • 动态代理

    • jdk动态代理

    • cglib动态代理

静态代理

真实类和代理类要实现同一个接口,在代理类中实现真实类的方法同时可以进行真实类方法的增强处理,在一个代理类中就可以完成对多个真实对象的注入工作。

java 复制代码
public interface IRentHouse {
    void rentHouse();
}
public class RentHouse implements IRentHouse {

    @Override
    public void rentHouse() {
        System.out.println("实现租房");
    }
}
public class IntermediaryProxy implements IRentHouse {
    private IRentHouse iRent;
    public IntermediaryProxy(IRentHouse iRentHouse) {
        iRent=iRentHouse;
    }
    @Override
    public void rentHouse() {
        System.out.println("交中介费");
        iRent.rentHouse();
        System.out.println("中介负责维修管理");
    }
}
//client测试类
public class TestStaticProxy {
    public static void main(String[] args) {
        //定义租房
        IRentHouse iRentHouse = new RentHouse();
        //定义中介
        IRentHouse intermediaryProxy = new IntermediaryProxy(iRentHouse);
        //中介租房
        intermediaryProxy.rentHouse();
    }
}

动态代理

从静态代理的代码中可以发现,静态代理的缺点显而易见,那就是当真实类的方法越来越多的时候,这样构建的代理类的代码量是非常大的,所以就引进动态代理.

动态代理允许使用一种方法的单个类(代理类)为具有任意数量方法的任意类(真实类)的多个方法调用提供服务

JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。

jdk动态代理(接口代理)

Jdk代理涉及到java.lang.reflect包中的InvocationHandler接口和Proxy类,核心方法是

java 复制代码
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 

jdk动态代理过程中实际上代理的是接口,是因为在创建代理实例的时候,依赖的是java.lang.reflect包中Proxy类的newProxyInstance方法,该方法的生效就恰恰需要这个参数;

java 复制代码
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{
        ......
}

下面以案例来说明jdk动态代理的完整过程:

java 复制代码
//接口
public interface Person {
    void wakeup();
    void sleep();
}
//实现类1
public class Student implements Person{
    private String name;
    public Student() {
    }
    public Student(String name) {
        this.name = name;
    }
    @Override
    public void wakeup() {
        System.out.println("学生"+name+"早晨醒来啦");
    }
    @Override
    public void sleep() {
        System.out.println("学生"+name+"晚上睡觉啦");
    }
}
java 复制代码
//代理类
public class JDKDynamicProxy implements InvocationHandler {
    private Object bean;
    public JDKDynamicProxy(Object bean) {
        this.bean=bean;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodname=method.getName();
        if (methodname.equals("wakeup")){
            System.out.println("早安~~~");
        }else if(methodname.equals("sleep")){
            System.out.println("晚安~~~");
        }
        return method.invoke(bean,args);
    }
}
java 复制代码
//测试类
public class TestJDKDynamicProxy {
    public static void main(String[] args) {
        JDKDynamicProxy proxy = new JDKDynamicProxy(new Student("张三"));
        //创建代理实例
        Person student = (Person) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Person.class}, proxy);
        student.wakeup();
        student.sleep();
    }
}

输出结果为

java 复制代码
早安~~
学生张三早晨醒来啦
晚安~~
学生张三晚上睡觉啦
复制代码
接口中的方法,以及代理类中重写的invoke方法,但是invoke()方法并不是显式调用的,是在创建代理实例的过程中生成的接口虚拟代理类中调用了invoke方法。(把Sproxy0的实例强制转换成对应接口类型的引用,然后执行接口方法,进而执行代理类中invoke ()) 

总结对比:

1.静态代理中,代理类和真实类实现的是同一个接口,重写同样的方法;jdk动态代理中,代理类和真实类关系不大,代理类实现无侵入式的代码扩展。

2.静态代理中当接口中方法增加的时候,在代理类代码量也会增加,显然是不妥的;jdk动态代理解决了这个问题,当业务增加,代理类的代码不会增加。

3.jdk动态代理实现的是jdk自带InvocationHandler接口,实现了这个接口的类也叫拦截器类,也叫代理类。

cglib动态代理

从上面可以看出,jdk动态代理的前提条件是,要有接口存在,那还有许多场景是没有接口的,这个时候就需要cglib动态代理了,CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。cglib动态代理过程中生成的是实现类的子类,cglib是如何凭空创造的实现类的子类的,下面是测试代码

java 复制代码
//所需的代理类
public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer=new Enhancer();
    private Object bean;

    public CglibProxy(Object bean) {
        this.bean = bean;
    }

    public Object getProxy(){
        //设置需要创建子类的类
        enhancer.setSuperclass(bean.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        String methodName = method.getName();
        if (methodName.equals("wakeup")){
            System.out.println("早安~~~");
        }else if(methodName.equals("sleep")){
            System.out.println("晚安~~~");
        }
        return method.invoke(bean,objects);
    }
}
java 复制代码
//测试类
public class TestCglibProxy {
    public static void main(String[] args) {
        //生成虚拟代理类的代码,本来虚拟代理子类是看不见的,
        //下面这句话的作用就是把执行过程中cglib增强后的class字节码文件
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\aop");
        CglibProxy proxy = new CglibProxy(new Cat("咪咪"));
        Cat cat = (Cat) proxy.getProxy();
        cat.wakeup();
        cat.sleep();
    }
}

总结:

cglib动态代理和jdk动态代理的区别显而易见,但是实现逻辑差不多,cglib代理类是通过实现MethodInterceptor,重写intercept方法,通过生成被代理类的子类来达到代理增强代码的目的;而Jdk代理是通过实现InvocationHandler,重写invoke方法,通过生成接口的代理类来达到代码增强的目的,所以jdk动态代理的实现需要接口,cglib则不需要,spring5.0以上以及springboot2.0以上默认采用cglib动态来实现AOP。

相关推荐
The Future is mine28 分钟前
Python计算经纬度两点之间距离
开发语言·python
Enti7c29 分钟前
HTML5和CSS3的一些特性
开发语言·css3
腥臭腐朽的日子熠熠生辉35 分钟前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
爱吃巧克力的程序媛36 分钟前
在 Qt 创建项目时,Qt Quick Application (Compat) 和 Qt Quick Application
开发语言·qt
ejinxian37 分钟前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之42 分钟前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
俏布斯1 小时前
算法日常记录
java·算法·leetcode
独好紫罗兰1 小时前
洛谷题单3-P5719 【深基4.例3】分类平均-python-流程图重构
开发语言·python·算法
27669582921 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿