反射核心操作 Link to heading

public class ReflectDemo {
    public static void main(String[] args) throws Exception{
        //1.获取class对象
        Class<?>clazz=Class.forName("com.example.Person");

        //2.创建对象实例(调用有参构造实例)
        Constructor<?>constructor=clazz.getConstructor(String.class,int.class);
        Object person=constructor.newInstance("张三",25);

        //3. 调用方法
        Method sayHello=clazz.getMethod("sayHello");
        sayHello.invoke(person);

        //4. 修改私有属性
        Field nameField=clazz.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(person,"李四");
        sayHello.invoke(person);
    }
}

优缺点: Link to heading

优点 Link to heading

  • 动态性:运行时加载类,实现高度灵活的代码(如插件化开发)。
  • 突破封装:可以访问私有属性和方法(慎用!破坏封装性)。

缺点 Link to heading

  • 性能开销:反射操作比直接调用慢(JVM 无法优化反射代码)。
  • 安全隐患:可以绕过权限检查,破坏封装性。
  • 代码复杂度:反射代码可读性差,调试困难。

首先拿到class

使用构造器创建实例

调用方法使用invoke

私有属性SetAccessible

代理模式 Link to heading

使用代理对象来代替真实对象的访问

静态代理 Link to heading

编译时就将接口,实现类,代理类都变成了一个个实际的class文件

接口

public interface SmsService {
    String send(String message);
}

实现类

package com.example.impl;
import com.example.SmsService;
public class SmsServiceImpl implements SmsService {
    public String send(String message){
        System.out.println("Send message : " + message);
        return message;
    }
}

代理类实现

package com.example.impl;
import com.example.SmsService;
public class SmsProxy implements SmsService {
    private final SmsService smsService;
    public SmsProxy(SmsService smsService) {
        this.smsService = smsService;
    }

    @Override
    public String send(String message) {
        System.out.println("before send");
        smsService.send(message);
        System.out.println("after send");
        return null;
    }
}

测试

package com.example;

import com.example.impl.SmsProxy;
import com.example.impl.SmsServiceImpl;

public class Main {
    public static void main(String[] args) {
        SmsService smsService=new SmsServiceImpl();
        SmsProxy smsProxy=new SmsProxy(smsService);
        smsProxy.send("java");
    }
}

动态代理 Link to heading

相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( CGLIB 动态代理机制)。

Java 中两种实现方式

  1. JDK 动态代理(基于接口)
  2. CGLIB 动态代理(基于子类继承)

JDK动态代理 Link to heading

** 核心类和接口** Link to heading

类/接口 作用
java.lang.reflect.Proxy 生成代理类的工厂类,提供创建代理对象的静态方法
InvocationHandler 调用处理器接口,由开发者实现,用于定义代理逻辑(如前置/后置增强)

在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心。

Proxy 类中使用频率最高的方法是:newProxyInstance() ,这个方法主要用来生成一个代理对象。

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        ......
    }

这个方法一共有 3 个参数:

  1. loader :类加载器,用于加载代理对象。
  2. interfaces : 被代理类实现的一些接口;
  3. h : 实现了 InvocationHandler 接口的对象;

当我们的动态代理对象调用方法时,实际上会被转发到InvocationHandler 接口类的 invoke 方法来调用:

public interface InvocationHandler {

    /**
     * 当你使用代理对象调用方法的时候实际会调用到这个方法
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

invoke() 方法有下面三个参数:

  1. proxy :动态生成的代理类
  2. method : 与代理类对象调用的方法相对应
  3. args : 当前 method 方法的参数

具体使用方法 Link to heading

因为实际上调用的是InvocatinHandler接口的invoke方法,所以我们需要先实现Invocationhandler接口作为我们的动态代理类,然后再通过Proxy类中的newProxyInstance()方法,通过传入类加载器(加载代理对象),接口,动态代理类来生成代理对象

定义接口

public interface SmsService {
    String send(String message);
}

实现接口

public class SmsServiceImpl implements SmsService {
    public String send(String message){
        System.out.println("Send message : " + message);
        return message;
    }
}

实现InvocationHandler接口,定义动态代理类

public class DebugInvocationHandler implements InvocationHandler {
    //代理类中的真实对象
    private Object target;

    public DebugInvocationHandler(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 JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new DebugInvocationHandler(target)
        );
    }
}

实际使用

public class Main {
    public static void main(String[] args) {
       SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
       smsService.send("java  e");
    }
}

总结流程:

Proxy.newProxyInstance()通过反射动态生成代理对象,传递参数为classloader,interfaces和invocationHandler,代理类中的方法调用会被转发到invocationHandler.invoke方法。

GGLIB动态代理 Link to heading

JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。

静态代理和动态代理的对比 Link to heading

  1. 灵活性:动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!
  2. JVM 层面:静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。