【JavaSE】Java基础语法(四十三):反射

news2025/5/24 10:34:27

文章目录

  • 概述:
  • 1. java.lang.Class
    • 1.1 获取 Class 对象
    • 1.2 通过反射创建对象
    • 1.3 通过反射获取类的属性、方法和注解等
      • 1.3.1 反射获取构造方法
      • 1.3.2 反射通过构造器创建对象
      • 1.3.3 反射获取成员方法
      • 1.3.4 反射获取属性
  • 2. 工具类操作
  • 3. 反射是如何破坏单例模式的
  • 4. 反射结合注解实现操作日志


在这里插入图片描述


反射是框架的灵魂。动态代理、很多框架(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/601977.html

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

相关文章

linux0.12-12-2-buffer

基本上看完赵老师中文解释&#xff0c;都可以自己写这部分的代码。 [622页] 12-2 buffer.c程序 从本节起&#xff0c;我们对fs/目录下的程序逐一进行说明和注释。按照本章第2节中的描述&#xff0c; 本章的程序可以被分成4个部分&#xff1a; 高速缓冲管理&#xff1b; 文件…

基于ATC89C51单片机的超市临时储物柜密码锁设计

点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/87855870?spm=1001.2014.3001.5503 源码获取 摘 要 随着微机测量和控制技术的迅速发展与广泛应用,以单片机为核心的电子密码锁的设计研发与应用在很大程度上改善了人们的…

windows 部署多个tomcat

去官网下载tomcat&#xff0c;地址&#xff1a;Apache Tomcat - Apache Tomcat 8 Software Downloads 选择对应的版本下载&#xff0c;下载完成后&#xff0c;直接解压文件&#xff0c; 修改第二个解压的tomcat的catalina.bat 和 startup.bat和service.bat文件的配置&#x…

iptables 基础

iptables防火墙 主要实现数据包的过滤、封包重定向和网络地址转换&#xff08;NAT&#xff09;等功能 iptables&#xff1a;用户空间的命令行工具&#xff0c;用于管理配置netfilter&#xff1a;真正实现功能的是netfilter运行在内核空间 iptables的4表5链 链&#xff1a;通过…

想管好数据资源,不妨了解大数据分析开源框架

在如今快节奏的时代中&#xff0c;办公自动化早已成为各行各业的发展趋势和方向。随着业务量的激增&#xff0c;数据资源也不断增多&#xff0c;如果没有一套完善的大数据分析开源框架&#xff0c;那这么多的数据资源就不能很好地利用和发挥其价值&#xff0c;如果采用专业的大…

基于AT89C52单片机的交通灯设计

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/87855439?spm1001.2014.3001.5503 源码获取 一、实验目的 掌握单片机的综合应用设计。加强对单片机和汇编语言的认识&#xff0c;充分掌握和理解设计各部分的工作…

华为防火墙双机热备外线vrrp地址和接口地址非同网段

主防火墙FW1: HRP_Mdis current-configuration 2023-06-02 15:51:48.270 08:00 !Software Version V500R005C10SPC300 sysname USG6000V1 l2tp domain suffix-separator undo info-center enable ipsec sha2 compatible enable undo telnet server enable undo telnet ipv6 se…

Office Visio 2007安装教程

哈喽&#xff0c;大家好。今天一起学习的是Visio 2007的安装&#xff0c;这是一个绘制流程图的软件&#xff0c;用有效的绘图表达信息&#xff0c;比任何文字都更加形象和直观。Office Visio 是office软件系列中负责绘制流程图和示意图的软件&#xff0c;便于IT和商务人员就复杂…

ROS:客户端Client的编程实现

目录 一、话题模型二、创建功能包三、创建代码并编译运行&#xff08;C&#xff09;3.1步骤3.2创建客户端Client代码3.3编译 四、运行 一、话题模型 Sever端是海龟仿真器/turtlesim&#xff0c;Client端是待实现的程序&#xff0c;其作为Response的节点&#xff0c;并产生Requ…

wav格式怎么转换?介绍三个转换wav格式的方法

在音乐制作或者编辑过程中&#xff0c;可能需要将录制的音频文件转换成特定的格式以便于后期处理。例如&#xff0c;你可能拍摄了一段演奏视频&#xff0c;想要提取其中的音频&#xff0c;并将其转换为wav或者flac等无损格式&#xff0c;以便于进行音频编辑和修复。那么你知道w…

【正点原子STM32连载】 第二十四章 OLED显示实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

1&#xff09;实验平台&#xff1a;正点原子stm32f103战舰开发板V4 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id609294757420 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第二十…

VMWare安装Ubuntu

VMWare安装Ubuntu 安装前 准备 访问https://cn.ubuntu.com/download(下载需要版本/以16.04为例)操作系统上安装VMWare 基本安装 下载映像文件后&#xff0c;按照一步一步操作即可安装注意选中映像文件安装结束&#xff0c;登录进入系统可看到以下画面 设置语言环境 进入系…

OpenMMLab-AI实战营第二期-课程笔记-Class 2:人体姿态估计与MMpose

Class 2&#xff1a;人体姿态估计与MMpose 文章目录 Class 2&#xff1a;人体姿态估计与MMpose**人体姿态估计概述****what?****下游任务** **2D 姿态估计****主流算法****基于回归坐标的方法****基于热图&#xff08;heatmap&#xff09;的方法** **多人姿态估计****基于回归…

5. 垃圾收集器G1ZGC详解

JVM性能调优 1. G1收集器(-XX:UseG1GC) 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确的地方&#xff0c;还望大佬多多指点纠正&#xff0c;勿喷。 课程内容&#xff1a; 1、G1垃圾收集器详解 2、每秒几十万并发的系统JVM级别如何优化 3、ZGC垃圾收集器详解 4、…

设置 session rdp 时间限制

起因&#xff1a;一台服务器 2016版本&#xff0c;每当退出远程桌面后&#xff0c;过一段时间&#xff0c;用户所有的进程就会自动关闭&#xff0c;导致后台运行的任务出现异常。 运行 gpedit.msc, 根据以下资料设置后&#xff0c;记得重启&#xff0c;不然不会生效 前三个选…

汽车出海势头旺,汽车零部件企业如何破浪前行?

随着国内汽车市场逐渐饱和&#xff0c;中国汽车企业开始寻求“汽车出海”的新市场增长点。在政府加大汽车出海政策支持力度下&#xff0c;根据中汽协数据&#xff0c;一季度的新能源汽车出口达24.8万辆&#xff0c;同比增长1.1倍。中国汽车行业持续深耕海外市场&#xff0c;出口…

节省公司资源,可以使用生产力分析系统!

经常出现有人下班后忘记关机的情况&#xff0c;不仅给公司造成损失&#xff0c;还浪费电力资源。虽说公司内通常会装有摄像头&#xff0c;可以看到具体是哪台电脑忘记关&#xff0c;但总不能让已经到家的职工&#xff0c;又返回公司关机吧。使用生产力分析系统&#xff0c;可以…

测量不确定度

不确定度与误差区别 相同点&#xff1a;都是评价测量结果质量高低的重要指标&#xff0c;都可以作为测量结果的精度评定参数。 不同点&#xff1a;测量误差是结果与真值之差&#xff0c;以真值为约定中心&#xff1b;而不确定度则是以估计值为约定中心&#xff0c;因此误差是…

SpringBoot使用SSE进行实时通知前端

SpringBoot使用SSE进行实时通知前端 说明maven依赖SSE工具类代码 Controller测试代码测试结果如下:注意将超时时间由原来的0改为默认的30秒&#xff0c;会报错。将springboot降为低版本如1.4.2.RELEASE。 说明 项目有个需求是要实时通知前端&#xff0c;告诉前端这个任务加载好…

数字孪生:让ECU在数字环境中“栩栩如生”

很多汽车广告中都会展示这样一个画面&#xff1a;在快要猛烈撞击障碍物之时&#xff0c;汽车所配备的防撞预警功能在最后关头实现停驶&#xff0c;守护车内老少一家人的生命安全。 近年来&#xff0c;汽车安全性能成为众多厂商广告中的主打&#xff0c;侧面印证了安全在当下消…