java中关于反射与动态代理

时间:2024-07-12 07:58:25

java中关于反射与动态代理

java反射技术

1、什么是反射?
Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。所谓的反射,实际上是获取到类的字节码.class文件,再通过Class对象获取类的方法和属性。

反射案例,
反射(Reflection)是 Java 中一种强大的机制,允许在运行时动态地获取类的信息、调用类的方法、操作类的属性等。下面是几个涵盖反射各方面的案例示例:

  1. 获取类的信息
    通过反射可以获取类的信息,如类名、方法、字段等。

import java.lang.reflect.*;

public class ReflectionExample {
    public static void main(String[] args) throws Exception {
        // 获取类的 Class 对象
        Class<?> clazz = Class.forName("java.util.ArrayList");

        // 获取类名
        String className = clazz.getName();
        System.out.println("Class Name: " + className);

        // 获取类的方法信息
        Method[] methods = clazz.getDeclaredMethods();
        System.out.println("Methods:");
        for (Method method : methods) {
            System.out.println(method.getName());
        }

        // 获取类的字段信息
        Field[] fields = clazz.getDeclaredFields();
        System.out.println("Fields:");
        for (Field field : fields) {
            System.out.println(field.getName());
        }

        // 获取类的构造方法信息
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        System.out.println("Constructors:");
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor.toString());
        }
    }
}
  1. 动态创建对象
    通过反射可以动态创建类的对象,并调用其方法。
import java.lang.reflect.*;

public class DynamicObjectCreation {
    public static void main(String[] args) throws Exception {
        // 获取类的 Class 对象
        Class<?> clazz = Class.forName("java.util.ArrayList");

        // 使用反射创建对象
        Object arrayList = clazz.getDeclaredConstructor().newInstance();

        // 调用对象的方法
        Method addMethod = clazz.getDeclaredMethod("add", Object.class);
        addMethod.invoke(arrayList, "Hello");
        addMethod.invoke(arrayList, "World");

        // 输出结果
        System.out.println(arrayList); // 输出:[Hello, World]
    }
}
  1. 修改私有字段
    通过反射可以访问和修改类中的私有字段。
import java.lang.reflect.*;

public class PrivateFieldAccess {
    public static void main(String[] args) throws Exception {
        // 创建一个类的实例
        MyClass obj = new MyClass();
        
        // 获取类的 Class 对象
        Class<?> clazz = obj.getClass();

        // 获取私有字段
        Field privateField = clazz.getDeclaredField("privateField");
        
        // 设置字段可访问
        privateField.setAccessible(true);
        
        // 修改私有字段的值
        privateField.set(obj, "New value");
        
        // 访问私有字段的值
        System.out.println(privateField.get(obj)); // 输出:New value
    }
    
    static class MyClass {
        private String privateField = "Initial value";
    }
}
  1. 执行私有方法
    通过反射可以调用类中的私有方法。
java
import java.lang.reflect.*;

public class PrivateMethodInvocation {
    public static void main(String[] args) throws Exception {
        // 创建一个类的实例
        MyClass obj = new MyClass();
        
        // 获取类的 Class 对象
        Class<?> clazz = obj.getClass();
        
        // 获取私有方法
        Method privateMethod = clazz.getDeclaredMethod("privateMethod");
        
        // 设置方法可访问
        privateMethod.setAccessible(true);
        
        // 调用私有方法
        privateMethod.invoke(obj);
    }
    
    static class MyClass {
        private void privateMethod() {
            System.out.println("Executing private method");
        }
    }
}
  1. 使用反射实现通用的 JSON 序列化工具
    通过反射,可以实现一个通用的 JSON 序列化工具,将任意 Java 对象序列化为 JSON 字符串。
java
import com.alibaba.fastjson.JSON;
import java.lang.reflect.*;

public class JSONSerializer {
    public static void main(String[] args) throws Exception {
        MyClass obj = new MyClass("Alice", 25);
        
        // 序列化对象为 JSON 字符串
        String jsonString = serializeObject(obj);
        System.out.println(jsonString);
        
        // 反序列化 JSON 字符串为对象
        MyClass newObj = deserializeObject(jsonString, MyClass.class);
        System.out.println(newObj.getName());
        System.out.println(newObj.getAge());
    }
    
    // 序列化对象为 JSON 字符串
    public static String serializeObject(Object obj) {
        return JSON.toJSONString(obj);
    }
    
    // 反序列化 JSON 字符串为对象
    public static <T> T deserializeObject(String jsonString, Class<T> clazz) {
        return JSON.parseObject(jsonString, clazz);
    }
    
    static class MyClass {
        private String name;
        private int age;
        
        public MyClass(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        public String getName() {
            return name;
        }
        
        public int getAge() {
            return age;
        }
    }
}

这些示例展示了反射在 Java 中的广泛应用,从获取类信息、动态创建对象、修改私有字段和调用私有方法,到实现通用的 JSON 序列化工具。反射使得在编译时未知类的情况下,能够在运行时进行灵活的操作和扩展。

反射不做过多解释

动态代理 这里主要讲JDK的动态代理

JDK动态代理的步骤
步骤一:定义接口
首先,我们定义一个简单的接口UserService,包含两个方法:save和delete。

java
public interface UserService {
    void save(String username);
    void delete(String username);
}
步骤二:实现接口
接下来,实现UserService接口的具体类UserServiceImpl,用于实际执行保存和删除用户的操作。

java
public class UserServiceImpl implements UserService {
    @Override
    public void save(String username) {
        System.out.println("Saving user: " + username);
    }

    @Override
    public void delete(String username) {
        System.out.println("Deleting user: " + username);
    }
}

步骤三:实现InvocationHandler
创建一个实现了InvocationHandler接口的类UserServiceInvocationHandler,在这里我们将为方法调用添加日志记录功能。

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

public class UserServiceInvocationHandler implements InvocationHandler {
    private final UserService target;

    public UserServiceInvocationHandler(UserService target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before calling method: " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After calling method: " + method.getName());
        return result;
    }
}

步骤四:创建代理对象
在主程序中,我们使用Proxy.newProxyInstance方法创建代理对象。这里将实现UserService接口的代理对象,它将会在每次调用save和delete方法时,自动在控制台输出调用前后的日志信息。

java
import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        UserService realService = new UserServiceImpl();
        UserServiceInvocationHandler handler = new UserServiceInvocationHandler(realService);

        UserService proxyService = (UserService) Proxy.newProxyInstance(
                Main.class.getClassLoader(),
                new Class[]{UserService.class},
                handler
        );

        proxyService.save("Alice");
        proxyService.delete("Bob");
    }
}

运行结果
当你运行上述代码时,控制台会输出以下信息:

Before calling method: save
Saving user: Alice
After calling method: save
Before calling method: delete
Deleting user: Bob
After calling method: delete

这些日志信息显示了在调用代理对象的save和delete方法时,UserServiceInvocationHandler中的invoke方法分别在方法调用前后被执行,从而实现了日志记录的功能。

总结
这个例子展示了如何使用JDK动态代理为一个实现了接口的类(UserServiceImpl)生成代理对象,并在方法调用前后添加额外的日志记录逻辑。这种方式可以轻松地为现有代码添加各种额外的横切关注点(cross-cutting concerns),如日志记录、性能监控等,而无需修改原始类的代码。

主要步骤

  UserService realService = new UserServiceImpl();
    UserServiceInvocationHandler handler = new UserServiceInvocationHandler(realService);

    UserService proxyService = (UserService) Proxy.newProxyInstance(
            Main.class.getClassLoader(),
            new Class[]{UserService.class},
            handler
    );

我现在的理解是,JDK的动态代理技术会根据我们想要代理的接口去获取需要实现的代理方法从而去构建一个代理类
在代理类中具有和实现这个接口的子类一样的方法,调用newProxyInstance会创建一个这个代理类的对象,而创建这个对象需要三个参数
一、第一个参数 Main.class.getClassLoader() 指定了类加载器,用于加载生成的代理类,一般这个参数用于当前方法所在类的加载器。具体原因我还未搞清楚
二、第二个参数 new Class[]{UserService.class} 是一个 Class 数组,指定了代理类要实现的接口,这里代理类会实现 UserService接口。这个参数可以去放多个接口,意味着可以代理多个接口的方法进行增强前提是所要代理的类需要实现数组中的接口
三、第三个参数是一个 InvocationHandler 对象,用于处理代理对象的方法调用。实际上InvocationHandler 可以采用匿名实现类来实现,起作用主要是其重写的invoke的方法invoke(Object proxy, Method method, Object[] args)
这个方法可以传入一个被代理的类的对象进行原始方法的调用者指定,也就是谁调用后面的method,Method method是用来指定调用的方法这里其实只有创建了代理对象并且使用代理对象去调用方法时才会知道,因为代理对象的方法名字和被代理对象时一致的我猜这里是直接获取的方法名,Object[] args则是用来指定调用方法的入参,调用被代理的对象的方法的入参。
四、生成对象 UserService proxyService = (UserService) Proxy.newProxyInstance();其实这里生成的是代理类的对,只是代理类字节码文件在jvm中不是我们生成的,我们看不到,实际上是代理类对象

简化写法

   public static void main(String[] args) {
        
        Star bigStar = new BigStar("鸡哥");
        Star star = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("代理开始");
                if ("sing".equals(method.getName())) {
                    System.out.println("代理开始唱歌");
                } else {
                    System.out.println("代理开始跳舞");
                }
                Object result = method.invoke(bigStar, args);
                System.out.println("代理结束");
                return result;
            }
        });
        star.sing("鸡哥");
    }

JDK动态代理问题一 为什么只能代理接口的?而不能代理实现类的。

关于JDK的动态代理设计 ,他底层生产的代理类会继承一个proxy类并且实现要代理的接口实现类的接口,如果是代理接抽象类或者类的 就需要去继承这个抽象类的或者类,这样就无法再去继承peoxy类,从而无法实现JDK的动态代理,这是设计问题。

相关文章