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

news2024/11/16 12:36:36

概述

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);

注意事项

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

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

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

相关文章

【正点原子Linux连载】 第四十七章 音频驱动实验摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

1&#xff09;实验平台&#xff1a;正点原子ATK-DLRK3568开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id731866264428 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/docs/boards/xiaoxitongban 第四十…

EEGLAB的相关使用

目录 概念 1.安装EEGLAB 2.文件实例演示 导入数据集处理 &#xff08;1&#xff09;导入数据集 &#xff08;2&#xff09;画图 &#xff08;3&#xff09; 修改并存储数据集 &#xff08;4&#xff09; 保存数据集 &#xff08;5&#xff09; 删除数据集 &#xff0…

零基础HTML教程(33)--HTML5表单新功能

文章目录 1. 背景2. HTML5新增表单元素2.1 number (数字输入框)2.2 email (邮箱输入框)2.3 url (链接输入框)2.4 tel (电话输入框)2.5 range (范围选择框)2.6 color (颜色选择框)2.7 datetime (日期时间选择框)2.8 search (搜索框) 3. placeholder &#xff08;占位属性&#x…

php TP8 阿里云短信服务SDKV 2.0

安装&#xff1a;composer require alibabacloud/dysmsapi-20170525 2.0.24 官方文档&#xff1a;短信服务_SDK中心-阿里云OpenAPI开发者门户 (aliyun.com) 特别注意&#xff1a;传入参数获得值形式 正确&#xff1a; $PhoneNumbers $postData[PhoneNumbers];$signName $po…

Vue集成Iframe

一、应用场景&#xff0c;为什么要集成Iframe&#xff1f; 1、庞大项目拆分后&#xff0c;便于管理和部署&#xff0c;用集成Iframe的方法合并 2、避免功能重复开发&#xff0c;共用模块可单独开发为一个项目&#xff0c;既可独立部署&#xff0c;也可集成到中台系统 二、集成…

【feature selection】特征选择学习笔记

文章目录 1. 什么是特征选择2. 特征选择与特征提取的区别3. 特征选择的方法3.1 Filtering过滤法3.2 Wrapper包装法3.3 Embedding嵌入法 4. 特征选择示例4.1 方差选择法示例4.2 递归特征消除法示例 1. 什么是特征选择 特征选择是特征工程的内容, 其目标是寻找最优特征子集。剔除…

暴雨“彩虹”行业大模型加速器平台全新发布

近日&#xff0c;在第七届数字中国建设峰会期间&#xff0c;暴雨信息全新发布“彩虹”行业大模型加速器平台&#xff0c;聚焦于为客户降本增效减负&#xff0c;将海量通用数据与行业特有数据融合&#xff0c;专注于流程工艺的智能化改进&#xff0c;因地制宜深挖业务需求&#…

图像上下文学习|多模态基础模型中的多镜头情境学习

【原文】众所周知&#xff0c;大型语言模型在小样本上下文学习&#xff08;ICL&#xff09;方面非常有效。多模态基础模型的最新进展实现了前所未有的长上下文窗口&#xff0c;为探索其执行 ICL 的能力提供了机会&#xff0c;并提供了更多演示示例。在这项工作中&#xff0c;我…

【论文速读】GPT-1:Improving Language Understanding by Generative Pre-Training

摘要 自然语言理解包括广泛的不同的任务&#xff0c;如文本隐含、问题回答、语义相似性评估和文档分类。虽然大量的未标记文本语料库非常丰富&#xff0c;但用于学习这些特定任务的标记数据非常稀缺&#xff0c;这使得经过区别训练的模型要充分执行任务具有挑战性。我们证明&a…

mongoengine,一个非常实用的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个超酷的 Python 库 - mongoengine。 Github地址&#xff1a;https://github.com/MongoEngine/mongoengine 在现代应用程序开发中&#xff0c;NoSQL数据库因其灵活性和高性能而广受欢迎。MongoD…

基于Android Studio图书管理,图书借阅系统

目录 项目介绍 图片展示 运行环境 获取方式 项目介绍 用户 书架&#xff1a;搜索书籍&#xff0c;查看书籍&#xff0c;借阅书籍&#xff0c;收藏书籍&#xff0c;借阅书籍必须在一个月之内还书&#xff1b; 我的&#xff1a;可以修改密码&#xff0c;退出登录&#xff…

M功能-支付平台(三)

target&#xff1a;离开柬埔寨倒计时-221day 前言 今天周六&#xff0c;但是在柬埔寨还是工作日&#xff0c;想着国内的朋友开始休周末就羡慕呀&#xff0c;记不清在这边过了多少个周六了&#xff0c;多到我已经习惯了。而且今天技术部还停电了&#xff0c;真的是热的受不了呀…

网络安全的重要组成部分:数据库审计

数据库审计&#xff08;简称DBAudit&#xff09;以安全事件为中心&#xff0c;以全面审计和精确审计为基础&#xff0c;实时记录网络上的数据库活动&#xff0c;对数据库操作进行细粒度审计的合规性管理&#xff0c;对数据库遭受到的风险行为进行实时告警。它通过对用户访问数据…

MCU复位电路

【单片机复位电路&#xff0c;巧妙的RC无处不在。】https://www.bilibili.com/video/BV1XW4y1571r?vd_source3cc3c07b09206097d0d8b0aefdf07958 左侧的RESET引脚正常情况下是低电平&#xff0c;是高电平复位&#xff1b;右侧的RESET引脚正常情况下是高电平&#xff0c;是低电…

刷代码随想录有感(77):回溯算法——含有重复元素的全排列

题干&#xff1a; 代码&#xff1a; class Solution { public:vector<int> tmp;vector<vector<int>> res;void backtracking(vector<int> nums, vector<int> used){if(tmp.size() nums.size()){res.push_back(tmp);return;}sort(nums.begin(),…

第十六节:带你梳理Vue2: 生命周期与钩子函数

前沿: 通过前面几节的学习&#xff0c;我们已经对vue有了初步的了解&#xff0c;大致了解了vue可以帮我们干什么&#xff0c; 那么接下来我们就来看看vue的生命周期和它常用的钩子函数, 1. 理解生命周期的含义 生命周期&#xff1a;就是一个组件从实例化创建并添加到DOM树开…

每日5题Day8 - LeetCode 36 - 40

每一步向前都是向自己的梦想更近一步&#xff0c;坚持不懈&#xff0c;勇往直前&#xff01; 第一题&#xff1a;36. 有效的数独 - 力扣&#xff08;LeetCode&#xff09; 题目要求我们进行判断&#xff0c;我们不需要自己填写&#xff0c;所以要一个标志位&#xff0c;来看当…

MySQL 存储过程(实验报告)

一、实验名称&#xff1a; 存储过程 二、实验日期&#xff1a; 2024 年5 月 25 日 三、实验目的&#xff1a; 掌握MySQL存储过程的创建及调用&#xff1b; 四、实验用的仪器和材料&#xff1a; 硬件&#xff1a;PC电脑一台&#xff1b; 配置&#xff1a;内存&#xff0…

外卖霸王餐返利外卖会员卡小程序开发

外卖霸王餐返利外卖会员卡小程序开发 "社交电商赋能下的外卖返利小程序"是专为商家与用户双赢而设计的创新平台。 以下是其开发方案的详细步骤&#xff1a; 一、需求梳理&#xff1a;首先&#xff0c;我们需要明确小程序的核心功能和特色。包括设定活动类型、返利…

7款好用到离谱的神级App推荐!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 转眼间&#xff0c;2024年已经是下个月。最近有很多小伙伴的咨询&#xff0c;我也赶紧整理了7款好用的软件&#xff0c;希望对大家有所帮助。 …