欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > Java 反射

Java 反射

2024/10/25 19:26:32 来源:https://blog.csdn.net/aliyunyyds/article/details/140067774  浏览:    关键词:Java 反射

文章目录

  • 概述:
  • 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. 反射结合注解实现操作日志

自定义注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SnowLog {//	操作类型public String operateType();//	操作模块public String operateTarget();//	操作对象idpublic 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();
}


在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com