【Java 基础】反射

news2024/12/24 10:03:11

反射是框架的灵魂。动态代理、很多框架(SoringIOC、AOP等)中都用到了反射。

概述:

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法(包括私有的);对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的);
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

1. java.lang.Class

Java 中 的java.lang.Class 类是 Java 反射机制的基础,我们想要在运行期间获取一个类的相关信息,就需要使用 Class 类。
JVM 会为每个类都创建一个 Class 对象,在程序运行时, JVM 会首先检查要加载的类对应的 Class 对象是否已经加载。如果没有加载,那么 JVM 会根据类名查找 .class 文件,并将其Class对象载入。

加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

image.png

Student 类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {

    private Integer id;

    private String name;

    private Integer age;


    private void eat(String name){
        System.out.println(name + " 吃东西。");
    }


    public int getBirthsDay(Integer age){
        return LocalDateTime.now().getYear() - age;
    }

}

1.1 获取 Class 对象

  1. 方式 1

调用对象的 getClass() 方法

Student student = new Student();
Class clazz = student.getClass();
  1. 方式 2

根据类名.class 获取

Class clazz = Student.class();
  1. 方式 3

根据Class类静态方法(全路径名)

Class clazz = Class.forName("com.snow.Student");

注意:在运行期间,一个类,只有一个Class对象产生。
三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都用第三种,一个字符串可以传入也可写在配置文件中等多种方法。

在这里插入图片描述

1.2 通过反射创建对象

使用反射创建对象有 2 种方式:

  1. 使用 Class 对象的 newInstance() 方法,其原理是通过类中定义的无参构造函数来创建对象的。
Student student = clazz.newInstance();
  1. 使用 java.lang.redlect.Constructor 类中的newInstance() 方法,这种方式既可以获取无参数的也可以获取有参数的。
//  调用无参
Student student1 = clazz1.getConstructor().newInstance();
System.out.println(student1);

//  调用有参
Student student2 
	= clazz1.getConstructor(Integer.class, String.class, Integer.class)
	.newInstance(10, "snow", 100);
System.out.println(student2);

1.3 通过反射获取类的属性、方法和注解等

除了 newInstance()方法,Class 类还有其他方法,可以在运行期间获得一个类的方法、属性和注解等。

在这里插入图片描述

1.3.1 反射获取构造方法

方法说明
Constructor<?>[] getConstructors()返回所有构造器对象的数组(只能拿public的)
Constructor<?>[] getDeclaredConstructors()返回所有构造器对象的数组,存在就能拿到
Constructor getConstructor(Class<?>… parameterTypes)返回单个构造器对象(只能拿public的)
Constructor getDeclaredConstructor(Class<?>… parameterTypes)返回单个构造器对象,存在就能拿到

1.3.2 反射通过构造器创建对象

方法说明
T newInstance(Object… initargs)根据指定的构造器创建对象(参数为属性赋值)
public void setAccessible(boolean flag)设置为true,表示取消访问检查,进行暴力反射

1.3.3 反射获取成员方法

方法说明
Method[] getMethods()返回所有成员方法对象的数组(只能拿public的)
Method[] getDeclaredMethods()返回所有成员方法对象的数组,存在就能拿到
Method getMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象(只能拿public的)
Method getDeclaredMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象,存在就能拿到(第一个是方法名称,后面的是参数类型)

Method 类中用于触发执行的方法

//  参数1:方法名 参数2:形参类型
Method eat = clazz1.getDeclaredMethod("eat", String.class);
//  设置此方法可访问(只有私有方法才需要加这个)
eat.setAccessible(true);
//  运行方法 参数1:执行该方法的对象 参数2:实际参数
eat.invoke(student1, "snow");
Method getBirthsDay = clazz1.getDeclaredMethod("getBirthsDay", Integer.class);
Integer year = (Integer) getBirthsDay.invoke(student1, 25);
System.out.println(year);

1.3.4 反射获取属性

方法说明
Field[] getFields()返回所有成员变量对象的数组(只能拿public的)
Field[] getDeclaredFields()返回所有成员变量对象的数组,存在就能拿到
Field getField(String name)返回单个成员变量对象(只能拿public的)
Field getDeclaredField(String name)返回单个成员变量对象,存在就能拿到

Filed类 用于取值,赋值的方法:

方法说明
void set(Object obj, Object value)赋值
Object get(Object obj)获取值。
Field[] declaredFields = clazz1.getDeclaredFields();
System.out.println(Arrays.toString(declaredFields));

2. 工具类操作

import org.springframework.util.ReflectionUtils;

String serviceClass = "orderUserServiceImpl";
String methodName = "getOne";
int id = 1;
//	获取方法
Method method 
     = ReflectionUtils.findMethod(SpringContextUtil.getBean(serviceClass).getClass(), methodName, Integer.class);
//用spring bean获取操作前的参数,此处需要注意:传入的id类型与bean里面的参数类型需要保持一致
//	获取对象
Object obj 
    = ReflectionUtils.invokeMethod(method, SpringContextUtil.getBean(serviceClass), id);

借助到的 SpringContextUtil 工具类:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext  applicationContext;

    /**
     * 实现ApplicationContextAware接口的回调方法,设置上下文环境
     */
    public void setApplicationContext(ApplicationContext applicationContext){
        SpringContextUtil.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext(){
        return applicationContext;
    }

    /**
     * 获取对象
     * @return  Object 一个以所给名字注册的bean的实例 (service注解方式,自动生成以首字母小写的类名为bean name)
     */
    public static Object getBean(String name) throws BeansException {
        return applicationContext.getBean(name);
    }
}

3. 反射是如何破坏单例模式的

反射可以在运行期间获取并调用一个类的任何方法(包括私有方法)。所以使用反射可以破坏单例模式的。

SO : 如何避免单例对象被反射破坏:
只需要改造构造函数,使其在反射调用时识别对象是不是被创建即可:

private Student(){
    if(student != null){
        throw new RuntimeException(".....");
    }
}

4. 反射结合注解实现操作日志

注解相关内容:https://blog.csdn.net/m0_60915009/article/details/130677945

自定义注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SnowLog {
	//	操作类型
    public String operateType();
    //	操作模块
    public String operateTarget();
    //	操作对象id
    public String operateTargetIdExpression();

}

切面类

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

import com.google.common.base.CaseFormat;

@Aspect
@Component
@Slf4j
public class SnowLogAspect {


    //  环绕通知
    @Around("@annotation(com.baga.web.controller.log.SnowLog)")
    public Object log(ProceedingJoinPoint point) throws Exception{
        Method method = ((MethodSignature) point.getSignature()).getMethod();
        SnowLog snowLog = method.getAnnotation(SnowLog.class);

        Object response = null;

        try {
            //  执行目标方法
            response = point.proceed();
        } catch (Throwable throwable) {
            throw new Exception(throwable);
        }

        if(StringUtils.isBlank(snowLog.operateTargetIdExpression())){
            return null;
        }

        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(snowLog.operateTargetIdExpression());

        EvaluationContext context = new StandardEvaluationContext();

        //  获取参数值
        Object[] args = point.getArgs();

        //  获取运行时参数名称
        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = discoverer.getParameterNames(method);

        //  将参数绑定到 context 中
        if(parameterNames != null){
            for (int i = 0; i < parameterNames.length; i++) {
                context.setVariable(parameterNames[i], args[i]);
            }
        }

        //  将方法的 resp 当做变量放到 context 中,变量名称为该类名转换为小写字母
        //  开头的驼峰形式
        if(response != null){
            context.setVariable(
                    CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL,
                            response.getClass().getSimpleName()), response);
        }

        //  解析表达式,获取结果
        String operateTargetId = String.valueOf(expression.getValue(context));

        //  执行日志记录
        handleSnowLog(snowLog.operateType(), snowLog.operateTarget(), operateTargetId);
        return response;
    }


    private void handleSnowLog(String operateType, String operateTarget, String operateTargetId){
        log.info("operateType : {}, operateTarget : {}, operateTargetId : {}", operateType, operateTarget, operateTargetId);
    }

}

业务-添加

@PostMapping("/addStudyReview")
@SnowLog(operateTarget = "温故知新", operateType = "添加", operateTargetIdExpression = "#baseResult.data.id")
public BaseResult addStudyReview(@Validated(Default.class) @RequestBody StudyReviewAddModel addModel){
	StudyReview studyReview = studyReviewService.addStudyReview(addModel);
	return BaseResult.success(studyReview);
}

业务-修改

@PutMapping("/updateStudyReview")
@SnowLog(operateTarget = "温故知新", operateType = "修改", 
         operateTargetIdExpression = "#updateModel.id")
public BaseResult updateStudyReview(@Validated(Default.class) @RequestBody StudyReviewUpdateModel updateModel){
    studyReviewService.updateStudyReview(updateModel);
    return BaseResult.success();
}

业务-删除

@DeleteMapping("/end/{id}")
@SnowLog(operateTarget = "温故知新", operateType = "终结", 
         operateTargetIdExpression = "#id")
public BaseResult end(@PathVariable("id") Integer id){
StudyReview studyReview = studyReviewService.getById(id);
    if(studyReview == null || "1".equals(studyReview.getIsClose())){
        return BaseResult.success();
    }
    studyReview.setIsClose("1");
    studyReviewService.updateById(studyReview);
    return BaseResult.success();
}

业务

@PostMapping("/test")
@SnowLog(operateTarget = "温故知新", operateType = "测试", 
         operateTargetIdExpression = "#id")
public BaseResult test(Integer id, Integer age, String name){
	return BaseResult.success();
}

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

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

相关文章

一款高效的企业级表格可视化搭建解决方案DripTable

DripTable 是京东零售推出的一款用于企业级中后台的动态列表解决方案&#xff0c;项目基于 React 和 JSON Schema&#xff0c;旨在通过简单配置快速生成页面动态列表来降低列表开发难度、提高工作效率。 DripTable 目前包含以下子项目&#xff1a;drip-table、drip-table-gene…

SpringBoot实战(四)获取接口请求中的参数(@PathVariable,@RequestParam,@RequestBody)

一&#xff1a;获取参数 SpringBoot提供的获取参数注解包括&#xff1a;PathVariable&#xff0c;RequestParam&#xff0c;RequestBody,三者的区别如下表&#xff1a; 二、java基础&#xff08;spring注解PathVariable和RequsetParam的区别还有RequestBody&#xff09; Path…

“AI孙燕姿”们侵了谁的权?

“2003年大火的歌手&#xff1a;孙燕姿&#xff1b;2023年大火的歌手&#xff1a;AI孙燕姿”。在B站&#xff0c;这条评论获赞2800多&#xff0c;而被网友们集体点赞的是用AI克隆孙燕姿声音后演唱其他歌曲的视频。 截止目前&#xff0c;Up主们打造的“AI孙燕姿”已翻唱了百余首…

每日学术速递5.14

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.An Inverse Scaling Law for CLIP Training 标题&#xff1a;CLIP 训练的逆比例定律 作者&#xff1a;Xianhang Li, Zeyu Wang, Cihang Xie 文章链接&#xff1a;https://arxiv.…

【Linux】Linux编辑器-gcc/g++使用

目录 一、背景知识 二、gcc是如何完成的 1、预处理(进行宏替换) 2、编译(生成汇编) 3、汇编(生成机器可识别代码) 4、链接(生成可执行文件或库文件) 4.1、静态库 4.2、动态库 4.3、动静态库的比较 三、gcc常见的选项 一、背景知识 计算机是二进制读取文件的&#xff0c;我们…

HHDBCS及HHDESK的资源加密功能

安全性&#xff0c;是头等重要的事情。HHDBCS及HHDESK均有一项实用功能&#xff0c;资源加密。 HHDBCS 打开HHDBCS&#xff0c;出现连接管理界面&#xff08;或者在运行过程中&#xff0c;点击连接管理&#xff09;&#xff0c;点击如下图箭头所指处的图标即可 HHDESK 点击主…

全球范围内的数字化时代,挑战和价值有哪些?

近年来&#xff0c;数字经济的发展趋势越来越明显&#xff0c;尤其是随着疫情的影响&#xff0c;加速了传统产业向数字化、网络化和智能化产业的转型和升级。全球数字经济规模不断扩大&#xff0c;体量连年增长&#xff0c;根据中国信息通信研究院报告显示&#xff0c;2019年全…

VMware虚拟机,匹配库中的文件系统文件夹层次结构

不需要把虚拟机文件复制到本地就不需要勾选“匹配库中的文件系统文件夹层次结构”这个选项。 但是&#xff0c;即便是勾选“匹配库中的文件系统文件夹层次结构”这一选项&#xff0c;也可以不勾选下一个选项卡的任何选项。

Midjourney AI 官方中文版已开启内测申请;OpenAI 正准备向公众发布一款新的开源语言模型。

&#x1f680; Midjourney AI 官方中文版已开启内测申请&#xff0c;搭载在 QQ 频道上&#xff0c;召唤机器人进行作画。 Midjourney AI 官方中文版已开启内测申请&#xff0c;搭载在 QQ 频道上&#xff0c;召唤机器人进行作画。 可调用 MJ 和 Niji 的最新模型和所有参数&…

Python源码怎么运行?

要运行Python源码&#xff0c;您需要安装Python解释器。Python解释器是一种软件&#xff0c;它可以读取Python源代码并将其转换为计算机可以理解和执行的指令。 在Windows操作系统上运行Python源代码的步骤&#xff1a; 在您的计算机上下载并安装Python解释器。您可以从Pyth…

COM接口规则的存在是有原因的

可能有些人认为接口上的 COM 接口规则没有必要设计的那么严格&#xff0c;但我想说的是&#xff0c;这些规则的存在是有原因的。 假设你在你的产品代码中新增加了版本号为 N 的接口&#xff0c;由于这个接口是内部使用的&#xff0c;没有任何公开文档。所以你可以随意修改它&a…

Sentinel 热点参数限流

何为热点&#xff1f;热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据&#xff0c;并对其访问进行限制。比如&#xff1a; 商品 ID 为参数&#xff0c;统计一段时间内最常购买的商品 ID 并进行限制用户 ID 为参数&#xff0c;针对一段时间…

Prompt工程师指南[资料整合篇]:Prompt最新前沿论文整理合集、工具和库推荐、数据集整合、推荐阅读内容等,超全面资料

Prompt工程师指南[资料整合篇]&#xff1a;Prompt最新前沿论文整理合集、工具和库推荐、数据集整合、推荐阅读内容等&#xff0c;超全面资料 1.论文合集 The following are the latest papers (sorted by release date) on prompt engineering. We update this on a daily bas…

Ozeki VOIP SIP SDK 10.3.199 Crack

Ozeki VOIP SIP SDK 使用Ozeki VoIP SIP SDK&#xff0c;您有机会制作自己的VoIP产品&#xff0c;例如软电话&#xff0c;甚至您自己的PBX。 Ozeki VoIP SIP SDK介绍 Ozeki VoIP SIP SDK 是一个软件开发工具包&#xff0c;允许您使用 SIP 协议进行 VoIP 呼叫。它可以很容易地…

LNMP平台对接redis服务

LNMP见我2023-04-17 10:51:16 发布的企业网站架构部署与优化 LNMP https://blog.csdn.net/Richard_Sniper/article/details/130158518?spm1001.2014.3001.5501 1、安装 LNMP 各个组件 2、安装 redis 服务 3、安装 redis 扩展 官网&#xff1a;http://redis.io/ 下载包&am…

解读直接RF采样架构及优势

多年来&#xff0c;数字收发机被应用在多种类型的应用中&#xff0c;包括地面蜂窝网络、卫星通信和基于雷达的监视、地球观测和监控。过去&#xff0c;收发机的系统工程师在这些应用中使用中频架构。现在&#xff0c;高速数据转换器的最新发展&#xff0c;使新型基于射频直接采…

gif怎么转换成mp4格式?

gif怎么转换成mp4格式&#xff1f;GIF动态图片是一种常见的图片文件&#xff0c;平时我们聊天时会使用到表情包、广告宣传场景也会使用到gif动图&#xff0c;而MP4则是目前广泛应用的视频格式&#xff0c;相信大家都知道这一点。将GIF图片转换为视频格式是一种非常实用的方法。…

SpringCloud实用篇02

文章目录 SpringCloud实用篇020.学习目标1.Nacos配置管理1.1.统一配置管理1.1.1.在nacos中添加配置文件1.1.2.从微服务拉取配置 1.2.配置热更新1.2.1.方式一1.2.2.方式二 1.3.配置共享1&#xff09;添加一个环境共享配置2&#xff09;在user-service中读取共享配置3&#xff09…

选择无论文答辩硕士,那只能选择免联考双证中国人民大学与加拿大女王大学金融硕士

硕士的论文和答辩是一种检验硕士阶段的学习研究成果的一种方式&#xff0c;通过答辩可以让老师清楚的了解论文的价值所在。但从选题背景、研究意义到研究思路、理论基础、研究方法再到关键技术点、实践难点等等&#xff0c;这一个复杂的过程让很多考生在最后这一关被淘汰出局。…

咚咚咚,穷人版生产力工具,好用到飞起

每个程序员都有自己的生产力工具&#xff0c;不管你是深耕职场多年的老鸟&#xff0c;还是在学校努力学习的小鸟&#xff0c;应该都有自己囊里私藏的好辅助。比如帮你完成从头脑风暴草图到创建线框图/原型的UI工具&#xff0c;让代码规范和交付更为可靠的版本控制工具等等。 今…