反射的理解

1. 反射是什么

反射是 Java 中的一个强大特性,允许程序在运行时检查和操作类、方法、字段等信息。它是一种动态机制,可以在运行时解析类,获取类的成员变量、方法、构造器等,并对它们进行操作。

2. 为什么使用反射

  • 灵活性:反射可以在运行时动态地操作类,而不需要在编译时知道类的具体信息。

  • 可扩展性:通过反射,可以编写通用的代码,支持多种不同的类和对象。

  • 框架和工具:许多框架和工具(如 Spring、Hibernate 等)都广泛使用反射来实现功能,如依赖注入、对象关系映射等。

3. 反射的基本用法

3.1 获取 Class 对象

在 Java 中,每个类都有一个对应的 Class 对象,它是反射的入口。可以通过以下方式获取 Class 对象:

  • 使用类的 .class 属性:Class clazz = Person.class;

  • 使用对象的 .getClass() 方法:Class clazz = personInstance.getClass();

  • 使用类加载器:Class clazz = Class.forName("com.example.Person");

3.2 获取类的信息

一旦获取了 Class 对象,就可以通过它获取类的各种信息,如:

  • 获取类名clazz.getName();

  • 获取构造器clazz.getConstructors();clazz.getConstructor(Class<?>... parameterTypes);

  • 获取方法clazz.getMethods();clazz.getMethod(String name, Class<?>... parameterTypes);

  • 获取字段clazz.getFields();clazz.getField(String name);

3.3 操作对象

通过反射,可以动态地创建对象、调用方法、访问字段等。

  • 动态创建对象

    Person person = (Person) clazz.newInstance(); // 已过时,推荐使用 Constructor

    或者:

    Constructor constructor = clazz.getConstructor(String.class, int.class);
    Person person = (Person) constructor.newInstance("John", 25);
  • 动态调用方法

    Method method = person.getClass().getMethod("sayHello", String.class);
    method.invoke(person, "World");
  • 动态访问字段

    Field field = person.getClass().getField("name");
    field.set(person, "Alice");

4. 反射的实际应用

4.1 动态加载和运行代码

例如,在一个插件系统中,可以通过反射动态加载并运行插件的代码:

Class<?> pluginClass = Class.forName("com.example.Plugin");
Object plugin = pluginClass.newInstance();
Method executeMethod = pluginClass.getMethod("execute");
executeMethod.invoke(plugin);
4.2 Java Bean 操作

通过反射可以动态地操作 Java Bean 的属性,如:

Person person = new Person("John", 30);
Field ageField = person.getClass().getField("age");
ageField.set(person, 35);
4.3 测试框架

许多测试框架(如 JUnit)都使用反射来运行测试方法:

TestRunner runner = new TestRunner(MyTest.class);
runner.run();

框架通过反射找到测试类中的测试方法,并调用它们。

4.4 ORM 框架

ORM 框架(如 Hibernate)使用反射将数据库表映射到 Java 类:

Session session = sessionFactory.openSession();
session.save(person);

Hibernate 通过反射操作 Person 类的字段,将其数据存储到数据库中。

5. 反射的优缺点

优点
  • 灵活性:可以在运行时动态地操作类。

  • 可扩展性:支持编写通用代码,适用于多种类和对象。

  • 框架和工具:许多现代框架和工具都依赖反射来实现核心功能。

缺点
  • 性能开销:反射操作的性能比直接调用稍差,因为它需要解析类信息。

  • 安全性问题:可能破坏封装性,访问或修改私有成员。

  • 复杂性:使用反射会使代码变得复杂,难以阅读和维护。

6. 示例代码

以下是个简单的反射示例,展示如何动态创建对象并调用方法:

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectionExample {
    public static void main(String[] args) {
        try {
            // 获取类
            Class<?> clazz = Class.forName("com.example.Person");

            // 创建对象
            Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
            Person person = (Person) constructor.newInstance("John", 30);

            // 调用方法
            Method sayHelloMethod = person.getClass().getMethod("sayHello", String.class);
            sayHelloMethod.invoke(person, "World");

            // 访问字段
            Field ageField = person.getClass().getField("age");
            System.out.println("Age: " + ageField.get(person));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Person如下:

public class Person {
    public String name;
    public int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void sayHello(String target) {
        System.out.println("Hello, " + target + "! My name is " + this.name);
    }
}

在黑马Java下部中,反射通常的俩中作用:

6.1:保存任意对象数据

 6.2:创建对象并运行方法

 

7.Skyakeout 中的应用

/**
 * 功能字段自动填充切面
 */
@Component
@Aspect
@Slf4j
public class AutoFillAspect {

    /**
     * 切入点
     */
    @Pointcut("execution(public * com.sky.mapper.*.*(..))&&@annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    /**
     * 前置通知,公共字段自动填充
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint){
        //通过 JoinPoint 获取方法参数和上下文信息。
        log.info("开始公共字段自动填充...");
        //获取当前被拦截的方法上的数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
        OperationType operationType = autoFill.value();//获得注解中的数据库操作类型
        //获取当前被拦截的方法上的——实体对象
        Object[] args = joinPoint.getArgs();//获得方法的参数
        if(args.length == 0||args == null){
            return;
        }
        Object  entity = args[0];//获得实体对象
        //准备赋值的数据
        LocalDateTime  now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();

        //根据不同的操作类型,通过反射赋予不同的值
        if(operationType == OperationType.INSERT){
            //为3个公共字段赋值
            try {
                //使用反射动态调用实体对象的 setter 方法。
                Method setCreateTime = entity.getClass().getDeclaredMethod("setCreateTime",LocalDateTime.class);
                Method setCreateUser = entity.getClass().getDeclaredMethod("setCreateUser",Long.class);
                Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime",LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser", Long.class);
                //通过反射设置值
                //invoke 是 Java 反射机制中的一个核心方法,用于动态调用对象的方法。
                //invoke 方法允许你在运行时调用一个对象的任意方法
                setCreateTime.invoke(entity,now);
                setCreateUser.invoke(entity,currentId);
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        if(operationType == OperationType.UPDATE){
            //为2个公共字段赋值
            try {
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME,LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                //通过反射设置值
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值