Java反射(三)
目录
1.反射与代理设计模式
2.反射与Annotation
3.自定义Annotation
4.Annotation整合工厂设计模式和代理设计模式
1.反射与代理设计模式
代理模式是指通过业务真实类实现业务接口,再通过设置代理类创建业务真实类子类从而间接访问业务真实类。但是这存在一个弊端,如果有1000个业务接口,对应的业务就需要实例化1000个对象,如下
代理设计模式
package Example1709;
//业务接口实现发送消息
interface Message{public void send();
}
//业务接口真实实现
class MessageReal implements Message{@Overridepublic void send() {System.out.println("传输信息Message");}
}
//代理类
class Proxy{
// 通过实例化子类进行间接实现功能private MessageReal real = new MessageReal();public void getMessage() {real.send();}
}
public class javaDemo {public static void main(String[] args) {Proxy p = new Proxy();p.getMessage();}
}
可以看到代理类里面创建了Message实例对象,通过代理设计就可以实现客户端无需创建相应对象就能调用其中方法,只需要创建代理类即可。但是如果业务非常多,而我仅需要其中一个业务功能,那么代理类创建的对象将浪费。所以可以通过反射实现动态代理
java中特地有InvocationHandle接口实现动态代理,只需要让代理类实现该接口并且覆写其中方法invoke()方法调用 就能实现动态代理
案例代码:
package Example1710;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//业务接口
@FunctionalInterface
interface Message{public void send();
}
interface Connect{public boolean connect();
}
interface Close{public void close();
}
//业务实现类
class MessageReal implements Message{@Overridepublic void send() {System.out.println("输出信息");}
}
class ConnectReal implements Connect{@Overridepublic boolean connect() {System.out.println("连接成功");return true;}
}
class closeReal implements Close{@Overridepublic void close() {System.out.println("断开连接");}
}
//代理类需要实现InvocationHandle接口
class MessageProxy implements InvocationHandler {private Object object;public Object bind(Object object){this.object = object;return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),this);}
//覆写InvocationHandle接口中的invoke方法实现方法的调用@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("当调用方法时候自动执行这个函数");Object returnData = null;returnData = method.invoke(this.object,args);return returnData;}
}
public class javaDemo {public static void main(String[] args) {
// 向上转型实现需要的代理类Connect con = (Connect) new MessageProxy().bind(new ConnectReal());con.connect();}
}
2.反射与Annotation
在java.lang.reflect中通过AccesibleObject类可以获取Annotation(注解)。
AccessibleObject类获取Annotation的方法:
方法名 | 返回类型 | 描述 |
---|---|---|
getAnnotations() | Annotation[] | 返回直接存在于此元素上的所有注解。 |
getAnnotation(Class<T> annotationClass) | <T extends Annotation> T | 如果此元素上存在指定类型的注解,则返回该注解;否则返回 null 。 |
getDeclaredAnnotations() | Annotation[] | 返回直接存在于此元素上的所有已声明注解。 |
getDeclaredAnnotation(Class<T> annotationClass) | <T extends Annotation> T | 如果此元素上存在指定类型的注解,则返回该注解;否则返回 null 。 |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | boolean | 如果此元素上存在指定类型的注解,则返回 true ;否则返回 false 。 |
案例代码:
package Example1711;import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;@Deprecated
@FunctionalInterface
interface Face{void face();
}@SuppressWarnings("serial")
class Test implements Face, Serializable {@Overridepublic void face() {System.out.println("No face");}
}public class javaDemo {public static void main(String[] args) {Annotation tation[] = Face.class.getAnnotations();for (Annotation temp:tation) {System.out.println(temp);}Annotation taion2[] = Test.class.getAnnotations();for (Annotation temp:taion2) {System.out.println(temp);}}
}
上图中可以发现输出的只有interface接口的注解Annotation 但是类的上面的还有类内部的@Override却没有输出。原因在于Annotation的定义范围有关
下面任意拆出一个注解比如@FunctionInterface,分析源码
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FunctionalInterface{}
可以看到其中还定义的范围是Rentention,这里是RUNTIME意思是运行,
@Retention
注解用于指定注解的保留策略,有三个可选值:
RetentionPolicy.SOURCE
:注解只保留在源代码中,编译时会被忽略。RetentionPolicy.CLASS
:注解保留在编译后的字节码文件中,但在运行时无法获取到。RetentionPolicy.RUNTIME
:注解保留在编译后的字节码文件中,并且可以在运行时通过反射获取到。
问1:Connect con = (Connect) new MessageProxy().bind(new ConnectReal());其中new MessageProxy是创建代理对象,那return Proxy.newProxyInstance(object.getClass().getClassLoader(),this)也是创建代理对象,这不是重复了操作吗,如果和new MessageProxy作用不一样的话,那么newProxyInstance方法有什么作用
3.自定义Annotation
开发者可以根据自己的需要自定义Annotation,其中定义Annotaion时候需要使用@interface进行标记,同时通过@Target定义范围
Annotation操作范围:
元素类型 | @Target 取值 | 描述 |
---|---|---|
类或接口 | ElementType.TYPE | 应用于类、接口或枚举类型。 |
字段 | ElementType.FIELD | 应用于字段(成员变量)。 |
方法 | ElementType.METHOD | 应用于方法。 |
构造方法 | ElementType.CONSTRUCTOR | 应用于构造方法。 |
方法参数 | ElementType.PARAMETER | 应用于方法的参数。 |
局部变量 | ElementType.LOCAL_VARIABLE | 应用于局部变量。 |
注解 | ElementType.ANNOTATION_TYPE | 应用于注解类型。 |
包 | ElementType.PACKAGE | 应用于包声明。 |
泛型类型参数 | ElementType.TYPE_PARAMETER | 应用于泛型类型参数。 |
泛型类型参数的边界 | ElementType.TYPE_USE | 应用于泛型类型参数的使用处,例如类型转换、instanceof 表达式等 |
类型导入声明(Java 9+) | ElementType.TYPE_IMPORT_DECLARATION | 应用于类型导入声明。 |
模块导入声明(Java 9+) | ElementType.MODULE_IMPORT_DECLARATION | 应用于模块导入声明。 |
类型使用 | ElementType.TYPE_USE 或 ElementType.TYPE_PARAMETER | 应用于类型使用或泛型类型参数。 |
自定义案例代码:
如果@DefaultInterface对象有必要的数据传入,可以在@interface下设置value值。
package Example1713;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface useMessage{public String title();public String value();public String Default()default "默认值,如果调用则返回这个默认";
}class Message {@useMessage(title="Annotation的使用标题",value = "value的值")public void send(String str){System.out.println("输出信息"+str);}
}
public class javaDemo {public static void main(String[] args) throws Exception{String str;
// 调用方法Method mehod = Message.class.getMethod("send", String.class);
// 获取指定的AnnotationuseMessage msg = mehod.getAnnotation(useMessage.class);str = msg.Default();
// 实现方法调用mehod.invoke(Message.class.getDeclaredConstructor().newInstance(),str);str = msg.title();mehod.invoke(Message.class.getDeclaredConstructor().newInstance(),str);str = msg.value();mehod.invoke(Message.class.getDeclaredConstructor().newInstance(),str);}
}
4.Annotation整合工厂设计模式和代理设计模式
使用Annotation进行开发时候最大的特点就是可以将相应的配置信息写入Annotation后,在项目启动的时候就能够通过反射获取到相应的Annotation定义并操作
以下案例实现了通过Annotation整合工厂设计模式和代理设计模式
案例代码:
package Example1714;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;//业务接口
interface Message{public void send(String msg);
}
//业务实现类
class NetMessageImp implements Message{@Overridepublic void send(String msg) {System.out.println("通过网络进行发送消息"+msg);}
}
class CloudMessageImp implements Message{@Overridepublic void send(String msg) {System.out.println("通过云网络进行发送消息"+msg);}
}
//定义工厂类
class Factory{
// 私有化工厂类无法在外部实例化private Factory(){};
// 通过getInstance获取实例化的对象public static<T> T getInstance(Class<T> clazz){try {return (T) new MessageProxy().bind(clazz.getDeclaredConstructor().newInstance());} catch (Exception e){e.printStackTrace();return null;}}
}
//设置自定义Annotation
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface RealInstance {public Class<?> claszz();}@RealInstance(claszz = NetMessageImp.class)
class MessageService{private Message msg;public MessageService(){
// getAnnotation需要对应的classRealInstance cls = MessageService.class.getAnnotation(RealInstance.class);this.msg = (Message) Factory.getInstance(cls.claszz());}public void send(String msg){this.msg.send(msg);}
}
//设置代理类
class MessageProxy implements InvocationHandler{private Object target;
// 绑定并创建指定对象public Object bind(Object target){this.target = target;return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);}
// 设置连接函数public boolean connect(){System.out.println("连接成功");return true;}
// 设置关闭函数public void close(){System.out.println("断开连接");}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (this.connect()){return method.invoke(this.target,args);}}catch (Exception e){e.printStackTrace();}finally {this.close();}return null;}
}
public class javaDemo {public static void main(String[] args) {MessageService instance = new MessageService();instance.send("我去,这东西是真的复杂啊,需要好好仔细读完啊");}
}
Message
接口是一个业务接口,定义了发送消息的方法。NetMessageImp
和CloudMessageImp
是两个实现了Message
接口的具体业务类,分别通过网络和云网络发送消息。Factory
是一个工厂类,通过使用代理模式创建并返回代理对象的实例。RealInstance
是一个自定义注解,用于标记需要被代理的具体业务类。MessageService
是一个具有注解的类,其中msg
字段是通过工厂类创建的代理对象,用于发送消息。MessageProxy
是一个实现了InvocationHandler
接口的代理类,负责在发送消息前后进行额外的处理,比如连接和关闭连接。- 在主函数中,创建
MessageService
实例并调用send
方法发送消息。
问1:public static<T> T getInstance(Class<T> clazz)为什么要用<T>,作为泛型方法是如何使用的?
public static <T> T getInstance(Class<T> clazz)
中的<T>
是泛型声明,它允许我们在方法中使用泛型类型。其中,T
是一个类型参数,表示方法的返回类型和传入参数的类型。通过在方法声明中使用泛型,我们可以在调用时指定具体的类型,并在编译时进行类型检查。
问2:在这段整合工厂设计和代理设计时候,工厂类和代理类分别担任了什么样的角色?他们完成了什么职责,为什么要加入MessageService?
- 工厂类(
Factory
)的角色是创建并返回代理对象的实例。它的职责是根据传入的业务接口类型,使用代理模式创建该接口的代理对象。工厂类的目的是为了提供一种通用的方式来创建代理对象。- 代理类(
MessageProxy
)的角色是实现了InvocationHandler
接口的代理类。它的职责是在代理对象的方法调用前后进行额外的处理。在这个例子中,它负责在发送消息前后进行连接和关闭连接的操作。MessageService
是一个业务类,其中的msg
字段通过工厂类创建的代理对象,用于发送消息。MessageService
通过使用代理对象,可以在发送消息的过程中加入额外的处理逻辑,而无需直接引用具体的业务实现类。
问3:Proxy.newProxyInstance()方法如何使用,该怎么传入参数?
Proxy.newProxyInstance()
方法用于创建代理对象。它接受三个参数:
ClassLoader loader
:类加载器,用于在运行时加载代理类。一般可以使用业务接口的类加载器。Class<?>[] interfaces
:代理类要实现的接口数组。代理对象将实现这些接口,并将方法调用委托给InvocationHandler
的invoke()
方法。InvocationHandler h
:代理对象在方法调用时的处理器,需要实现InvocationHandler
接口
问4:方法调用method.invoke()如何传入参数?
问4:
method.invoke()
方法用于调用代理对象的方法。它接受两个参数:
Object obj
:方法所属的对象,即代理对象。Object... args
:方法的参数数组。
问5:在设计模式中,工厂设计模式,代理设计模式,业务处理类的作用?
- 在设计模式中,工厂模式的作用是将对象的创建过程封装起来,并且提供一个统一的接口来获取对象的实例。通过使用工厂类,可以实现对象的创建和管理的解耦,使得代码更加灵活和可维护。
- 代理模式的作用: 代理模式充当了客户端和实际业务对象之间的中介角色,为客户端提供一种间接访问对象的方式。具体来说,代理类封装了实际业务对象并提供了一个类似的接口,使得客户端可以通过代理类来访问实际业务对象。代理类还可以在访问被代理对象之前或之后执行额外的逻辑(例如验证、缓存、日志记录等)。代理模式的优点是它可以在不修改客户端的情况下对实际业务对象进行控制和扩展。
- 业务处理类的作用: 业务处理类是实际执行业务逻辑的类。它是根据特定需求实现业务功能的地方。在代理模式中,业务处理类是被代理的真实对象。它定义了代理类需要代理的具体业务逻辑,代理类会将请求传递给业务处理类并最终由业务处理类完成实际的业务操作。
相关文章:

Java反射(三)
目录 1.反射与代理设计模式 2.反射与Annotation 3.自定义Annotation 4.Annotation整合工厂设计模式和代理设计模式 1.反射与代理设计模式 代理模式是指通过业务真实类实现业务接口,再通过设置代理类创建业务真实类子类从而间接访问业务真实类。但是这存在一个弊…...

ansible-playbook roles编写lnmp剧本
目录 集中式编写lnmp剧本 执行 分布式编写lnmp剧本 一定要设置ssh免交互 nginx mysql php 执行 集中式编写lnmp剧本 vim /etc/ansible/lnmp.yml - name: lnmp playhosts: dbserversremote_user: roottasks:- name: perpare condifurecopy: src/etc/yum.repos.d/nginx.r…...

相机可用性变化监听AvailabilityCallback流程分析
相机可用性变化监听及流程分析 一、接口说明 相机可用性变化监听可以通过CameraManager中的接口registerAvailabilityCallback()来设置回调,接口如下: /** *注册一个回调以获得有关相机设备可用性的通知。 * *<p>再次注册相同的回调将用提供…...

使用Python多线程实现生产者消费者模型
“Talk is cheap, show me the code.” 废话不多说,直接上代码: """ 生产者消费者模型 Python实现 """ import queue import threading import random import timeclass ConsProd:# 队列参数_que None # 队列# 生产者…...

Notepad++工具通过正则表达式批量替换内容
1.每行末尾新增特定字符串 CtrlH弹出小窗口;查找目标输入$,替换为输入特定字符串;选中循环查找,查找模式选正则表达式;最后点击全部替换 2.每行行首新增特定字符串 CtrlH弹出小窗口;查找目标输入^&…...
从零构建深度学习推理框架-3 手写算子relu
Relu介绍: relu是一个非线性激活函数,可以避免梯度消失,过拟合等情况。我们一般将thresh设为0。 operator类: #ifndef KUIPER_COURSE_INCLUDE_OPS_OP_HPP_ #define KUIPER_COURSE_INCLUDE_OPS_OP_HPP_ namespace kuiper_infer {…...

想做上位机,学C#还是QT?
学习C#还是Qt,取决于你的具体需求和偏好。 如果你计划开发跨平台的桌面应用程序,并且希望使用一种更轻量级、直观的界面框架,那么Qt可能是一个不错的选择。Qt是一个功能丰富且成熟的跨平台框架,支持多种开发语言(包括…...

Ansible —— playbook 剧本
Ansible —— playbook 剧本 一、playbook的概述1.playbook简介2.什么是Ansible playbook剧本?3.Ansible playbook剧本的特点4.如何使用Ansible playbook剧本?5.playbooks 本身由以下各部分组成 二、playbook示例1.运行playbook2.定义、引用变量3.指定远…...

ARM寻址方式
寻址方式 寻址方式是根据指令中给出的地址码字段来实现寻找操作数地址的方式,ARM中有以下8种基本的寻址方式。 1、寄存器寻址 将寄存器中的值作为操作数,指令中的地址码字段是寄存器编号。 MOV R1,R2 ;R1 R2 ADD R0,R1,R2 ;R0 R1 R22、立即寻…...

【JAVA】String ,StringBuffer 和 StringBuilder 三者有何联系?
个人主页:【😊个人主页】 系列专栏:【❤️初识JAVA】 文章目录 前言StringBufferStringBuffer方法 StringBuilderStringBuilder方法 String ,StringBuffer 和 StringBuilder的区别String和StringBuffer互相转换 前言 在之前的文章…...

关于计数以及Index返回订单号升级版(控制字符长度,控制年月标记)
数据库表操作: EXEC sys.sp_dropextendedproperty nameNName , level0typeNSCHEMA,level0nameNdbo, level1typeNTABLE,level1nameNSetNoIndexGOEXEC sys.sp_dropextendedproperty nameNMS_Description , level0typeNSCHEMA,level0nameNdbo, level1typeNTABLE,level…...

【计算机网络】11、网桥(bridge)、集线器(hub)、交换机(switch)、路由器(router)、网关(gateway)
文章目录 一、网桥(bridge)二、集线器(hub)三、交换机(switch)四、路由器(router)五、网关(gateway) 对于hub,一个包过来后,直接将包转发到其他口。 对于桥&…...

第九篇-自我任务数据准备
格式化自我意识数据用于ChatGLM微调 准备数据源 https://github.com/hiyouga/ChatGLM-Efficient-Tuning cd data self_cognition.json代码self_process.py #!/usr/bin/python # -*- coding: UTF-8 -*- # 读取self_cognition自我认知解析并写入转换新文件import json# 读取se…...

2023.8.1号论文阅读
文章目录 MCPA: Multi-scale Cross Perceptron Attention Network for 2D Medical Image Segmentation摘要本文方法实验结果 SwinMM: Masked Multi-view with SwinTransformers for 3D Medical Image Segmentation摘要本文方法实验结果 MCPA: Multi-scale Cross Perceptron Att…...

webpack优化前端框架性能
webpack优化目的 webpack优化目的1. 提升开发体验提升开发体验使用 SourceMap 2. 提升打包构建速度提升打包构建速度(开发模式)提升打包速度 oneOf提升打包速度 include(包含)/exclude(排除)提升第二次打包…...

Unity UGUI的Outline(描边)组件的介绍及使用
Unity UGUI的Outline(描边)组件的介绍及使用 1. 什么是Outline(描边)组件? Outline(描边)组件是Unity UGUI中的一种特效组件,用于给UI元素添加描边效果。通过设置描边的颜色、宽度和模糊程度,可以使UI元素在视觉上更加突出。 2. Outline(描…...

爆改vue3 setup naiveui可编辑table
使用naiveui官网的可编辑table总是报错,所以手写了一个 思路:table数据数组unitMsgArr对应一个布尔的数组isEditArr ,点击table可编辑的行数据的时候,更改对应的isEdit为true,此时渲染组件EditCom,在EditC…...

功率放大器的种类有哪三种类型
功率放大器是一种能将输入信号转换为更高功率输出的电子设备。在电子工程和音频领域中,功率放大器通常被分为三种类型:A类、B类和AB类。下面安泰电子将详细介绍这三种类型的功率放大器及其特点。 A类功率放大器 A类功率放大器是一种基本的线性功率放大器…...

HDFS 分布式存储 spark storm HBase
HDFS 分布式存储 spark storm HBase 分布式结构 master slave name node client 负责文件的拆分 128MB 3份 data node MapReduce 分布式计算 离线计算 2.X之前 速度比较慢 对比spark 编程思想 Map 分 Reduce 合 hadoop streaming Mrjob Yarn 资源管理 cpu 内存 MapReduc…...

Vue3文字实现左右和上下滚动
可自定义设置以下属性: 滚动文字数组(sliderText),类型:Array<{title: string, link?: string}>,必传,默认[] 滚动区域宽度(width),类型:…...

Docker Sybase修改中文编码
镜像:datagrip/sybase 镜像默认用户名sa,密码myPassword,服务名MYSYBASE 1.进入容器 docker exec -it <container_name> /bin/bash2.加载Sybase环境变量 source /opt/sybase/SYBASE.sh3.查看是否安装了中文字符集 isql -Usa -PmyP…...

【SpringCloud Alibaba】(六)使用 Sentinel 实现服务限流与容错
今天,我们就使用 Sentinel 实现接口的限流,并使用 Feign 整合 Sentinel 实现服务容错的功能,让我们体验下微服务使用了服务容错功能的效果。 因为内容仅仅围绕着 SpringCloud Alibaba技术栈展开,所以,这里我们使用的服…...

mysql的主从复制
1.主从复制的原理 主从复制的原理是通过基于日志的复制方式实现数据的同步。当主服务器上发生数据变更时,会将这些变更写入二进制日志(Binary Log)中。从服务器通过连接到主服务器,请求从主服务器获取二进制日志,并将…...

【Golang 接口自动化03】 解析接口返回XML
目录 解析接口返回数据 定义结构体 解析函数: 测试 优化 资料获取方法 上一篇我们学习了怎么发送各种数据类型的http请求,这一篇我们来介绍怎么来解析接口返回的XML的数据。 解析接口返回数据 定义结构体 假设我们现在有一个接口返回的数据resp如…...

Java+bcprov库实现对称和非对称加密算法
BouncyCastle,即BC,其是一款开源的密码包,包含了大量的密码算法。 本篇主要演示BC库引入,对称加密算法AES、SM4和 非对称加密EC算法的简单实现,以下是实现过程。 一、将BC添加到JRE环境 前提:已安装JRE环…...

国内最大Llama开源社区发布首个预训练中文版Llama2
"7月31日,Llama中文社区率先完成了国内首个真正意义上的中文版Llama2-13B大模型,从模型底层实现了Llama2中文能力的大幅优化和提升。毋庸置疑,中文版Llama2一经发布将开启国内大模型新时代! | 全球最强,但中文短板…...

Qt应用开发(基础篇)——滑块类 QSlider、QScrollBar、QDial
目录 一、前言 二、QAbstractSlider类 1、invertedAppearance 2、invertedControls 3、maximum 4、minimum 5、orientation 6、pageStep 7、singleStep 8、sliderDown 9、tracking 10、sliderPosition 11、value 12、信号 三、QDial类 1、notchSize 2、notchTa…...

【3-D深度学习:肺肿瘤分割】创建和训练 V-Net 神经网络,并从 3D 医学图像中对肺肿瘤进行语义分割研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

MongoDB文档--架构体系
阿丹: 在开始学习先了解以及目标知识的架构体系。就能事半功倍。 架构体系 MongoDB的架构体系由以下几部分组成: 存储结构:MongoDB采用文档型存储结构,一个数据库包含多个集合,一个集合包含多个文档。存储形式&#…...

GEE学习03-Geemap配置与安装,arcgis pro自带命令提示符位置等
跟着吴秋生老师的视频开展的学习,首先购买了云,用来设置全局。 1、尝试使用arcgis pro自带的conda conda env list查看电脑上环境,我自己电脑上有三个环境,使用的arcgis pro python克隆的环境作为的默认的环境 但是这样的前提…...