JAVA动态代理

270 阅读7分钟

JAVA动态代理

动态代理(Dynamic Proxy) 是Java中实现 AOP(面向切面编程) 的核心技术之一。它允许开发者在运行时动态生成代理对象,从而在不修改原始代码的前提下,为方法调用添加额外的逻辑(如日志、事务、权限控制等)。其核心优势包括:

  • 解耦业务逻辑与非功能性需求:将核心业务逻辑(如用户管理)与横切关注点(如日志、事务)分离,提升代码可维护性。
  • 减少冗余代码:通过统一代理逻辑,避免为每个类重复编写相同的增强代码。
  • 灵活扩展:动态代理可适应接口或类的变化,只需调整代理逻辑,无需修改大量静态代理类。

1、代理模式

JAVA的动态代理是与设计模式中的代理模式相关的,什么是代理模式呢?

代理模式是指通过创建一个**代理对象(Proxy)控制对目标对象(Target)**的访问。代理对象在调用目标方法前后可以执行额外操作。

image-20250216204216430

  • ISubject:定义了Proxy和RealSubject的公共对外接口方法。

  • RealSubject:真正实现业务逻辑的类,定义Proxy所代表的真实体。

  • Proxy:用来代理和封装真实体,使得代理可以访问真实体。

如果根据字节码的创建时机来分类,可以分为静态代理和动态代理:

  • 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。
  • 而动态代理的源码是在程序运行期间JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件。

2、静态代理

首先来编写静态代理实例,直接创建UserService接口与其实现类UserServiceImpl以及代理类Proxy。

UserService接口:即是代理模式结构图中的ISubject接口。

public interface UserService {
	/**
	 * 添加用户
	 * @param username 用户名
	 */
	void addUser(String username);

	/**
	 * 删除用户
	 * @param username 用户名
	 */
	void deleteUser(String username);

}

UserServiceImpl类:真实体类。

public class UserServiceImpl implements UserService {

    @Override
    public void addUser(String username) {
       System.out.println("添加用户: " + username);
    }

    @Override
    public void deleteUser(String username) {
       System.out.println("删除用户: " + username);
    }

}

Proxy:代理类。通过代理类输出一些日志信息,即对原本的实体类的功能进行增强。

public class UserServiceProxy implements UserService {

    private UserService target; // 被代理的对象

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

    @Override
    public void addUser(String username) {
       before();
       target.addUser(username);
       after();
    }

    @Override
    public void deleteUser(String username) {
       before();
       target.deleteUser(username);
       after();
    }

    private void before() {
       // 在执行方法之前执行的操作
       System.out.println("Log start time:" + LocalDateTime.now());
    }

    private void after() {
       // 在执行方法之后执行的操作
       System.out.println("Log end time:" + LocalDateTime.now());
    }

}

创建一个客户端进行测试:

public class Main {

    public static void main(String[] args) {
       UserService userService = new UserServiceImpl();
       UserServiceProxy userServiceProxy = new UserServiceProxy(userService);
       userServiceProxy.addUser("张三");
       System.out.println();
       userServiceProxy.deleteUser("张三");
    }

}

输出结果:

image-20250216210157541

可见,静态代理就是通过代理类去访问目标对象(真实体),从而屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情,从而达到功能增强的目的。

静态代理虽然实现简单,但是存在很大缺陷。当在接口中增加一些方法时,不仅在真实体中需要进行相应的实现,在代理类中也需要进行实现,十分的麻烦,且工作量大。其次,当需要代理多个类时,通常需要为每个类创建一个代理类。这会导致代码冗余和维护困难。故而产生动态代理方法,也就是动态的生成代理类。

3、动态代理

(1)JDK动态代理

JDK动态代理主要涉及两个类:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

Proxy主要是用其newProxyInstance方法获取动态生成的代理类对象,通过实现InvocationHandler接口并重写invoke方法实现调用逻辑。具体的看如下代码,仍然使用之前的UserService与UserServiceImpl:

逻辑处理类:实现InvocationHandler接口并重写invoke

public class LogInvocationHandler implements InvocationHandler {

    private Object target;

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

    /**
     * 代理的方法逻辑
     *
     * @param proxy  代理对象
     * @param method 代理对象调用的方法
     * @param args   当前method方法的参数
     * @return 代理结果
     * @throws Throwable 异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       before();
       Object result = method.invoke(target, args); // 利用反射来调用传入的目标对象(target)的方法
       after();
       return result;
    }

    private void before() {
       // 在执行方法之前执行的操作
       System.out.println("Log start time:" + LocalDateTime.now());
    }

    private void after() {
       // 在执行方法之后执行的操作
       System.out.println("Log end time:" + LocalDateTime.now());
    }

}

代理工厂类:获取动态生成的代理类对象。

public class JdkProxyFactory {

    public static Object getProxy(Object target) {
       return Proxy.newProxyInstance(
             target.getClass().getClassLoader(), // 目标类的类加载器
             target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
             new LogInvocationHandler(target)   // 代理对象对应的自定义InvocationHandler
       );
    }

}

测试:

public class Main {

    public static void main(String[] args) {
       UserService proxy = (UserService) JdkProxyFactory.getProxy(new UserServiceImpl());
       proxy.addUser("张三");
       System.out.println();
       proxy.deleteUser("张三");
    }

}

结果输出:

image-20250216213357413

从上可以看出,JDK动态代理并不用去编写代理类,只需要维护需要代理部分的逻辑,主要步骤如下:

  1. 定义一个接口及其实现类;
  2. 自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
  3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;

也就是说:我们通过Proxy 类的 newProxyInstance()创建的代理对象在调用方法的时候,实际就会调用实现InvocationHandler 接口中的invoke方法。而这里就是实现一些额外功能的地方,但需要记得返回目标方法处理的结果,最后就可以通过代理获取到结果,并实现功能的增加。

(2)CGLIB动态代理

静态代理与JDK动态代理都基于接口的,所以要求代理类一定是有定义接口的,这也存在着一定局限。而CGLIB动态代理就避免这一问题。CGLIB动态代理是通过继承的方式生成目标类的子类来实现代理类的。其流程是与JDK动态代理类似的,但原理可不是一样的。JDK动态代理是基于反射机制实现的,而CGLIB动态代理是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。

使用CGLIB是需要添加依赖的,直接在maven工程的pom.xml添加如下依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

直接上代码,首先这里不在需要接口了,我们直接定义一个普通类UserService。

public class UserService {
    /**
     * 添加用户
     * @param username 用户名
     */
    public void addUser(String username){
       System.out.println("添加用户: " + username);
    };

    /**
     * 删除用户
     * @param username 用户名
     */
    public void deleteUser(String username){
       System.out.println("删除用户: " + username);
    };

}

处理自定义逻辑的代码,实现MethodInterceptor

public class LogMethodInterceptor implements MethodInterceptor {

    /**
     *
     * @param object 被代理的对象(需要增强的对象)
     * @param method 被拦截的方法(需要增强的方法)
     * @param args 方法参数
     * @param methodProxy 对方法的代理,invokeSuper方法表示对被代理对象方法的调用
     * @return 代理结果
     * @throws Throwable 异常
     */
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
       before();
       Object result = methodProxy.invokeSuper(object, args);
       after();
       return result;
    }

    private void before() {
       // 在执行方法之前执行的操作
       System.out.println("Log start time:" + LocalDateTime.now());
    }

    private void after() {
       // 在执行方法之后执行的操作
       System.out.println("Log end time:" + LocalDateTime.now());
    }

}

获取代理类:

public class CglibProxyFactory {

    public static Object getProxy(Class<?> clazz) {
       // 创建动态代理增强类
       Enhancer enhancer = new Enhancer();
       // 设置类加载器
       enhancer.setClassLoader(clazz.getClassLoader());
       // 设置被代理类
       enhancer.setSuperclass(clazz);
       // 设置方法拦截器
       enhancer.setCallback(new LogMethodInterceptor());
       // 创建代理类
       return enhancer.create();
    }

}

测试与结果输出:

public static void main(String[] args) {
    UserService proxy = (UserService) CglibProxyFactory.getProxy(UserService.class);
    proxy.addUser("张三");
    System.out.println();
    proxy.deleteUser("张三");
}

CGLIB 动态代理步骤:

  1. 定义一个类;
  2. 自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
  3. 通过 Enhancer 类的 create()创建代理类;

需要注意的是,使用CGLIB时注意JDK8版本,JDK8以上会出现报错,当然如果只能使用高版本JDK也是有方法可以解决的,可以自行度娘。

4、总结

  • 动态代理 是Java实现AOP的核心技术,通过运行时生成代理对象解耦业务逻辑与非功能性需求。
  • JDK动态代理 适用于接口代理,CGLIB 适用于无接口的类代理。
  • 实际开发中,优先选择JDK动态代理;若目标类无接口,则使用CGLIB。