Appearance
基础概念
反射定义: 在一个类中 定义了一个私有属性/方法,但是使用反射能使所有属性都访问到,会破解私有属性
反射的应用场景:
反编译: .class → .java
- 通过反射机制访问 java 对象的属性,方法,构造方法等
- 反射技术的使用
Class类 代表类的实体,在运行的Java应用程序中表示类和接口Field类 代表类的成员变量(成员变量也称为类的属性)Method类 代表类的方法Constructor类 代表类的构造方法- 1.
getField、getMethod和getCostructor方法可以获得指定名字的域、方法和构造器 - 2.
getFields、getMethods和getCostructors方法可以获得类提供的public域、方法和构造器数组,其中包括超类的共有成员 - 3.
getDeclatedFields、getDeclatedMethods和getDeclaredConstructors方法可以获得类中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。
- 1.
反射的基础使用: https://www.cnblogs.com/cg-ww/p/15609172.html
演示使用类:
User
package com.ruoyi.luoqi.reflect;
/**
* @author luoqi
* @File User.java
* @Desc
* @Create 2024/4/24 10:42
* @ChangeList --------------------------------------------------------------------
* Date Editor ChangeReason
*/
public class User {
private String name;
private int age;
private String email;
// 无参数构造器
public User() {
}
// 带所有属性的构造器
public User(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public int add(int a,int b ){
return a + b;
}
private int subtract(int a,int b ){
return a - b;
}
// Getter 和 Setter 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
// 重写 toString 方法以便于打印用户信息
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}ReflectionExample
package com.ruoyi.luoqi.reflect;
/**
* @author luoqi
* @File ReflectionExample.java
* @Desc
* @Create 2024/4/24 10:40
* @ChangeList --------------------------------------------------------------------
* Date Editor ChangeReason
*/
import java.lang.reflect.*;
public class ReflectionExample {
public static void main(String[] args) {
try {
System.out.println("无参构造:");
//加载 User 类
Class<?> cls = Class.forName("com.ruoyi.luoqi.reflect.User");
Constructor<?> constructor = cls.getConstructor(); // 获取无参公共构造函数
System.out.println(constructor);
System.out.println("获取公共访问字段:");
//Class.getFields() 方法返回的是类中所有可访问的公共字段(public fields),包括从超类继承来的公共字段
Field[] fields = cls.getFields();
System.out.println(fields.length);
for(Field d : fields) {
System.out.println(d.getName());
}
System.out.println("---------");
System.out.println("获取全部访问字段:");
//获得类中声明的全部域,其中包括私有和受保护的成员,但不包括超类的成员。
Field[] declaredFields = cls.getDeclaredFields();
for(Field d : declaredFields) {
System.out.println(d.getName());
}
System.out.println("对实例进行属性赋值操作");
Object instance = constructor.newInstance(); // 创建 User 类的一个实例
//反射给属性赋值
//查找到属性
Field pubUserName = cls.getDeclaredField("name");
//指定给哪个userEntity对象赋值
//这是给公有属性赋值,默认只能访问公有属性,如果要访问私有属性,会报错
//反射没有权限访问私有属性,如果需要访问需要设置权限setAccessible
//设置 accessible 为 true,允许访问私有字段
pubUserName.setAccessible(true);
pubUserName.set(instance,"test");
System.out.println(instance);
System.out.println("有参构造:");
// 获取接受 String 和 int 和 String 三个参数的构造函数
Constructor<?> constructor2 = cls.getConstructor(String.class, int.class,String.class);
// 使用获取的构造函数创建 User 类的实例
Object userInstance = constructor2.newInstance("John Doe", 30,"test@163.com");
System.out.println(userInstance);
System.out.println("---------");
System.out.println("反射机制的三种方式创建对象:");
//反射机制的三种方式创建对象
//第一种方式:通过new出来的对象获取class
User userEntity = new User();
Class userClass = userEntity.getClass();
// 默认执行无参构造函数
User user1 = (User) userClass.newInstance();
System.out.println(user1==userEntity);//false
//第二种方式:直接获取class
Class userClass2 = User.class;
User user2 = (User) userClass2.newInstance();
System.out.println(user2);
//第三种方式:通过完整类名获取class(常用)
Class<?> aClass = Class.forName("com.ruoyi.luoqi.reflect.User");
User user3 = (User) aClass.newInstance();
System.out.println(user3);
System.out.println("方法的基础使用:");
//方法的基础使用
//getMethod: 用于获取类的公共方法(public methods),包括从父类继承的公共方法
Method method = aClass.getMethod("add", int.class,int.class);
//调用方法
Integer invoke = (Integer)method.invoke(user3, 1, 2);
System.out.println(invoke);
//getDeclaredMethod 用于获取类中声明的所有方法,不考虑它们的访问级别(即可以是公共、私有、保护或包内访问)
Method method2 = aClass.getDeclaredMethod("subtract", int.class,int.class);
//调用方法
//如果需要私有方法,需要加上 setAccessible
method2.setAccessible(true);
Integer invoke2 = (Integer)method2.invoke(user3, 2, 1);
System.out.println(invoke2);
} catch (Exception e) {
e.printStackTrace();
}
}
}自定义注解使用
这部分代码是蘑菇博客中关于日志记录自定义注解的使用:OperationLogger
OperationLogger
package com.moxi.mogublog.admin.annotion.OperationLogger;
import com.moxi.mougblog.base.enums.PlatformEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标注该该注解的方法需要记录操作日志
*
* @author 陌溪
* @date 2020年3月23日09:35:57
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLogger {
/**
* 业务名称
*/
String value() default "";
/**
* 平台,默认为WEB端
*/
PlatformEnum platform() default PlatformEnum.ADMIN;
/**
* 是否将当前日志记录到数据库中
*/
boolean save() default true;
}切面:LoggerAspect
package com.moxi.mogublog.admin.annotion.OperationLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.moxi.mogublog.admin.global.RedisConf;
import com.moxi.mogublog.admin.global.SysConf;
import com.moxi.mogublog.commons.config.security.SecurityUser;
import com.moxi.mogublog.commons.entity.ExceptionLog;
import com.moxi.mogublog.utils.*;
import com.moxi.mougblog.base.global.Constants;
import com.moxi.mougblog.base.holder.RequestHolder;
import com.moxi.mougblog.base.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 日志切面
*
* @author 陌溪
* @date 2020年12月31日21:26:04
*/
@Aspect
@Component
@Slf4j
public class LoggerAspect {
@Autowired
RedisUtil redisUtil;
@Autowired
ThreadPoolTaskExecutor threadPoolTaskExecutor;
/**
* 开始时间
*/
Date startTime;
//使用 @Pointcut 注解定义了一个切点,用来匹配带有 OperationLogger 注解的方法。
@Pointcut(value = "@annotation(operationLogger)")
public void pointcut(OperationLogger operationLogger) {
}
//环绕通知包装了目标方法的执行,它首先保存了方法调用的开始时间,然后执行方法,并在方法执行后进行日志记录。
@Around(value = "pointcut(operationLogger)")
public Object doAround(ProceedingJoinPoint joinPoint, OperationLogger operationLogger) throws Throwable {
startTime = new Date();
//先执行业务
Object result = joinPoint.proceed();
try {
// 日志收集
//在 handle 方法中,可以通过反射获取当前执行的方法,获取方法上的 OperationLogger 注解,并根据注解的属性决定如何记录日志。
handle(joinPoint);
} catch (Exception e) {
log.error("日志记录出错!", e);
}
return result;
}
//当被注解的方法抛出异常时,这个通知会捕获异常,并记录异常日志。
@AfterThrowing(value = "pointcut(operationLogger)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, OperationLogger operationLogger, Throwable e) throws Exception {
ExceptionLog exception = new ExceptionLog();
HttpServletRequest request = RequestHolder.getRequest();
String ip = IpUtils.getIpAddr(request);
exception.setIp(ip);
String operationName = AspectUtil.INSTANCE.parseParams(joinPoint.getArgs(), operationLogger.value());
//从Redis中获取IP来源
String jsonResult = redisUtil.get(RedisConf.IP_SOURCE + Constants.SYMBOL_COLON + ip);
if (StringUtils.isEmpty(jsonResult)) {
String addresses = IpUtils.getAddresses(SysConf.IP + SysConf.EQUAL_TO + ip, SysConf.UTF_8);
if (StringUtils.isNotEmpty(addresses)) {
exception.setIpSource(addresses);
redisUtil.setEx(RedisConf.IP_SOURCE + Constants.SYMBOL_COLON + ip, addresses, 24, TimeUnit.HOURS);
}
} else {
exception.setIpSource(jsonResult);
}
//设置请求信息
exception.setIp(ip);
//设置调用的方法
exception.setMethod(joinPoint.getSignature().getName());
exception.setExceptionJson(JSON.toJSONString(e,
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteMapNullValue));
exception.setExceptionMessage(e.getMessage());
exception.setOperation(operationName);
exception.setCreateTime(new Date());
exception.setUpdateTime(new Date());
exception.insert();
}
/**
* 管理员日志收集
*
* @param point
* @throws Exception
*/
private void handle(ProceedingJoinPoint point) throws Exception {
HttpServletRequest request = RequestHolder.getRequest();
Method currentMethod = AspectUtil.INSTANCE.getMethod(point);
//获取操作名称
OperationLogger annotation = currentMethod.getAnnotation(OperationLogger.class);
boolean save = annotation.save();
String bussinessName = AspectUtil.INSTANCE.parseParams(point.getArgs(), annotation.value());
String ua = RequestUtil.getUa();
log.info("{} | {} - {} {} - {}", bussinessName, IpUtils.getIpAddr(request), RequestUtil.getMethod(), RequestUtil.getRequestUrl(), ua);
if (!save) {
return;
}
// 获取参数名称和值
Map<String, Object> nameAndArgsMap = AopUtils.getFieldsName(point);
// 当前操作用户
SecurityUser securityUser = (SecurityUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String paramsJson = JSONObject.toJSONString(nameAndArgsMap);
String type = request.getMethod();
String ip = IpUtils.getIpAddr(request);
String url = request.getRequestURI();
// 异步存储日志
threadPoolTaskExecutor.execute(
new SysLogHandle(ip, type, url, securityUser,
paramsJson, point.getTarget().getClass().getName(),
point.getSignature().getName(), bussinessName,
startTime, redisUtil));
}
}具体内容可以看一下实际代码,同时后续有其他示例,可以继续往下写。