反射

基本概念

1、每个java类运行时都在JVM里表现为一个class对象
2、现在知道的是一个Class类(类型)的对象,如何通过该类型获取和操作与之对应的类成员、类方法。
3、Java反射是指在运行时动态地获取类的信息并操作类的成员(字段、方法、构造函数等)。通过反射,可以在运行时检查类的属性和方法,并且可以在运行时创建对象、调用方法、访问和修改字段等。
4、Class类与java.lang.reflect类库一起对反射技术进行了全力的支持。

核心API

在反射包中,我们常用的类主要有Constructor类表示的是Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象、Field表示Class对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含private)、Method表示Class对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含private)

测试类信息--点击查看代码
public class TestReflect {
    private Integer num;
    private String name;

    public TestReflect(String s) {
        System.out.println("执行带参构造函数");
    }

    public TestReflect() {
        System.out.println("执行默认构造函数");
    }
}

1、获取Class对象
类名.class; 类型.getClass(); Class.forName("类名")

2、获取构造函数创建实例

Class<TestReflect> cl = TestReflect.class;

// 使用默认构造函数创建实例
cl.newInstance();

// 获取所有构造函数
for (Constructor constructor : cl.getDeclaredConstructors()) {
	System.out.println(constructor.toGenericString());
}

3、获取成员变量

点击查看代码
for (Field f : cl.getDeclaredFields()) {

    if (f.getType() == Integer.class) {
        try {
            f.setAccessible(true);
            boolean access = f.isAccessible();
            f.set(obj, 1);
            f.setAccessible(access);
        } catch (Exception e) {
            System.out.println("赋值异常");
        }
    }
    if (f.getType() == String.class) {
        try {
            f.setAccessible(true);
            boolean access = f.isAccessible();
            f.set(obj, "hello");
            f.setAccessible(access);
        } catch (Exception e) {
            System.out.println("赋值异常");
        }
    }
}

4、调用方法

点击查看代码
for (Method m : cl.getDeclaredMethods()) {
    try {
        m.invoke(obj);
    } catch (Exception e) {
    }
}

核心作用与示例

作用

1、程序运行期间的自我检查:反射可以在运行时检查类、接口、字段和方法的信息。这是一种在运行时检查类型信息和成员信息的机制。
2、框架设计:许多Java框架(如Spring和Hibernate)使用反射来实现自动化和灵活的配置。反射使得框架可以在运行时动态地创建对象、调用方法和设置属性。
3、单元测试:反射也常被用于单元测试中,通过反射访问和操作私有方法和字段,以便进行更全面的测试。
4、序列化和反序列化:在Java中,对象可以被序列化为一串字节,然后可以反序列化回原来的对象。这个过程也使用了反射。

灵活性:反射使得代码可以在运行时改变其行为。例如,它可以用来实现设计模式,如工厂模式和代理模式,或者用来在运行时实现接口。

示例

框架设计

spring框架自动织入能力

假设有一个UserService接口和一个UserServiceImpl实现类:

public interface UserService {
    void addUser(User user);
}

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser(User user) {
        userDao.addUser(user);
    }
}
在Spring框架中,可以使用注解或XML配置来指定依赖关系。下面是使用注解的方式:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    public void addUser(User user) {
        userDao.addUser(user);
    }
}

在这个例子中,通过@Autowired注解将UserDao对象注入到UserServiceImpl类中的userDao字段。Spring框架在运行时会通过反射来实例化UserServiceImpl类,并通过反射来获取userDao字段,并将UserDao对象注入到该字段。

通过反射,Spring框架可以在运行时动态地获取类的信息并操作类的成员,从而实现依赖注入的功能。通过注解或配置,Spring框架可以在运行时通过反射来查找和创建依赖对象,并将依赖对象注入到需要依赖的类中。

手动练习实现织入能力

通过反射能力,模拟实现spring的依赖注入功能。
1、汽车类定义

点击查看代码
package com.demo.reflect;

import java.util.Objects;

/**
 * @Descrpition TODO
 * @Author liutai
 * @Date 2023/9/10 17:34
 **/
class Car {
    @MyAutoWire
    Driver driver;

    public void drive() {
        if (Objects.nonNull(driver)) {
            System.out.println("姓名:" + driver.getName() + ", 年龄:" + driver.getAge()  + ", driving this car");
        } else {
            System.out.println("please set a driver firstly");
        }
    }
}
2、司机类定义
点击查看代码
package com.demo.reflect;

/**
 * @Descrpition TODO
 * @Author liutai
 * @Date 2023/9/10 17:34
 **/
class Driver {
    private String name;
    private String age;

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

    public String getName() {
        return name;
    }

    public String getAge() {
        return age;
    }
}

3、织入注解

点击查看代码
package com.demo.reflect;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;

@Retention(RetentionPolicy.RUNTIME)
@Target(value={ FIELD})
public @interface MyAutoWire {
}

4、通过反射为汽车类注入一个司机

点击查看代码
package com.demo.reflect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
 * @Descrpition TODO
 * @Author liutai
 * @Date 2023/9/10 17:21
 **/
public class TestAutoWire {

    public static void main(String[] args) {
        Car car = new Car();
        car.drive();

        // 开始自动注入drivier
        Field[] fields = car.getClass().getDeclaredFields();
        for (Field f : fields) {
            for (Annotation ano : f.getAnnotations()) {
                if (ano instanceof MyAutoWire) {
                    // 如果当前变量使用了@MyAutoWire注解,那么使用反射注入变量信息
                    boolean access = f.isAccessible();
                    f.setAccessible(true);
                    try {
                        Constructor objConstruct = Class.forName("com.demo.reflect.Driver").getDeclaredConstructor(String.class, String.class);
                        Driver driver = (Driver) objConstruct.newInstance("xiaoming", "6");
                        f.set(car, driver);
                        f.setAccessible(access);
                    } catch (Exception e) {
                    }
                }
            }
        }
        // 再次执行car的drive方法
        car.drive();

    }
}
// 执行结果
please set a driver firstly
姓名:xiaoming, 年龄:6, driving this car

动态代理

动态代理常用于AOP(面向切面编程)和RPC(远程过程调用)等场景。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxyExample {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();
        
        // 创建代理对象
        UserService proxy = (UserService) createProxy(userService);
        
        // 调用代理对象的方法
        proxy.addUser("Alice");
    }
    
    // 创建代理对象的方法
    public static Object createProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 类加载器
            target.getClass().getInterfaces(),  // 接口数组
            new InvocationHandler() {           // InvocationHandler对象
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 在调用目标方法之前执行一些操作
                    System.out.println("Before invoking method: " + method.getName());
                    
                    // 调用目标方法
                    Object result = method.invoke(target, args);
                    
                    // 在调用目标方法之后执行一些操作
                    System.out.println("After invoking method: " + method.getName());
                    
                    return result;
                }
            }
        );
    }
}

// 目标接口
interface UserService {
    void addUser(String name);
}

// 目标实现类
class UserServiceImpl implements UserService {
    public void addUser(String name) {
        System.out.println("Adding user: " + name);
    }
}

在这个示例中,我们首先定义了一个目标接口UserService和一个目标实现类UserServiceImpl。然后,通过createProxy()方法使用反射创建代理对象。在InvocationHandler的invoke()方法中,我们可以在调用目标方法之前和之后执行一些操作。最后,我们使用代理对象调用目标方法addUser()。

运行示例代码,输出如下:

Before invoking method: addUser
Adding user: Alice
After invoking method: addUser
从输出结果可以看出,在调用代理对象的addUser()方法时,会先执行invoke()方法中的操作,然后再调用目标方法addUser(),最后再执行invoke()方法中的操作。

通过这种方式,我们可以使用反射来动态地创建代理对象,并在代理对象的方法调用前后执行一些操作,从而实现动态代理的功能。动态代理常用于AOP(面向切面编程)和RPC(远程过程调用)等场景,可以在不修改目标类的情况下,对目标类的方法进行增强或拦截。

posted @ 2023-09-10 19:28  六太_延麒  阅读(10)  评论(0编辑  收藏  举报