Java反射角度简单理解spring IOC容器

概述

Java反射(Reflection)是Java编程语言的一个特性,它允许在运行时对类、接口、字段和方法进行动态查询和操作。反射提供了一种在运行时查看和修改程序行为的能力,这通常用于实现一些高级功能,如框架(Spring)、ORM(对象关系映射)工具、IDE(集成开发环境)等。 
反射的主要用途
动态加载类:可以在运行时加载和使用类,而无需在编写/编译时知道它们。
检查类信息:可以获取类的名称、父类、实现的接口、字段、方法等。
动态调用方法:可以调用类的任何公共方法,即使该方法在编译时未知。
创建和操作对象:可以创建类的实例,并调用其方法或访问其字段。
修改字段值:可以修改类的私有字段的值(但通常不推荐这样做,因为它破坏了封装性,又名暴力反射)。 

反射使用场景及理解 

1.1. 动态加载类信息及动态调用类中的方法

 假设我们需要报考微软云的证书证书分别有如下几种,而且将来可能还会新增。
Azure900, Azure903, Azure300, Azure380

我们假设有这样一个需求,当用户传入的参数能模糊匹配上如下的方法名时,就算要报名该考试。如我传入 "30", 这个值正好能模糊匹配上方法名“singUpAzure930Exam”和“signUpAzure300Exam”,这时就报名930 和300两门考试,如我传入Azure,很显然,四门课程都会报名。具体需要传入什么样的参数根据用户需求动态而定。

我们这里用伪代码编写一个报名系统,首先我们申明一个报名类。

public class AzureCloudExam {
    public void AzureCloudExam(){
    }

    public String signUpAzure900Exam(){
        return "You signed up Azure 900!";
    }

    public String singUpAzure930Exam(){
        return "You signed up Azure 930!";
    }

    public String signUpAzure300Exam(){
        return "You signed up Azure 300!";
    }

    public String signUpAzure380Exam(){
        return "You signed up Azure 380!";
    }
}

显然这种场景,使用我们以往的先new 对象,再调用方法的普通模式是很难做到的,而且将来有新增加的考试方法后,我们也需要修改调用考试方法的服务类才能做到,继而需要修改服务代码,重新部署等麻烦。因为这种普通模式需要调用哪个方法是我们编写是就必须按照逻辑提前写好的,显然它不够灵活,然后在我们平时的开发过程中,普通方式new 对象调用方法已经足够用了,但是在面临一些更加灵活的需要时,就不行了,如spring框架等都有大量的使用反射解决,因为我们在使用spring框架开发时,显然编写阶段spring也是不知道需要加载哪些bean的。而是运行时根据注解动态生成的。

因而如果我们使用反射,就可以很好的解决动态调用方法的这个问题,模拟步骤如下:

编写服务代码用于调用如上的考试类。

package com.mycompany.myreflect.refDemo2;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyReflectionServer {
    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
        //接收用户传入参数
        String className = args[0];
        String examLike = args[1];
        //动态加载需要使用class字节码文件
        Class<?> clazz = Class.forName(className);
        //动态无参构造方法new 对象
        Object obj = clazz.getDeclaredConstructor().newInstance();
        //获取class字节码中对应的所有方法
        Method[] allMethods = clazz.getDeclaredMethods();
        //loop所有方法
        for (Method method:allMethods){
            //如果用户传入的参数能模糊匹配上当前方法名
            if (method.getName().contains(examLike)){
                //调用报名方法
                String result = (String) method.invoke(obj);
                System.out.println(result);
            }
        }
    }
}

如我们传入参数
运行结果

如上实现了动态决定调用哪个方法。
如将来还要加入Alicloud 的考试门类,我们也只需要添加Alicloud的报名类就可以了,而服务类的代码不需要做任何改动。这就是反射带来的动态调用方法的灵活性。

1.2. 动态创建类对象

假设我们有这样一个需求。还是如上的报名考试系统,但是由于业务的扩展,现在不仅要支持微软云认证报名,还需要支持阿里云报名。将来还会新增厂家。报名的用户也在爆发增长。
显然我们每次客户请求都new一个对应厂商的的类,然后再在该类中去调用对应厂商的服务接口是报名,会带来不必要的对象创建和销毁。我们这里只需要一个单例的bean对象就可以了。

  • 在创建报名类之前我们先创建一个注解@MySupportExam, 用于注解某厂商的某类考试我们是否支持在我们这里报名,因为有的门类报名,对应厂商有授权考虑。
    package com.mycompany.myreflect.refDemo2;
    
    import java.lang.annotation.*;
    
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MySupportExam {
    }
    
  • 创建@MyComponent注解,用于标准这是我们需要注入的厂商bean
    package com.mycompany.myreflect.refDemo2;
    
    import java.lang.annotation.*;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MyComponent {
    }
  • 创建对应厂商的报名类。
    package com.mycompany.myreflect.refDemo2;
    
    @MyComponent
    public class AzureCloudExam {
        public void AzureCloudExam(){}
        @MyEnableSupport
        public String signUpAzure900Exam(){return "You signed up Azure 900!";}
        @MyEnableSupport
        public String singUpAzure930Exam(){return "You signed up Azure 930!";}
    
        public String signUpAzure300Exam(){return "You signed up Azure 300!";}
    
        public String signUpAzure380Exam(){return "You signed up Azure 380!";}
    }
    
    package com.mycompany.myreflect.refDemo2;
    
    @MyComponent
    public class AliCloudExam {
        public void AliCloudExam(){
        }
    
        public String signUpAli900Exam(){return "You signed up Ali 900!";}
        @MyEnableSupport
        public String singUpAli930Exam(){return "You signed up Ali 930!";}
        @MyEnableSupport
        public String signUpAli300Exam(){return "You signed up Ali 300!";}
    
        public String signUpAli380Exam(){return "You signed up Ali 380!";}
    }
    
  •  创建一个ClassScanner 扫描添加了@MyCompany注解的厂商类
    package com.mycompany.myreflect.refDemo2;
    import java.io.File;
    import java.net.URL;
    import java.net.URLDecoder;
    import java.util.Enumeration;
    import java.util.LinkedHashSet;
    import java.util.List;
    import java.util.Set;
    
    public class ClassScanner {
        public static Set<Class<?>> scanPackage(String packageName) {
            Set<Class<?>> classSet = new LinkedHashSet<>();
            try {
                Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replace(".", "/"));
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    String protocol = url.getProtocol();
                    if ("file".equals(protocol)) {
                        String filePath = URLDecoder.decode(url.getPath(), "UTF-8");
                        findClassByPackageName(new File(filePath), packageName, classSet);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return classSet;
        }
    
        private static void findClassByPackageName(File file, String packageName, Set<Class<?>> classSet) throws ClassNotFoundException {
            if (file.isFile() && file.getName().endsWith(".class")) {
                String className = file.getName().substring(0, file.getName().length() - 6);
                String fullClassName = packageName + "." + className;
                Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(fullClassName);
                classSet.add(clazz);
            } else if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (File f : files) {
                    findClassByPackageName(f, packageName, classSet);
                }
            }
        }
    
        public static List<Class<?>> getExamCompanyClazz(List<Class<?>> examCompany,String packageName) {
            Set<Class<?>> classes = scanPackage(packageName);
            for (Class<?> clazz : classes) {
                if (clazz.getDeclaredAnnotation(MyComponent.class) !=null){
                    examCompany.add(clazz);
                }
            }
            return examCompany;
        }
    }
    
  •  创建一个容器类用于管理所有的厂商bean,和方法bean
    package com.mycompany.myreflect.refDemo2;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class ExamContainer {
        private Map<Class<?>,List<Method>> methodsSupportMap;
        private Map<Class<?>,Object> servicesMap;
        private List<Class<?>> examCompanies;
    
        public void init() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
            examCompanies = new ArrayList<>();
            servicesMap = new HashMap<>();
            methodsSupportMap = new HashMap<>();
            ClassScanner.getExamCompanyClazz(examCompanies,"com.mycompany.myreflect.refDemo2");
            for (int i = 0;i<examCompanies.size();i++){
                Class<?> clazz = examCompanies.get(i);
                //将获取到的厂商字节码,判断容器中是否存在该对象,如果不存在则创建该对象并放入容器中
                setServiceInstanceByClass(clazz);
                setMethod(clazz);
            }
        }
    
        public void setServiceInstanceByClass(Class<?> clazz) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
            Object obj = clazz.getDeclaredConstructor().newInstance();
            servicesMap.put(clazz,obj);
        }
    
        public void setMethod(Class<?> clazz){
            Method[] methods =clazz.getDeclaredMethods();
            // 如果方法加了@MySupportExam注解,说明我们这里支持该科目考试,加入容器
            for (Method method: methods){
                if (method.getDeclaredAnnotation(MySupportExam.class) != null){
                    if (methodsSupportMap.get(clazz)!=null){
                        methodsSupportMap.get(clazz).add(method);
                    }else {
                        List<Method> methodList = new ArrayList<>();
                        methodList.add(method);
                        methodsSupportMap.put(clazz,methodList);
                    }
                }
            }
        }
    
        public void signUpExam(String examLike) throws InvocationTargetException, IllegalAccessException {
            for (Class<?> clazz:examCompanies) {
                Object object = servicesMap.get(clazz);
                List<Method> methodList = methodsSupportMap.get(clazz);
                for (Method method : methodList) {
                    if (method.getName().contains(examLike)) {
                        System.out.println(method.invoke(object));
                    }
                }
            }
    
        }
    
    }
    
  • 用户请求服务方法调用 
    package com.mycompany.myreflect.refDemo2;
    
    import java.lang.reflect.InvocationTargetException;
    
    public class MyReflectionServer {
        public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
            ExamContainer examContainer = new ExamContainer();
            examContainer.init();
            String examLike = args[0];
            examContainer.signUpExam(examLike);
        }
    }
    

    请求参数
    运行结果

    请求参数

    运行结果

 1.3. 暴力反射

最近在开发过程中使用到暴力反射的一个案例就是,当我去给一个service类写UT实,发现某些方法是private的,但是这些private的方法,又是需要写UT的,这个时候我们就可以利用反射的暴力反射去为这个方法写UT调用。只需要设置如下属性即可,这里不在代码举案例。

​​​​​​​method.setAccessible(true);

注意事项

性能:反射通常比直接方法调用要慢得多,因为涉及到了额外的类型检查和安全性检查。因此,在性能敏感的代码中应谨慎使用。
安全性:反射允许绕过访问控制检查(例如,可以访问和修改私有字段),这可能导致安全问题。因此,在使用反射时应格外小心。
可读性:使用反射的代码通常比直接代码更难理解和维护。因此,在可以使用直接代码的情况下,应避免使用反射。 

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

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

相关文章

Git简介以及下载安装和配置

Git介绍 什么是版本控制?什么是Git?什么是集中式版本控制(了解)分布式版本控制工作流程 Git的安装与配置注册邮箱以及用户名(方便远程使用)初始化项目Git在ideal上的使用(本地) 什么是版本控制? ​ 版本控制是指对软件开发过程中各种程序代码,控制文件及说明文档等文件变更…

《计算机网络微课堂》1-3 三种交换方式

本节课我们介绍三种交换方式&#xff0c;分别是电路交换&#xff08;Circuit Switching&#xff09;&#xff0c;分组交换&#xff08;Packet Switching&#xff09;以及报文&#xff08;Message Switching&#xff09;交换。 我们首先来看电路交换&#xff0c;在电话问世后不…

设计模式8——原型模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 原型模式&#xff08;Prototyp…

MiniCPM-Llama3-V-2_5-int4

MiniCPM-Llama3-V-2_5-int4大模型部署使用环境&#xff1a; python3.8cuda11.8其它要求&#xff0c;按照安装文档要求下载即可 我是在算力平台用4090跑的&#xff0c; GPU 显存&#xff08;8GB&#xff09;可以部署推理 int4 量化版本&#xff0c;如果推理非量化版本需要更高显…

开视频号小店要花哪些钱?这些费用大家要知道

大家好&#xff0c;我是喷火龙。 目前&#xff0c;视频号小店从推出到现在已经快两年的时间了&#xff0c;视频号小店虽然门槛高&#xff0c;但是单价也高&#xff0c;利润也高&#xff0c;市场环境也好&#xff0c;算是一个不错的项目。 接下来给大家讲讲开视频号小店要花哪…

推荐五个线上兼职,在家也能轻松日入百元,适合上班族和全职宝妈

在这个瞬息万变的时代&#xff0c;你是否也曾考虑过在繁忙的工作之外&#xff0c;寻找一份兼职副业来补贴家用&#xff0c;同时保持生活的多样性&#xff1f;别急&#xff0c;现在就让我为你揭秘五个可靠的日结线上兼职岗位&#xff0c;助你轻松迈向财务自由之路&#xff01; 一…

VBA批量合并带有图片、表格与文本框的Word

本文介绍基于VBA语言&#xff0c;对大量含有图片、文本框与表格的Word文档加以批量自动合并&#xff0c;并在每一次合并时添加分页符的方法。 在我们之前的文章基于Python中docx与docxcompose批量合并多个Word文档文件并逐一添加分页符&#xff08;https://blog.csdn.net/zhebu…

iBarcoder for Mac v3.15.1中文激活版:让条形码生成变得如此简单

在现代社会&#xff0c;条形码无处不在&#xff0c;从超市商品到物流包裹&#xff0c;都离不开它的身影。iBarcoder for Mac作为一款简单易用的条形码生成软件&#xff0c;让条形码的生成变得如此简单。 iBarcoder for Mac v3.15.1中文激活版下载 无论你是需要为商品添加条形码…

信息安全等级保护测评: 登陆日志

文章目录 引言I 登录日志表结构设计II 日志处理2.1 封装日志入库2.2 收集登陆信息2.3 查询接口引言 等保测评是信息安全等级保护测评的简称,是对信息和信息载体按照重要性等级分级别进行检测、评估的过程。 背景:近期AIS监控平台(网页版)等保测评,发现没有登陆日志,现要…

【高时效通路】

一 高时效通路 1.1 pathchdumper 实时数据拉取、实时数据处理、5分钟微批dump来加速时效性&#xff0c;具体来说&#xff1a; 实时数据拉取&#xff08;Fetcher&#xff09;&#xff1a;基于Databus Fetcher基建&#xff0c;直接对接F0层实时拉取最新数据&#xff0c;保证该…

微服务架构-链式微服务设计模式

微服务架构-链式微服务设计模式 链式微服务设计模式&#xff08;Chain Microservice Pattern&#xff09;是一种微服务架构中的设计模式&#xff0c;它强调将一系列的服务按照特定的业务逻辑顺序串联起来&#xff0c;形成一个服务链。每个服务在链中负责完成特定的业务功能&am…

基于JSP/Servlet校园二手交易平台(二)

目录 2 开发技术及开发环境 2.1 Java语言简介 2.2 J2EE技术介绍 2.3 Servlet/JSP技术 2.4 MVC 简介 2.5 Struts 技术 2.6 Hibernate 技术 2.6.1 应用程序的分层体系结构 2.6.2 Hibernate的应用及API简介 2.7 开发环境及环境配置 2.7.1 Java/JSP系统环境 2.7.2 JSP环…

uniapp 对接 微信App/支付宝App 支付

相关文档&#xff1a;uni.requestPayment(OBJECT) | uni-app官网 示例代码&#xff1a; import qs from qsasync aliPay(){const { provider } await uni.getProvider({ service:payment })if(provider.includes(alipay)){uni.request({url:后端接口地址,data:{ //传参 },suc…

Rabbitmq 搭建使用案例 [附源码]

Rabbitmq 搭建使用案例 文章目录 RabbitMQ搭建docker 代码golang生产者消费者 可视化消费进度 RabbitMQ搭建 docker docker run -d --hostname rabbitmq --name rabbitmq -e RABBITMQ_DEFAULT_USERadmin -e RABBITMQ_DEFAULT_PASSadmin -e RABBITMQ_DEFAULT_VHOSTmy_vhost -e…

分类预测 | Matlab实现ZOA-SVM斑马算法优化支持向量机的多变量输入数据分类预测

分类预测 | Matlab实现ZOA-SVM斑马算法优化支持向量机的多变量输入数据分类预测 目录 分类预测 | Matlab实现ZOA-SVM斑马算法优化支持向量机的多变量输入数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现ZOA-SVM斑马算法优化支持向量机的多变量输…

5. C++网络编程-UDP协议的实现

UDP是无连接的。 UDP Server网络编程基本步骤 创建socket&#xff0c;指定使用UDP协议将socket与地址和端口绑定使用recv/send接收/发送数据 由于UDP是无连接的&#xff0c;直接侦听就行使用close关闭连接 这个UDP接收数据的时候用的API是recvfrom,发送数据是sendto 客户端 …

IS-IS链路状态数据库

原理概述 一个OSPF链路状态数据库是若干条LSA的集合。与此相似&#xff0c;一个IS-IS链路状态数据库是由若干条LSP的集合。与OSPF链路状态数据库不同&#xff0c;IS-IS链路状态数据库有Level-1和Level-2之分。 在IS-IS协议中&#xff0c;每一条LSA都有一条剩余生存时间、一个…

Behind the Code:Polkadot 如何重塑 Web3 未来

2024 年 5 月 17 日 Polkadot 生态 Behind the Code 第二季第一集 《创造 Web3 的未来》正式上线。第一集深入探讨了 Polkadot 和 Web3 技术在解决数字身份、数据所有权和去中心化治理方面的巨大潜力。 &#x1f50d; 查看完整视频&#xff1a; https://youtu.be/_gP-M5nUidc?…

Docker安装OnlyOffice

工作需要&#xff0c;多人在线编辑同一文档&#xff0c;找了一圈发现onlyoffice满足需求&#xff0c;于是使用docker安装了社区版本。下面记录下安装过程。 Onlyoffice 是什么&#xff1f; Onlyoffice 是一个多端协同的 Office 办公套件&#xff0c;相当于微软的 Office365 全…

MySQL多表关联查询习题

一、素材 -- Active: 1714203732007127.0.0.13306db_stu -- 1.创建student和score表 CREATE TABLE student ( id INT(10) NOT NULL UNIQUE PRIMARY KEY , name VARCHAR(20) NOT NULL , sex VARCHAR(4) , birth YEAR, department VARCHAR(20) , address VARCHAR(50) ); -- 创建…