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

简介: 这篇文章通过示例代码讲解了静态代理和动态代理在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)的话,那么就会陷入一个死循环。

相关文章
|
网络协议 API
让每个进程不同外网 IP,实现局部单窗口单 IP,驱动级网络加速器原理!
为解决游戏多开IP限制问题,可采用API HOOK、LSP劫持或驱动层拦截(WFP/TDI)结合SOCKS5转发数据,实现每个进程独立的外网IP。LSP方法需HOOK多个网络函数,如WSPCONNECT等。驱动层中,WFP适用于Win7以上系统,全面拦截,TDI兼容所有系统但拦截不全。提供了一个自编写的驱动层代理示例,支持全局和局部IP设置,适用于每个进程不同IP,长期维护,欢迎使用。下载链接:http://down.8u18.com/down/jsq.rar。附图片展示。
9611 0
|
监控 Linux 数据安全/隐私保护
IPMI介绍
IPMI简要介绍
7717 0
|
12月前
|
XML Java 数据格式
Spring BeanFactory深度讲解
Spring `BeanFactory` 是 Spring 容器的基础,负责创建、配置和管理 Bean 实例,并提供对 Bean 生命周期的管理和控制。它通过读取配置文件或注解来实例化和管理 Bean,支持 XML、Java 配置和注解配置方式。与 `ApplicationContext` 相比,`BeanFactory` 更轻量级,采用延迟加载策略,适用于资源受限的环境。它实现了工厂模式,将对象的创建和管理分离,使应用程序更灵活、可扩展且易于维护。
167 0
|
Java 开发者 Spring
什么是静态代理和动态代理,两者的区别(笔记)
什么是静态代理和动态代理,两者的区别(笔记)
318 0
|
关系型数据库 MySQL 数据库连接
成功解决「MySQL问题1」启动mysql时:发生系统错误5拒绝访问
这篇文章介绍了如何解决启动MySQL服务时遇到的系统错误5(拒绝访问),通过管理员权限启动命令窗口并使用"net start mysql"和"net stop mysql"命令来控制服务。
|
Cloud Native 安全 物联网
智能家居技术的未来展望云原生时代的技术革新与挑战
【8月更文挑战第23天】随着科技的飞速发展,智能家居已经从科幻小说走入了现实生活。本文将探讨智能家居技术的发展趋势、面临的挑战以及未来的潜力。我们将了解如何通过技术创新,使家居生活变得更加智能和便捷,同时确保用户隐私和数据安全。文章旨在启发读者思考智能家居如何与日常生活更紧密地结合,并探索其在未来社会中的角色。
|
前端开发 Java Spring
SpringBoot通过拦截器和JWT令牌实现登录验证
该文介绍了JWT工具类、匿名访问注解、JWT验证拦截器的实现以及拦截器注册。使用`java-jwt`库生成和验证JWT,JwtUtil类包含generateToken和verifyToken方法。自定义注解`@AllowAnon`允许接口匿名访问。JwtInterceptor在Spring MVC中拦截请求,检查JWT令牌有效性。InterceptorConfig配置拦截器,注册并设定拦截与排除规则。UserController示例展示了注册、登录(允许匿名)和需要验证的用户详情接口。
1879 1
|
Python
多ip多进程代理的实现方法
多ip多进程代理的实现方法
355 7
|
存储 Java Python
【Python小知识】如何解决代理IP在多线程环境下的并发问题?
【Python小知识】如何解决代理IP在多线程环境下的并发问题?
161 0

热门文章

最新文章