7000字详解 动态代理(JDK动态代理 CGLIB动态代理)与静态代理

代理模式

  • 1. 代理模式 概念
  • 2. 静态代理
  • 3. 动态代理
    • 3.1.JDK动态代理
    • 3.2.CGLIB动态代理
    • 3.3. JDK动态代理和CGLIB动态代理区别
  • 4.静态代理和动态代理区别
  • 5.篇末

1. 代理模式 概念

代理模式是一种设计模式
使用代理对象来替代真实对象,用代理对象去访问目标对象。这样可以保证在不修改目标对象的前提下,还可以增加一些额外的功能,作出扩展。
在这里插入图片描述

代理模式的作用主要是扩展目标对象的功能,在目标对象执行方法的前后,可以自己自定义一些操作。

同时代理模式分为静态代理动态代理

2. 静态代理

静态代理对目标对象的方法增强要手动完成,如果接口增加了新的方法,那么就要更改代码,不够灵活,开发中比较少见静态代理。

JVM层面:
静态代理在编译期讲接口,和接口的实现类,代理类都变成class文件。

静态代理实现:
1. 定义接口A和实现类 (也就是要传输的内容)
2. 创建一个代理类实现接口A (代理类要可以接收传输的内容)
3. 将目标对象注入到代理类中,在代理类的方法中调用目标对象的方法,然后在目标对象方法执行的前后 就可以扩展一些内容。

代码:
1.定义发送内容的接口

package 静态代理;

// 1.定义发送内容的接口    目标对象
public interface Message {
    String send(String message);
}

2.实现发送短信的接口

package 静态代理;

//2. 发送内容接口的实现类      就是目标对象处理传输的内容的处理方法
public class MessageImpl implements Message {
    @Override
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}

3.创建代理类并实现接口

package 静态代理;

//3. 代理类 也需要实现接口 且需要将目标对象注入到其中来   代理对象
public class MessageProxy implements Message {

    //目标对象(Message)注入到代理类中
    private final Message Message;

    public MessageProxy(Message message) {
        this.Message = message;
    }

    @Override
    public String send(String message) {
        //方法执行前 添加自定义内容
        System.out.println("before method send()");
        //在代理类中的方法 调用目标类的方法
        Message.send(message);
        //方法执行后 添加自定义内容
        System.out.println("after method send()");
        return null;
    }
}

4.执行代码

public class Main {
    public static void main(String[] args) {
        System.out.println("静态代理");
        //实例化 实现类对象 类型是Message    目标对象
        //Message sendMessage = new MessageImpl();
        MessageImpl sendMessage = new MessageImpl();
        //实例化 代理类对象 完成注入          代理对象
        MessageProxy messageProxy = new MessageProxy(sendMessage);
        //执行方法
        messageProxy.send("hello");
    }
}

在这里插入图片描述

3. 动态代理

对于静态代理来说,动态代理更加灵活,不需要保证每个目标类都有一个代理类,实现接口也不是必须的,可以直接代理实现类。

JVM层面:
动态代理是在运行时动态生成类字节码,加载到JVM中。

举例:
Spring AOP ,RPC框架使用了动态代理。

动态代理对于框架的学习帮助较大

动态代理实现方法很多 ,这篇介绍 JDK动态代理CGLIB动态代理。

3.1.JDK动态代理

JDK动态代理中 InvocationHandler接口Proxy类是重点。

Proxy类中有一个方法:newProxyInstance() 用来生成代理对象。

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

Proxy类中的newProxyInstance()方法有三个参数:
1.loader:类加载器,用来加载代理对象。
2.interfaces: 被代理类实现的一些接口。
3.h: 实现了InvocationHandler接口的对象。

动态代理还需要实现InvocationHandler接口,自定义处理内容的逻辑,这样我们在代理对象调用一个方法的时候,方法会转发到InvocationHandler接口类中的invoke方法

public interface InvocationHandler {

    /**
     * 当你使用代理对象调用方法的时候实际会调用到这个方法
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

InvocationHandler接口中的invock方法有三个参数:
**1.proxy:**动态生成的代理类。
**2.method:**与代理类调用的方法对应
**3.args:**method方法的参数

动态代理机制:通过Proxy类的newProxyInstance方法 创建的代理类在调用方法的时候,实际调用的InvocationHandler接口中的Invoke方法。
所以需要在Invoke方法中自定义内容。

JDK动态代理实现:
1.定义一个接口和实现类
2.重写InvocationHandler中的Invoke()方法,用来自定义内容。
3.通过Proxy类的newProxyInstance()方法创建代理对象。

代码:
1.定义发送内容的接口

package JDK动态代理;
//目标对象
public interface SdMessage {
    String send(String message);
}

2.发送内容接口的实现类

package JDK动态代理;
//实现接口的实现类     就是目标对象处理传输的内容的处理方法
public class SdMessageImp implements SdMessage {
    @Override
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}

3.重写InvocationHandler中的Invoke方法

package JDK动态代理;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//代理类调用方法时 调用的是InvocationHandler中的Invoke方法 重写它 自定义
public class MyInvocationHandler implements InvocationHandler {
    /**
     * 代理类中的真实对象
     */
    private final Object target;
    public MyInvocationHandler(Object target) {
        this.target = target;
    }


    //其中的method和代理类调用的方法对应
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法调用前,自定义的内容
        System.out.println("before method() " + method.getName());
        Object result = method.invoke(target,args);
        //方法调用后,自定义的内容
        System.out.println("after method() " + method.getName());
        return result;
    }
}

4.创建代理类

package JDK动态代理;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//代理类调用方法时 调用的是InvocationHandler中的Invoke方法 重写它 自定义
public class MyInvocationHandler implements InvocationHandler {
    /**
     * 代理类中的真实对象
     */
    private final Object target;
    public MyInvocationHandler(Object target) {
        this.target = target;
    }


    //其中的method和代理类调用的方法对应
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法调用前,自定义的内容
        System.out.println("before method() " + method.getName());
        Object result = method.invoke(target,args);
        //方法调用后,自定义的内容
        System.out.println("after method() " + method.getName());
        return result;
    }
}

5.执行



public class Main {
    public static void main(String[] args) {
        //JDK动态代理  其中的参数是目标对象
        System.out.println("JDK动态代理");
        SdMessage sdMessage = (SdMessage) ProxyFactory.getProxy(new SdMessageImp());
        sdMessage.send("hello");
    }
}

在这里插入图片描述

3.2.CGLIB动态代理

JDK动态代理的缺点是 只可以代理已经实现了接口的实现类。
而CGLIB动态代理可以避免

CGLIB是一个基于ASM的字节码生成库,允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理,很多开源框架都使用到了CGLIB,例如Spring中的AOP模块中,如果实现了接口那么就采用JDK动态代理,如果没实现,就使用CGLIB动态代理。

CGLIB动态代理MethodInterceptor接口Enhancer类是重点

需要重写MethodInterceptor接口中的Intercept方法,这个方法用来拦截被代理类

public interface MethodInterceptor
extends Callback{
    // 拦截被代理类中的方法
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
}


**MethodInterceptor接口中的Intercept()**方法有4个参数:
1.obj:被代理的对象(也可以叫做需要增强的对象)
2.method:被拦截的方法(也可以叫做需要增强的方法)
3.args:方法的参数
4.proxy:用于调用原始方法

可以通过Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用的是MethodInterceptor接口中的Intercept方法。

CGLIB动态代理实现:
1.定义一个类
2.重写MethodInterceptor接口中的Intercept()方法,这个方法用来拦截被代理类的方法,其实也就是拦截到方法以后,会执行intercept方法,和被代理类的方法。
3.通过Enhancer类的create()方法创建代理类。

CGLIB动态代理代码实现:
CGLIB是开源项目需要引入依赖。

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>

1.实现一个发送消息的类

package CGLIB动态代理;

//发送内容的类
public class CglibSdMessage {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}

2.自定义MethodInterceptor(方法拦截器)重写其中的Intercept方法

package CGLIB动态代理;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

//方法拦截器  重写MethodInterceptor接口中的intercept方法
public class MyMethodInterceptor implements MethodInterceptor {

    /**
     *
     * @param o             被代理的对象(需要增强的对象)
     * @param method        被拦截的方法(需要增强的方法)
     * @param objects       方法的参数
     * @param methodProxy   用于调用原始方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //方法调用前,自定义的内容
        System.out.println("before method() " + method.getName());
        Object object =  methodProxy.invokeSuper(o,objects);
        //方法调用后,自定义的内容
        System.out.println("after method() " + method.getName());
        return object;
    }
}

3.获取代理类(创建代理类)

package CGLIB动态代理;

import net.sf.cglib.proxy.Enhancer;

//创建代理类 通过Enhancer类的create方法
public class CglibProxyFactory {
    //Class<?> clazz之前接收的是实现接口的实现类 现在接收的是类 泛型
    public static Object getProxy(Class<?> clazz) {
        //创建动态代理类
        Enhancer enhancer = new Enhancer();
        //设置类加载器
        enhancer.setClassLoader(clazz.getClass().getClassLoader());
        //设置被代理类(目标对象)
        enhancer.setSuperclass(clazz);
        //设置方法拦截器
        enhancer.setCallback(new MyMethodInterceptor());
        //创建代理类
        return enhancer.create();
    }
}

4.调用

public class Main {
    public static void main(String[] args) {
        //CGLIB动态代理 其中的参数是被代理类(目标对象)
        System.out.println("JDK动态代理");
        CglibSdMessage cglibSdMessage = (CglibSdMessage) CglibProxyFactory.getProxy(CglibSdMessage.class);
        cglibSdMessage.send("hello");
    }
}

在这里插入图片描述

3.3. JDK动态代理和CGLIB动态代理区别

  • JDK动态代理只能代理已经实现了接口的类,CGLIB动态代理可以直接代理没有实现接口的(CGLIB动态代理通过生成一个被代理类的子类来拦截被代理类中的方法调用,因为是继承所以不能代理被final修饰的类和方法)
  • JDK动态代理效率更高

4.静态代理和动态代理区别

  • 灵活性:动态代理更加的灵活,接口的实现不是必要的,CGLIB动态代理可以直接代理一个类。静态代理只能代理实现了接口的类,并且接口增加新方法的时候,目标对象的代码和代理对象的代码都要修改。
  • JVM:静态代理在编译期就把接口,实现类,代理类变成Class文件,动态代理在运行时动态生成类字节码文件,加载到JVM中。

5.篇末

我在文章中介绍了静态代理和动态代理,包阔了静态代理的代码实现和动态代理中的2钟代理(JDK动态代理和CGLIB动态代理)的代码实现,已经静/动态代理的区别,和两种动态代理的区别。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/191330.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

笔记(三)maxflow push relabel与图像分割

笔记&#xff08;三&#xff09;maxflow push relabel与图像分割 1. Push-Relabel算法思想2.Push-Relabel算法原理示意图3.Push-Relabel算法具体实例4. push relabel与图割 1. Push-Relabel算法思想 对于一个网络流图: 该算法直观可以这样理解&#xff0c;先在源节点处加入充足…

WordPress无需插件禁用WP生成1536×1536和2048×2048尺寸图片

我们在使用WordPress上传图片媒体文件的时候&#xff0c;是不是看到媒体库中有15361536和20482048的图片文件&#xff0c;当然这么大的文件会占用我们的服务器空间&#xff0c;如何禁止掉呢&#xff1f; function remove_default_image_sizes( $sizes) {unset( $sizes[1536x15…

人力资源管理后台 === 组织架构

目录 1.组织架构-树组件应用 2.组织架构-树组件自定义结构 3.组织架构-获取组织架构数据 4.组织架构-递归转化树形结构 5.组织架构-添加子部门-新建弹层组件 6.组织架构-添加子部门-表单结构 7.组织架构-添加子部门-表单基本校验 8.组织架构-添加子部门-表单业务校验 9…

ATK-ESP8266 WIFI模块串口通信通用实现方案

ATK-ESP8266 WIFI模块是一种常用的无线模块&#xff0c;它可以通过串口与外部设备进行通信&#xff0c;实现数据的收发和控制。本文将介绍一种通用的实现方案&#xff0c;帮助您在项目中使用ATK-ESP8266 WIFI模块进行串口通信。 【方案概述】 这个通用实现方案涵盖了ATK-ESP82…

VMWare虚拟机ubuntu克隆打不开

ubuntu克隆打不开 复制的存有ubuntu克隆的文件夹&#xff0c;导入vmware打不开 说找不到这个文件&#xff0c;那就到目录把它的删掉 的删掉 换000001.vmdk后缀的

Android 10.0 mtp模式下连接pc后显示的文件夹禁止删除copy重命名功能实现

1.前言 在10.0的系统开发中,usb连接pc端的时候有好几种模式,在做otg连接pc端的时候,改成mtp模式的时候,在pc端可以看到产品设备 的显示的文件夹的内容,对于产品设备里面的文件在pc端禁止做删除重命名拷贝等操作功能的实现 2.mtp模式下连接pc后显示的文件夹禁止删除copy重命…

在我国干独立游戏开发有多难?

游戏独立开发在中国&#xff0c;一直以来都是一条充满挑战的道路。尽管有着无限的激情和创意&#xff0c;但面对市场、资金、政策等多方面的困难&#xff0c;许多独立开发者在这条路上艰难前行。 首先&#xff0c;市场竞争激烈是中国游戏独立开发者面临的首要挑战。随着游戏产…

Unsupervised MVS论文笔记(2019年)

Unsupervised MVS论文笔记&#xff08;2019年&#xff09; 摘要1 引言2 相关工作3 实现方法3.1 网络架构3.2 通过光度一致性学习3.3 MVS的鲁棒光度一致性3.4 学习设置和实施的细节3.5.预测每幅图像的深度图 4 实验4.1 在DTU上的结果4.2 消融实验4.3 在ETH3D数据集上的微调4.4 在…

BUUCTF [MRCTF2020]Ez_bypass 1

题目环境&#xff1a;F12查看源代码 I put something in F12 for you include flag.php; $flagMRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}; if(isset($_GET[gg])&&isset($_GET[id])) { $id$_GET[id]; $gg$_GET[gg]; if (md5($id) md5($gg) && $id ! $gg) { …

显示Excel功能区或工具栏的方法不少,其中快捷方式最快

Microsoft Excel是Office套件中最复杂的工具之一&#xff0c;它提供了大量功能&#xff0c;其中大部分都是使用工具栏操作的。缺少工具栏使Excel很难完成工作。 如果Excel中没有这些关键元素&#xff0c;你将无法快速完成工作&#xff0c;因此&#xff0c;可以理解的是&#x…

算法基础之合并集合

合并集合 核心思想:并查集: 1.将两个集合合并2.询问两个元素是否在一个集合当中 基本原理:每个集合用一棵树表示 树根的编号就是整个集合的编号 每个节点存储其父节点&#xff0c;p[x]表示x的父节点 #include<iostream>using namespace std;const int N100010;int p[N];…

RESTful API 架构快速入门 Flask实现

RESTful 简介 1.1 为什么要使用 RESTful 架构&#xff1f; Representational State Transfer&#xff08;REST&#xff09;是一种面向资源的架构风格&#xff0c;广泛应用于网络服务的设计和开发。使用RESTful架构有以下几个优点&#xff1a; 简单性和可扩展性&#xff1a; RE…

springboot 拦截器中使用@Value注解为null

拦截器中获取配置参数为null 代码如下&#xff1a; 解决方式一&#xff1a; 检查你的WebMvcConfigurer实现类&#xff0c;比如我的是CCBWebMvcConfig 将拦截器以bean的形式注入&#xff1a; 我之前的写法是new 一个放进去的&#xff0c;这种会导致Value为null AutowiredJSCCB…

图像分割模型及架构选型介绍(MMSegmentation|sssegmentation等)

参考&#xff1a; https://zhuanlan.zhihu.com/p/618226513 0. 图像分割概述 图像分割通过给出图像中每个像素点的标签&#xff0c;将图像分割成若干带类别标签的区块&#xff0c;可以看作对每个像素进行分类。图像分割是图像处理的重要组成部分&#xff0c;也是难点之一。随…

二次创作Z01语言

目录 一&#xff0c;字符集 二&#xff0c;编译分词 三&#xff0c;token含义 四&#xff0c;Z01翻译成C 五&#xff0c;执行翻译后的代码 六&#xff0c;打印Hello World! 一&#xff0c;字符集 假设有门语言叫Z01语言&#xff0c;代码中只有0和1这两种字符。 二&#…

使用花生壳外网远程ssh访问内网主机 亲测有效

经常会遇到远程访问其他电脑的需求&#xff0c;一般首选向日葵软件&#xff0c;傻瓜式的连接远程桌面控制&#xff0c;非常方便。但是仅限于远程桌面远程协助这种。 对于程序员来说最佳的登录方式是ssh&#xff0c;同时远程桌面连过来的时候分辨率比较低&#xff0c;图形效果相…

上海交通大学生存手册

强烈推荐所有大学生去阅读《上海交通大学生存手册》。虽然它可能有些冗长&#xff0c;但非常重要&#xff0c;因为它道出了大学教育的本质。 如果几年前我能够看到这本书&#xff0c;也许我的大学生活会有所不同。现在我将向正在上大学或者将要上大学的你推荐这本书。 无论你…

Less的函数的介绍

文章目录 前言描述style.less输出后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Sass和Less &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现错误&#xff0c;…

python--获取每张切片的不同PEF区间值的百分比

在全直径数字岩心中&#xff0c;如何获取每张切片的不同PEF区间值的百分比&#xff1f; import os import datetime from PIL import Image import numpy as np import csv import easygui as gclass Table(object):def __init__(self, table_data_path):self.table_data_path…

MySQL进阶_10.锁

文章目录 一、概述二、MySQL并发事务访问相同记录2.1、读-读2.2、写-写2.3、读-写2.4、并发问题的解决方案 三、锁的不同角度分类3.1、 读锁、写锁3.1.1、 锁定读 3.2、表级锁、页级锁、行锁3.2.1、表锁3.2.2、意向锁3.2.2.1、意向锁的作用3.2.2.2、意向锁的互斥性 3.2.3、自增…