JavaWeb开发(三)3.6——代理模式

news2024/11/27 10:24:32

一、代理模式概述

1.1、代理模式的理解

参考人家的举例,感觉挺形象,容易理解:
就拿明星与经纪人的关系来说,明星就好比被代理类,明星只需要负责唱歌,表演或给粉丝签名等事务,而类似签合同,面谈,计划日程安排等经纪事务都不需要明星个人去做,可以交给其经纪人来代理完成。

1.2、代理模式的分类:

  • 静态代理模式(静态的定义代理类,编译前定义好):涉及接口的使用。
  • 动态代理模式(动态的生成代理类):涉及接口、反射、Proxy类的相关api。

1.3、代理模式的应用场景

  • 安全代理:对外隐藏真实的调用者
  • 远程代理:通过代理类处理远程方法调用(RMI)
  • 延迟加载

eg:
意图:为其他对象提供一种代理以控制对这个对象的访问。

主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

何时使用:想在访问一个类时做一些控制。

如何解决:增加中间层。

关键代码:实现与被代理类组合。

应用实例: 1、Windows 里面的快捷方式。2、买火车票不一定在火车站买,也可以去代售点。 3、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。4、spring aop。

优点: 1、职责清晰。 2、高扩展性。 3、智能化。

缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

使用场景:按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。

注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

二、静态代理

2.1、静态代理模式的实现要求

  • 提供两个真实的具体的类,代理类与被代理类;
  • 两个类同时实现同一个接口,接口中定义多个抽象方法(提取代理类,被代理各自的行为任务);
  • 代理类一定要由被代理对象的引用,为了能在代理类中调用被代理类的重写接口中的方法。

2.2、静态代理存在的问题

  • 一旦接口新增或者修改,那么代理对象和被代理对象就得去适配修改。
  • 静态代理是在代码编写时,去生成的,class文件必然会造成类爆炸的风险。

2.3、静态代理示例

(1)被代理接口:

public interface StarInterface {
	//个人签名
	void personSignature();
	//表演
	void show();
	//签合同
	void signContract();
	//日程安排
	void schedule();
}

(2)被代理类(被代理类实现被代理接口):

//明星类
public class Star implements StarInterface{
	@Override
	public void personSignature() {
		System.out.println("明星个人签名");
	}
 
	@Override
	public void show() {
		System.out.println("明星表演");
	}
 
	@Override
	public void signContract() {

	}
 
	@Override
	public void schedule() {

	}
	
}

(3)代理类(实现被代理接口,有被代理对象的引用):

//经纪人
public class Agent implements StarInterface{
	private Star star;
	
	public Agent(Star star) {
		this.star = star;
	}
	
	public Agent() {
		
	}
	
	@Override
	public void personSignature() {
		star.personSignature();
	}
 
	@Override
	public void show() {
		star.show();
	}
 
	@Override
	public void signContract() {
		System.out.println("经纪人谈合同");
	}
 
	@Override
	public void schedule() {
		System.out.println("经纪人安排演唱会");
	}
	
}

(4)测试类:

public class StaticProxyTest {
	public static void main(String[] args) {
		Agent agent = new Agent(new Star());
		agent.personSignature();
		agent.show();
		agent.schedule();
		agent.signContract();
	}
}

三、动态代理

当我们写了一个接口,里面有方法,然后写了实现类实现方法,完成方法逻辑,然后有一天,想对这个方法进行修改,但是不想改源码,有两种方式可以实现。第一种是静态代理,创建一个类继承实现类,然后对方法进行修改,这样太局限,因为只能针对特定的类增强方法,有100个实现类就要创建100个子类去实现。第二种方式是动态代理,可以动态地生成代理类,这是可以接受的。

动态代理,顾名思义,就是在运行时去动态生成代理,我们下面分别说两种实现方式,分别为JDK 动态代理 和 Cglib 动态代理。

3.1、JDK动态代理

JDK提供了一些API,可以实现动态代理,通过实现 InvocationHandler接口和 Proxy.newProxyInstance来实现。

(1)需要被代理的接口(必须要有)和实现类,采用上述静态代理中的例子,被代理接口StarInterface和被代理类Star。

(2)创建动态代理类
需要实现InvocationHandler接口,重写invoke方法,这里可以对方法进行增强。:

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

public class Dynamicproxy implements InvocationHandler {
    private Object targetObject;

    public Dynamicproxy(Object targetObject) {
        this.targetObject = targetObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("日志开始");
        Object invoke = method.invoke(targetObject, args);
        System.out.println("日志结束");
        return invoke;
    }
}

(3)创建动态代理对象

import com.qizekj.proxy.Star;
import com.qizekj.proxy.StarInterface;

import java.lang.reflect.Proxy;

/**
 * @author chenqun
 * @date 2023/3/10 20:20
 */
public class JDKProxyTest {
    public static void main(String[] args) {
        //生成代理类文件 在根目录的同级目录,com下
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        //实现了接口的业务类
        Star iservice = new Star();
        //获取Class对象
        Class<?> iserviceClass = iservice.getClass();
        //代理类 实现需要实现InvocationHandler接口,重写invoke方法 传入业务实现类对象
        Dynamicproxy dynamicproxy = new Dynamicproxy(iservice);

        //创建代理类对象
        StarInterface so = (StarInterface) Proxy.newProxyInstance(iserviceClass.getClassLoader(),
                iserviceClass.getInterfaces(), dynamicproxy);
        so.show();

    }
}

运行结果:
在这里插入图片描述

如果这时接口新增或者修改,不再需要去修改代理类。因为其本质是,通过反射+classloader去实现的,所以不依赖于对象。
但是依然有一个问题,就是Proxy.newProxyInstance使用上有一个弊端,就是必须面向接口,如果源对象也就是被代理对象,如果没有实现某个接口,这时就不能用JDK动态代理了,此时 Cglib 可以帮我们解决这个问题。

3.2、Cglib 代理

我们知道一个对象在内存中存储,一般就是在栈或者堆上,那是否有一种方法或者组件,可以直接从内存copy这个对象,实现深克隆,也就是在内存中在copy一个出来。

(1)Cglib代理模式的基本介绍

  • JDK动态代理模式要求目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,这就是Cglib代理。
  • Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展。
  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口,它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截。
  • Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。
  • Cglib由于是基于字节码的,显然这时android就不能使用了,因为android是dex文件,这怎么办?没关系,有dexmaker 和 cglib-for-android 库。

(2)Cglib代理的实现

  • 第一步:引入cglib的文件,目标类使用上述静态方法中的Star。
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.7</version>
</dependency>
  • 第二步:创建方法拦截器
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author chenqun
 * @date 2023/3/10 20:33
 */
public class MyInterceptor implements MethodInterceptor {

    private Object target;

    public MyInterceptor(Object target) {
        this.target = target;
    }

    /**
     *
     * @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("-----cglib before-----");
        // 调用代理类FastClass对象
        Object result =  methodProxy.invokeSuper(o, objects);
//        Object result = methodProxy.invoke(target, objects);
        System.out.println("-----cglib after-----");
        return result;
    }
}
  • 第三步,调用测试。
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

/**
 * @author chenqun
 * @date 2023/3/10 20:37
 */
public class CglibTest {
    public static void main(String[] args) {
        Star star = new Star();

        // 代理类class文件存入本地磁盘方便我们反编译查看源码
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./code");
        // 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        // 设置enhancer对象的父类
        enhancer.setSuperclass(Star.class);
        // 设置enhancer的回调对象
        enhancer.setCallback(new MyInterceptor(star));
        // 创建代理对象
        Star starService = (Star)enhancer.create();
        // 通过代理对象调用目标方法
        starService.personSignature();
        starService.show();
    }
}

运行结果:
在这里插入图片描述

3.3、JDK 和 CGLIB动态代理的区别

JDK代理使用的是反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CGLIB代理使用字节码处理框架asm,对代理对象类的class文件加载进来,通过修改字节码生成子类。

JDK创建代理对象效率较高,执行效率较低;
CGLIB创建代理对象效率较低,执行效率高。

JDK动态代理机制是委托机制,只能对实现接口的类生成代理,通过反射动态实现接口类;
CGLIB则使用的继承机制,针对类实现代理,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,因为是继承机制,不能代理final修饰的类。

JDK代理是不需要依赖第三方的库,只要JDK环境就可以进行代理,需要满足以下要求:
 1.实现InvocationHandler接口,重写invoke()
 2.使用Proxy.newProxyInstance()产生代理对象
 3.被代理的对象必须要实现接口
CGLib 必须依赖于CGLib的类库,需要满足以下要求:
 1.实现MethodInterceptor接口,重写intercept()
 2.使用Enhancer对象.create()产生代理对象

总结:

(1)jdk代理只能对实现了接口的类进行代理,而cglib代理可以对普通类进行代理;
(2)jdk代理是通过反射的方式来实现动态代理,而cglib则是通过为目标类生成一个子类的方式来实现动态代理;
(3)由于cglib代理是为目标类生成了一个子类,并对父类方法进行增强,所以目标类不能用final修饰;

3.4、什么情况下使用JDK或CGLIB代理

(1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP,可以强制使用CGLIB实现AOP。
(2)如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。

3.5、强制使用CGLIB实现AOP的方法

(1)添加CGLIB库(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)
(2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Jenkins从配置到实践

Jenkins从配置到实践 1 持续集成 Continuous integration&#xff08;CI&#xff09; 1.1 什么是持续集成&#xff1f; 持续集成Continuous integration&#xff08;CI&#xff09;是一种软件开发实践&#xff0c;即团队开发成员经常集成他们的工作&#xff0c;通常每个成员…

COLMAP

简介&#xff1a;在使用instant-ngp过程中需要使用COLMAP得到模型的必要输入&#xff0c;比如模型需要的相机外参我们就可以通过COLMAP中的sparse reconstruction稀疏重建得到&#xff1b;而对于depth map深度图我们则需要dense reconstruction稠密重建得到&#xff0c;下面我们…

STM32Cube STM32MP157 M4端CAN通讯实战

1、环境 开发系列&#xff1a;STM32MP157 开发软件&#xff1a;STM32CubeIDE 1.4.0 例程目的&#xff1a;在M4端实现CAN通讯 2、目的 近日&#xff0c;有客户需要在STM32MP157中的M4端实现CAN通讯&#xff0c;我也是初次在M4端编写CAN通讯代码&#xff0c;上网研究了其他人写…

从LeNet到ResNet:深入探索卷积神经网络

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️&#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

论文解读15——LightGBM: A Highly Efficient Gradient Boosting Decision Tree

目录1、文章贡献2、直方图算法Histogram&#xff08;减少分裂点&#xff09;3、基于梯度的单边采样算法GOSS&#xff08;减少样本量&#xff09;4、互斥特征捆绑算法EFB&#xff08;减少特征&#xff09;在上篇中提到&#xff0c;XGBoost算法的局限是它在寻找最优分裂点算法中需…

AidLux AI应用案例悬赏选题 | 电路板表面瑕疵检测

AidLux AI 应用案例悬赏征集活动 AidLux AI 应用案例悬赏征集活动是AidLux推出的AI应用案例项目合作模式&#xff0c;悬赏选题将会持续更新。目前上新的选题涉及泛边缘、机器人、工业检测、车载等领域&#xff0c;内容涵盖智慧零售、智慧社区、智慧交通、智慧农业、智能家居等…

正点原子裸机开发之C语言点灯程序

一. 简介 本文针对 IMX6ULL 的裸机开发的&#xff08;即不带Linux操作系统的开发&#xff09;。 主要分两部分的工作&#xff1a; 1. 配置 C语言运行环境 2. C 语言编写及运行 二. 配置C语言运行环境 配置 C 语言运行环境的工作分 三部分。如下&#xff1a; 1. 设置…

Nginx配置实例-反向代理案例一

实现效果&#xff1a;使用nginx反向代理&#xff0c;访问 www.suke.com 直接跳转到本机地址127.0.0.1:8080 一、准备工作 Centos7 安装 Nginxhttps://liush.blog.csdn.net/article/details/125027693 1. 启动一个 tomcat Centos7安装JDK1.8https://liush.blog.csdn.net/arti…

YonBuilder 应用构建全新入门指南

用友开发者中心以 YonBuilder 低代码开发为核心&#xff0c;提供可视化 低代码 全代码的一站式开发能力&#xff0c;企业组织和个人开发者可实现业务应用的快速开发。YonBuilder 基于用友 BIP 强大的中台支撑能力&#xff0c;在元数据驱动和运行框架的统一模型架构下&#xf…

Ethercat学习-GD32以太网学习

文章目录1、GD32F4以太网简介2、以太网模框图简介3、以太网主要模块介绍SMI接口RMII接口与MII接口DMA控制器4、以太网配置流程5、其他1、GD32F4以太网简介 GD32F4系列以太网模块包含10/100Mbps以太网MAC&#xff0c;数据的收发都通过DMA进行操作&#xff0c;支持MII&#xff0…

一个C#开发的开源的快速启动工具

更多开源项目请查看&#xff1a;一个专注推荐.Net开源项目的榜单 平常计算机安装软件比较多、或者工作涉及的文件比较多&#xff0c;很多人都会直接放在桌面&#xff0c;一方面不安全&#xff0c;还不容易查找&#xff0c;这时候我们往往&#xff0c;都会放在其他硬盘内&#x…

springboot项目yml文件中${}的使用

作用 项目启动时可以灵活的通过修改环境变量来替换配置中的值&#xff0c;如果没有传该环境变量时&#xff0c;就是用默认值&#xff1b; 格式&#xff1a;${自定义参数名:默认值} 代码举例&#xff0c;已开启应用的端口号为例&#xff1a; server:port: ${SERVER_PORT:960…

AI+人类,实现高效网络安全

导语 聊天机器人和生成式人工智能&#xff08;如 ChatGPT&#xff09;突然成为主流让很多人感到担忧。很多人开始担忧&#xff0c;人工智能取代人的时代已经到来。 幸运的是&#xff0c;事实并非如此。 更有可能的情况是&#xff0c;人类将与 AI 合作创建工作角色的混合模型。…

Mercedes-Benz EDI需求分析

作为奔驰的仓储服务提供商&#xff0c;企业A需要借助EDI实现仓储流程的自动化。奔驰将车存放在企业A处&#xff0c;由企业A将货物提供给4S店。整体业务流程中将通过EDI系统来进行业务数据的传输&#xff0c;今天的文章主要从奔驰EDI需求概览以及EDI项目业务流程两方面展开介绍。…

646. 最长数对链——【Leetcode每日刷题】

646. 最长数对链 给你一个由 n 个数对组成的数对数组 pairs &#xff0c;其中 pairs[i][lefti,righti]pairs[i] [left_i, right_i]pairs[i][lefti​,righti​] 且lefti<rightileft_i < right_ilefti​<righti​。 现在&#xff0c;我们定义一种 跟随 关系&#xff…

MySQL查询索引原则

文章目录 等值匹配原则最左前缀匹配原则范围查找规则等值匹配+范围查找Order By + limit 优化分组查询优化总结MySQL 是如何帮我们维护非主键索引的等值匹配原则 我们现在已经知道了如果是【主键索引】,在插入数据的时候是根据主键的顺序依次往后排列的,一个数据页不够就会分…

保姆级使用PyTorch训练与评估自己的MobileViT网络教程

文章目录前言0. 环境搭建&快速开始1. 数据集制作1.1 标签文件制作1.2 数据集划分1.3 数据集信息文件制作2. 修改参数文件3. 训练4. 评估5. 其他教程前言 项目地址&#xff1a;https://github.com/Fafa-DL/Awesome-Backbones 操作教程&#xff1a;https://www.bilibili.co…

2023最新ELK日志平台(elasticsearch+logstash+kibana)搭建

去年公司由于不断发展&#xff0c;内部自研系统越来越多&#xff0c;所以后来搭建了一个日志收集平台&#xff0c;并将日志收集功能以二方包形式引入自研系统&#xff0c;避免每个自研系统都要建立一套自己的日志模块&#xff0c;节约了开发时间&#xff0c;管理起来也更加容易…

【UE4 RTS游戏】07-控件蓝图显示当前游戏时间

效果步骤新建一个文件夹&#xff0c;命名为“Interfaces”&#xff0c;在该文件夹内新建一个蓝图接口&#xff0c;命名为“I_HUD”打开“Interfaces”&#xff0c;默认的函数名改为“SetClock”&#xff0c;添加一个输入&#xff0c;命名为“Time”&#xff0c;整数数组类型。添…

一套Java语言的云HIS医院信息管理系统SaaS源码

这是一套优秀的云His医院管理系统&#xff0c;它区别于普通的HIS系统&#xff0c;它通过云端管理整个HIS业务 一、HIS使用技术框架&#xff1a;自主研发&#xff0c;系统完全开源。 1、前端&#xff1a;AngularNginx 2、后台&#xff1a;JavaSpring&#xff0c;SpringBoot&…