Java 的反射与代理

67 阅读3分钟

反射

Java 类型信息的获取

RTTI(Run-Time Type-Identification)

Java程序运行时会有类型转换,因此需要在运行时获取类型信息来判断类型转换是否有效。

Java 反射机制

动态获取类信息以及动态调用对象的方法的功能称为Java的反射机制。

Object 类、 Class 类 Method 类

Object

Java中所有类都继承自Object类。拥有Object类的方法,例如:

  1. hashCode()
  2. equals()
  3. clone()
  4. toString()
  5. notify()
  6. wait()

Class

每当Java虚拟机装载一个新类时,都会在Java堆中创建一个Class实例,这个实例代表这个Class类型。以下为此类的一些方法,其中Method和Field也是Java中的类。

  1. Method[] getMethods()
  2. Field[] getFields()
  3. Construct<?>[] getDeclaredConstructors()

Method类

使用Method类的invoke方法调用类方法,需要传入一个类实例,有参数也需要传参数。

通过反射创建一个类

public static class Apple{
    @Override
    public String toString(){
        return "I am apple";
    }
}
public static void main(String[] args) throws Exception {
    Constructor<Apple> constructor = Apple.class.getDeclaredConstructor();
    Apple one = constructor.newInstance();
    System.out.println(one.toString());
}

通过反射调用一个类的方法

public static class Apple{
    public String sayHello(){
        return "Hello";
    }
    public String hello(String toOne){
        return "Hello " + toOne;
    }
}
public static void main(String[] args) {
    try {
        Apple apple = new Apple();
        Class<?> cls = apple.getClass();
        Method method = cls.getMethod("hello", String.class);
        System.out.println(method.invoke(apple,"me"));
    }catch (Exception e){
        e.printStackTrace();
    }
}

代理

有时候不能直接引用某个对象,而想要间接访问某个对象。代理就是提供了间接访问某个对象的功能。

代理中的角色:

  • 抽象角色:真实角色和代理角色都有的接口。
  • 真实角色:被代理的角色,真正访问的对象。
  • 代理角色:代理真实角色的角色,提供了访问真实角色的方法,通过代理可以间接访问真实角色。

静态代理

private interface comAction{
    void sayHello();
}

private static class realActor implements comAction{

    @Override
    public void sayHello() {
        System.out.println("Hello the World, I am the real Actor");
    }
}

private static class proxyActor implements comAction{
    private final realActor ra;
    proxyActor(){
        this.ra = new realActor();
    }
    @Override
    public void sayHello() {
        beforeSayHello();
        ra.sayHello();
        afterSayHello();
    }

    public void beforeSayHello(){
        System.out.println("Before say hello");
    }

    public void afterSayHello(){
        System.out.println("After say hello");
    }

}

public static void main(String[] args) {
    proxyActor pa = new proxyActor();
    pa.sayHello();
}

动态代理

Java.lang.reflect.proxy 提供了一组静态方法来为一组接口实现代理类和其对象。

  • 代理对象关联的调用处理器:getInvocationHandler( Object proxy)
  • 关联于指定类的装载器和一组动态接口的代理类的类对象:getProxyClass(ClassLoader cl,Class [] interfacces)
  • 判断是否为一个动态代理类:isProxy(Class cl)
  • 为给定的类装载器、动态接口的代理类和调用处理器生成动态代理类实例:newProxyInstance(ClassLoader cl,Class[] interfaces,InvocationHandler h)

动态代理与包的位置:如果代理的接口是public,那么代理类应该定义在最顶层包,如果非public,则应该将代理类定义在接口所定义的位置。这样做以解决代理类和包的管理导致的问题。

代理类不应该被继承,应使用final修饰符修饰。

对于一对相同的接口(包括顺序相同),JVM不会创建两次代理类,会返回上一次的代理类。


// 定义一个接口
interface Subject {
    void request();
}

// 实现接口的类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 创建InvocationHandler实现类
class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在方法调用之前执行一些操作
        System.out.println("Before method: " + method.getName());

        // 调用原始方法
        Object result = method.invoke(target, args);

        // 在方法调用之后执行一些操作
        System.out.println("After method: " + method.getName());

        return result;
    }
}

public class C5experimentApplication {
    public static void main(String[] args) {
        // 创建目标对象
        RealSubject realSubject = new RealSubject();

        // 创建InvocationHandler
        InvocationHandler handler = new MyInvocationHandler(realSubject);

        // 创建代理对象
        Subject subject = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                new Class[]{Subject.class},
                handler);

        // 调用代理对象的方法
        subject.request();
    }
}