Java反射(三)

news2024/9/23 1:38:56

目录

1.反射与代理设计模式

2.反射与Annotation

3.自定义Annotation

4.Annotation整合工厂设计模式和代理设计模式


1.反射与代理设计模式

代理模式是指通过业务真实类实现业务接口,再通过设置代理类创建业务真实类子类从而间接访问业务真实类。但是这存在一个弊端,如果有1000个业务接口,对应的业务就需要实例化1000个对象,如下

代理设计模式

package Example1709;
//业务接口实现发送消息
interface Message{
    public void  send();
}
//业务接口真实实现
class MessageReal implements Message{
    @Override
    public 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{
    @Override
    public void send() {
        System.out.println("输出信息");
    }
}
class ConnectReal implements Connect{
    @Override
    public boolean connect() {
        System.out.println("连接成功");
        return true;
    }
}
class closeReal implements Close{
    @Override
    public 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方法实现方法的调用
    @Override
    public 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 {
    @Override
    public 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:注解保留在编译后的字节码文件中,并且可以在运行时通过反射获取到。

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_USEElementType.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);
//        获取指定的Annotation
        useMessage 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{
    @Override
    public void send(String msg) {
        System.out.println("通过网络进行发送消息"+msg);
    }
}
class CloudMessageImp implements Message{
    @Override
    public 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需要对应的class
        RealInstance 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("断开连接");
    }
    @Override
    public 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("我去,这东西是真的复杂啊,需要好好仔细读完啊");
    }
}

  1. Message接口是一个业务接口,定义了发送消息的方法。
  2. NetMessageImpCloudMessageImp是两个实现了Message接口的具体业务类,分别通过网络和云网络发送消息。
  3. Factory是一个工厂类,通过使用代理模式创建并返回代理对象的实例。
  4. RealInstance是一个自定义注解,用于标记需要被代理的具体业务类。
  5. MessageService是一个具有注解的类,其中msg字段是通过工厂类创建的代理对象,用于发送消息。
  6. MessageProxy是一个实现了InvocationHandler接口的代理类,负责在发送消息前后进行额外的处理,比如连接和关闭连接。
  7. 在主函数中,创建MessageService实例并调用send方法发送消息。

问1:public static<T> T getInstance(Class<T> clazz)为什么要用<T>,作为泛型方法是如何使用的?

public static <T> T getInstance(Class<T> clazz)中的<T>是泛型声明,它允许我们在方法中使用泛型类型。其中,T是一个类型参数,表示方法的返回类型和传入参数的类型。通过在方法声明中使用泛型,我们可以在调用时指定具体的类型,并在编译时进行类型检查。

问2:在这段整合工厂设计和代理设计时候,工厂类和代理类分别担任了什么样的角色?他们完成了什么职责,为什么要加入MessageService?

  1. 工厂类(Factory)的角色是创建并返回代理对象的实例。它的职责是根据传入的业务接口类型,使用代理模式创建该接口的代理对象。工厂类的目的是为了提供一种通用的方式来创建代理对象。
  2. 代理类(MessageProxy)的角色是实现了InvocationHandler接口的代理类。它的职责是在代理对象的方法调用前后进行额外的处理。在这个例子中,它负责在发送消息前后进行连接和关闭连接的操作。
  3. MessageService是一个业务类,其中的msg字段通过工厂类创建的代理对象,用于发送消息。MessageService通过使用代理对象,可以在发送消息的过程中加入额外的处理逻辑,而无需直接引用具体的业务实现类。

问3:Proxy.newProxyInstance()方法如何使用,该怎么传入参数?

Proxy.newProxyInstance()方法用于创建代理对象。它接受三个参数:

  • ClassLoader loader:类加载器,用于在运行时加载代理类。一般可以使用业务接口的类加载器。
  • Class<?>[] interfaces:代理类要实现的接口数组。代理对象将实现这些接口,并将方法调用委托给InvocationHandlerinvoke()方法。
  • InvocationHandler h:代理对象在方法调用时的处理器,需要实现InvocationHandler接口

问4:方法调用method.invoke()如何传入参数?

问4:method.invoke()方法用于调用代理对象的方法。它接受两个参数:

  • Object obj:方法所属的对象,即代理对象。
  • Object... args:方法的参数数组。

问5:在设计模式中,工厂设计模式,代理设计模式,业务处理类的作用?

  1. 在设计模式中,工厂模式的作用是将对象的创建过程封装起来,并且提供一个统一的接口来获取对象的实例。通过使用工厂类,可以实现对象的创建和管理的解耦,使得代码更加灵活和可维护。
  2. 代理模式的作用: 代理模式充当了客户端和实际业务对象之间的中介角色,为客户端提供一种间接访问对象的方式。具体来说,代理类封装了实际业务对象并提供了一个类似的接口,使得客户端可以通过代理类来访问实际业务对象。代理类还可以在访问被代理对象之前或之后执行额外的逻辑(例如验证、缓存、日志记录等)。代理模式的优点是它可以在不修改客户端的情况下对实际业务对象进行控制和扩展。
  3. 业务处理类的作用: 业务处理类是实际执行业务逻辑的类。它是根据特定需求实现业务功能的地方。在代理模式中,业务处理类是被代理的真实对象。它定义了代理类需要代理的具体业务逻辑,代理类会将请求传递给业务处理类并最终由业务处理类完成实际的业务操作。

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

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

相关文章

助你丝滑过度到 Vue3 组合式Api的优势新的组件 ②⑧

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; VUE3~TS &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f449;…

centos系统离线安装k8s v1.23.9最后一个版本并部署服务,docker支持的最后一个版本

注意&#xff1a;我这里的离线安装包是V1.23.9. K8S v1.23.9离线安装包下载&#xff1a; 链接&#xff1a;https://download.csdn.net/download/qq_14910065/88143546 这里包括离线安装所有的镜像&#xff0c;kubeadm&#xff0c;kubelet 和kubectl&#xff0c;calico.yaml&am…

微信小程序页面传值为对象[Object Object]详解

微信小程序页面传值为对象[Object Object]详解 1、先将传递的对象转化为JSON字符串拼接到url上2、在接受对象页面进行转译3、打印结果 1、先将传递的对象转化为JSON字符串拼接到url上 // info为对象 let stationInfo JSON.stringify(info) uni.navigateTo({url: /pages/statio…

引入联合GraphQL以解决系统架构中的问题

随着使用需求的增长&#xff0c;用户群的扩大以及新功能的引入&#xff0c;让工程师按照业务的主要领域进行组织变得不可避免。当这些领域在单个实体&#xff08;如类、服务、应用程序或代码库&#xff09;的层面变得过于庞大难以管理时&#xff0c;引入联合GraphQL成为优化系统…

任务通知

Q: 什么是任务通知&#xff1f; A: FreeRTOS 从版本 V8.2.0 开始提供任务通知这个功能&#xff0c;每个任务都有一个 32 位的通知值。按照 FreeRTOS 官方的说法&#xff0c;使用消息通知比通过二进制信号量方式解除阻塞任务快 45%&#xff0c; 并且更加省内存&#xff08;无需…

图为科技加入深圳市智能交通行业协会 ,打 …

图为科技加入深圳市智能交通行业协会&#xff0c;打造智能交通新生态&#xff01; 交通是国民经济发展的“大动脉”&#xff0c;交通拥堵、事故频发等问题不仅影响了人们的出行体验&#xff0c;也对经济的发展产生了负面影响。安全、高效、便捷的出行&#xff0c;一直是人们的…

策略路由实现多ISP接入Internet

组网需求&#xff1a; 企业分别从ISP1和ISP2租用了一条链路 PC3用户上网访问Server1时走ISP1PC4用户上网访问Server1时走ISP2 拓扑图 一、ISP1 运营商 R1路由器 <Huawei>sys [Huawei]sys R1 [R1]un in en[R1]int g0/0/0 [R1-GigabitEthernet0/0/0]ip addr 2.2.2.2 2…

【电影推荐系统】数据爬取、数据加载进MongoDB数据库

概览 本篇主要介绍数据来源、数据加载进数据库过程 1 数据获取 使用Scrapy爬取豆瓣电影数据&#xff0c;然后利用movielens数据集来造一份rating数据。 1.1 数据集获取 数据集获取&#xff1a;选取movielens 数据集&#xff1a;movielens官网数据集包括&#xff1a;movies…

【计算机网络】网络基础(上)

文章目录 1. 网络发展认识协议 2.网络协议初识协议分层OSI七层模型 | TCP/IP网络传输基本流程情况1&#xff1a;同一个局域网(子网)数据在两台通信机器中如何流转协议报头的理解局域网通信原理(故事版本)一般原理数据碰撞结论 1. 网络发展 计算工作是不可能一个科学家搞出来的…

机器学习(一)---概述

文章目录 1.人工智能、机器学习、深度学习2.机器学习的工作流程2.1 获取数据集2.2 数据基本处理2.3 特征工程2.3.1 特征提取2.3.2 特征预处理2.3.3 特征降维 2.4 机器学习2.5 模型评估 3.机器学习的算法分类3.1 监督学习3.1.1 回归问题3.1.2 分类问题 3.2 无监督学习 1.人工智能…

FastAPI 教程、结合vue实现前后端分离

英文版文档&#xff1a;https://fastapi.tiangolo.com/ 中文版文档&#xff1a;https://fastapi.tiangolo.com/zh/ 1、FastAPI 教程 简 介 FastAPI 和 Sanic 类似&#xff0c;都是 Python 中的异步 web 框架。相比 Sanic&#xff0c;FastAPI 更加的成熟、社区也更加的活跃。 …

8.1作业

文件IO函数实现拷贝文件。子进程先拷贝后半部分&#xff0c;父进程再拷贝前半部分&#xff0c;允许使用sleep函数 #include<stdio.h> #include<string.h> #include<stdlib.h> #include<head.h> int main(int argc, const char *argv[]) {pid_t cpidfo…

构建语言模型:BERT 分步实施指南

学习目标 了解 BERT 的架构和组件。了解 BERT 输入所需的预处理步骤以及如何处理不同的输入序列长度。获得使用 TensorFlow 或 PyTorch 等流行机器学习框架实施 BERT 的实践知识。了解如何针对特定下游任务(例如文本分类或命名实体识别)微调 BERT。为什么我们需要 BERT? 正…

保护云数据库实用指南

在数字化转型时代&#xff0c;越来越多的企业将运营转移到云端&#xff0c;导致对云数据库的依赖越来越大。虽然它们提供了可扩展性和可访问性等显着优势&#xff0c;但它们也带来了独特的安全挑战&#xff0c;需要解决这些挑战以保护敏感数据免受各种威胁。 在本文中&#x…

巨人互动|Facebook海外户Facebook page的功能以及如何应用

Facebook Page是Facebook平台上的一种工具&#xff0c;是企业、品牌、机构、公益组织等组织和个人创建社交媒体宣传页面的主要方式之一&#xff0c;通过Page可以将内容传播到更广泛的受众群体&#xff0c;提高品牌知名度和曝光率。下面将详述Facebook Page的功能以及如何应用。…

第125天:内网安全-隧道技术SMBICMP正反向连接防火墙出入规则上线

知识点 #知识点&#xff1a; 1、入站规则不出网上线方案 2、出站规则不出网上线方案 3、规则-隧道技术-SMB&ICMP-隧道技术&#xff1a;解决不出网协议上线的问题&#xff08;利用出网协议进行封装出网&#xff09; -代理技术&#xff1a;解决网络通讯不通的问题&#xff0…

Redis 集群 (cluster)

是什么 官网&#xff1a;Redis cluster specification | Redis 由于数据量过大&#xff0c;单个Master复制集难以承担&#xff0c;因此需要对多个复制集进行集群&#xff0c;形成水平扩展每个复制集只负责存储整个数据集的一部分&#xff0c;这就是Redis的集群&#xff0c;其作…

递归——在运行的过程中调用自己

递归&#xff0c;就是在运行的过程中调用自己 递归必须要有三个要素&#xff1a; ①、边界条件 ②、递归前进段 ③、递归返回段 当边界条件不满足时&#xff0c;递归前进&#xff1b;当边界条件满足时&#xff0c;递归返回。 递归关键&#xff1a;写出递推公式&#xff0c;找…

maven里面没有plugins dependence问题解决

说明&#xff1a;今天在做Nacos、Dubbo整合的时候&#xff0c;在父模块中做了版本限制&#xff0c;出错后就又把版本控制什么都删掉&#xff0c;回退到最开始的状态&#xff0c;此时父模块下面的服务右侧的 maven里面没有plugins dependence &#xff0c;然后项目全都报错。 问…

openSUSE安装虚拟化 qemu kvm

1) 第一种&#xff1a;图形界面yast安装虚拟化 左下角开始菜单搜索yast 点一下就能安装&#xff0c;是不是很简单呢 2&#xff09;第二种&#xff1a; 命令行安装 网上关于openSUSE安装qemu kvm的教程比较少&#xff0c;可以搜索centos7 安装qemu kvm的教程&#xff0c;然后…