静态代理和动态代理的区别以及实现过程

简介: 这篇文章通过示例代码讲解了静态代理和动态代理在Java中的使用方式和区别,重点介绍了动态代理的实现原理及其在Spring框架中的应用。

前言

代理模式是一种设计模式,能够使得在不修改源目标的前提下,额外扩展源目标的功能。即通过访问源目标的代理类,再由代理类去访问源目标。这样一来,要扩展功能,就无需修改源目标的代码了。只需要在代理类上增加就可以了。

在这里插入图片描述
其实代理模式的核心思想就是这么简单,在java中,代理又分静态代理动态代理2种,其中动态代理根据不同实现又区分基于接口的的动态代理和基于子类的动态代理。

其中静态代理由于比较简单,面试中也没啥问的,在代理模式一块,问的最多就是动态代理,而且动态代理也是spring aop的核心思想,spring其他很多功能也是通过动态代理来实现的,比如拦截器,事务控制等。

熟练掌握动态代理技术,能让你业务代码更加精简而优雅。如果你需要写一些中间件的话,那动态代理技术更是必不可少的技能包。

静态代理

静态代理,就是通过声明一个明确的代理类来访问源对象。

我们有1个接口,Person。这个个接口各有2个实现类,UML如下图:
在这里插入图片描述

实现

接口:person.java

package StaticProxy;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:29
 * @Description:
 */
public interface Person {

    /**
     * 起床
     */
    public void wakeup();

    /**
     * 睡觉
     */
    public void sleep();
}

实现类:Student .java

package StaticProxy;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:32
 * @Description:
 */
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+",睡觉了!!!");
    }
}

假设我们现在要做一件事,就是在所有的实现类调用wakeup()前增加一行输出早安,调用sleep()前增加一行输出晚安。那我们只需要编写1个代理类PersonProxy

代理类:PersonProxy .java

package StaticProxy;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:35
 * @Description:
 */
public class PersonProxy implements Person{
    private Person person;

    public PersonProxy(Person person){
        this.person = person;
    }
    @Override
    public void wakeup() {
        System.out.println("早上好啊!!!");
        person.wakeup();
    }

    @Override
    public void sleep() {
        System.out.println("晚上好啊!!!");
        person.sleep();
    }
}

测试类

package StaticProxy;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:37
 * @Description:
 */
public class Test {
    public static void main(String[] args) {
        Person student1 = new Student("张三");
        PersonProxy studentProxy = new PersonProxy(student1);
        studentProxy.wakeup();
        studentProxy.sleep();
    }
}

结果

在这里插入图片描述

结论:

静态代理的代码相信已经不用多说了,代码非常简单易懂。这里用了1个代理类,代理了Person接口。

这种模式虽然好理解,但是缺点也很明显:

  • 会存在大量的冗余的代理类,这里演示了1个接口,如果有10个接口,就必须定义10个代理类。
  • 不易维护,一旦接口更改,代理类和目标类都需要更改。

动态代理

动态代理,通俗点说就是:无需声明式的创建java代理类,而是在运行过程中生成"虚拟"的代理类,被ClassLoader加载。从而避免了静态代理那样需要声明大量的代理类。

JDK从1.3版本就开始支持动态代理类的创建。主要核心类只有2个:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

还是前面那个例子,用JDK动态代理类去实现的代码如下:

创建一个JdkProxy类,用于统一代理:

package DynamicProxy;

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

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:28
 * @Description:
 */
public class JdkProxy implements InvocationHandler {

    private Object bean;

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

    /**
     * 其中proxy为代理过之后的对象(并不是原对象),method为被代理的方法,args为方法的参数。
     *
     * 如果你不传原有的bean,直接用method.invoke(proxy, args)的话,那么就会陷入一个死循环。
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @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);
    }
}

测试

package DynamicProxy;

import StaticProxy.Person;
import StaticProxy.Student;

import java.lang.reflect.Proxy;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:46
 * @Description:
 */
public class Test {
    public static void main(String[] args) {
        JdkProxy proxy = new JdkProxy(new Student("李四"));
        Person student = (Person) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Person.class}, proxy);
        student.wakeup();
        student.sleep();
    }
}

结果

在这里插入图片描述

可以看到,相对于静态代理类来说,无论有多少接口,这里只需要一个代理类。核心代码也很简单。唯一需要注意的点有以下2点:

JDK动态代理是需要声明接口的,创建一个动态代理类必须得给这个”虚拟“的类一个接口。可以看到,这时候经动态代理类创造之后的每个bean已经不是原来那个对象了。

这里JdkProxy最核心的方法就是

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

其中proxy为代理过之后的对象(并不是原对象),method为被代理的方法,args为方法的参数。

如果你不传原有的bean,直接用method.invoke(proxy, args)的话,那么就会陷入一个死循环。

相关文章
|
2月前
|
Java 数据库 Spring
Java编程问题之在测试中使用CGLIB创建代理类如何解决
Java编程问题之在测试中使用CGLIB创建代理类如何解决
|
4月前
|
数据采集 SEO
动态代理IP和静态代理,到底有什么区别?
静态代理IP提供固定IP,适用于长期稳定连接,如服务器管理和账户维护,具有较高稳定性和安全性。动态代理IP则会定期更换IP,适合网络爬虫和需要模拟全球用户行为的场景,提供灵活性和广泛覆盖。选择代理IP应根据业务需求、预算和法规要求,确保合法合规使用。
|
4月前
|
设计模式 Java 索引
由反射引出的Java动态代理与静态代理
由反射引出的Java动态代理与静态代理
27 0
|
4月前
|
Java API 开发者
Java代理模式——静态代理与动态代理
Java代理模式——静态代理与动态代理
45 1
|
4月前
|
Java 开发者 Spring
什么是静态代理和动态代理,两者的区别(笔记)
什么是静态代理和动态代理,两者的区别(笔记)
189 0
|
设计模式 缓存 SpringCloudAlibaba
浅析动态代理原理与实现
代理是一种软件设计模式,目的地希望能做到代码重用。具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就好比 商户---->明星经纪人(代理)---->明星这种模式。我们可以不通过直接与明星对话的情况下,而通过明星经纪人(代理)与其产生间接对话
136 0
浅析动态代理原理与实现
|
缓存 Java 测试技术
动态代理:JDK动态代理源码学习
动态代理:JDK动态代理源码学习
57 1
|
Java
动态代理有哪些实现方式?
动态代理是一种在运行时创建代理对象的技术,它可以在不修改目标对象的情况下,通过代理对象来增强目标对象的功能。在Java中,有两种主要的动态代理实现方式:JDK动态代理和CGLIB动态代理。
138 0
|
设计模式 Java 程序员
静态代理和动态代理区别(是否有实现类)
静态代理和动态代理区别(是否有实现类)
356 0
静态代理和动态代理区别(是否有实现类)
|
设计模式 Java API
【Java代理】【静态代理】【动态代理】【动态代理的2种方式】
【Java代理】【静态代理】【动态代理】【动态代理的2种方式】
【Java代理】【静态代理】【动态代理】【动态代理的2种方式】