Java动态代理

本文深入介绍了Java中的代理模式,包括静态代理与动态代理的概念及其实现方式。通过具体示例展示了如何利用Java Assist和JDK自带API实现动态代理,帮助读者理解代理模式在增强功能与简化访问过程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java动态代理

代理模式

我们生活中充满了代理的例子:

  • 例如房产中介,卖房者将房子委托给房产中介,买房者买房去房产中介而不是直接去寻找卖房者,这里面房产中介就是代理。
  • 再比如,现在都流行点外卖,在点外卖的场景中,商户将自己委托给外卖平台,而客户在这个外卖平台直接下单,省去了实地寻找商户的过程,这里外卖平台就是代理

通过上述生活中的实际例子,相信我们都知道代理是什么了,代理模式是一种结构型设计模式,让你能够提供对象的替代品或其占位符,代理控制着对于原对象的访问,并允许在将请求提交给对象前后进行一些处理。

下面是代理模式的类图:

image-20210408161931144

在实际的使用中,代理模式又衍生出了静态代理和动态代理,静态代理就是我们将要代理的服务硬编码到代码中,运行期间无法进行修改;

而动态代理是在运行期间,或者说加载对象的时候,选择我们要代理的服务对其进行代理的方法,动态代理是在运行时生成它对应的代理类。

通过代理我们可以增强原有类的功能。

静态代理

首先我们看看静态代理的例子

静态代理示例代码:

// 代理要实现的接口
public interface UserService {
    /**
     * 测试静态代理的方法
     */
    void addUser();
}
// 代理类接口的实现
public class UserServiceImpl implements UserService{
    @Override
    public void addUser() {
        System.out.println("成功添加用户!");
    }
}
// 进行代理类的代理
public class UserServiceProxy implements UserService{
    UserService impl;
    public UserServiceProxy() {
        this.impl = new UserServiceImpl();
    }
    @Override
    public void addUser() {
        System.out.println("进行了代理!");
        impl.addUser();
    }
}
// 测试静态代理
public class Client {
    public static void main(String[] args) {
        UserService proxy = new UserServiceProxy();
        proxy.addUser();
    }
}

上述静态代理对应的类图如下:

image-20210408163939043

动态代理

首先我们回顾下java类是如何加载的:

image-20210408164513939

我们可以通过修改.class文件的指令来生成需要的代理对象,这就是动态代理,例如cglib就是这样实现动态代理的。

在jdk的动态代理中,是通过适配器来代理源目标的:

image-20210408170342159

我们来看看使用javassist实现自己的动态代理:

首先看看javassist的maven仓库

		<dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.27.0-GA</version>
        </dependency>

接下来就是具体的实现

package com.wyg.learn_javassist;

import javassist.*;
import javassist.util.proxy.ProxyFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * @description:
 * @author: WYG
 * @time: 2021/4/8 19:34
 */

public class TestJavassist {

    public static void main(String[] args) throws Exception {
        // 我们自己使用javassist实现的动态代理方法
        UserService userService = perfectCreateProxy(UserService.class, new InvocationHandler() {
            @Override
            public Object invoke(String methodName, Object[] args) {
                if (methodName.equals("sayHello")) {
                    System.out.println("hello:\t" + args[0]);
                } else if (methodName.equals("addUser")) {
                    System.out.println("添加user" + args[0] + "成功!");
                }
                return null;
            }
        });
        userService.sayHello("wyg");
        userService.addUser("wyg");
		// jdk的动态代理方法
        JdkProxy();
    }
    
	// 简易版动态代理,主要了解了javassist的基本使用
    public static UserService createProxy() throws Exception {
        ClassPool classPool = new ClassPool();
        // 设置classloader
        classPool.appendSystemPath();
        // javassist --> ASM(编辑jvm指令码)
        // 1、创建一个类
        CtClass userServiceImpl = classPool.makeClass("UserServiceImpl");
        userServiceImpl.addInterface(classPool.get(UserService.class.getName()));
        // 2、创建一个方法
        CtMethod sayHello = CtNewMethod.make(CtClass.voidType,                              // 方法的返回类型
                "sayHello",                                     // 方法名称
                new CtClass[]{classPool.get(String.class.getName())},   // 方法的参数类型
                new CtClass[0],                                         // 方法抛出的异常
                "System.out.println(\"hello:\" + $1);",           // 方法的具体实现
                userServiceImpl);//  将该方法加入到对应的class中
        userServiceImpl.addMethod(sayHello);
        // 3、实例化一个对象
        Class aClass = classPool.toClass(userServiceImpl);
        return (UserService) aClass.getDeclaredConstructor().newInstance();
    }

    // 1.支持所有接口代理
    // 2.按常规方式传递实现
    static int count = 0;
    /**
     *
     * @param calInterface  接口类
     * @param invocationHandler 调用方法的辅助类
     * @param <T>   返回类型
     * @return
     * @throws Exception
     */
    public static <T> T perfectCreateProxy(Class<T> calInterface, InvocationHandler invocationHandler) throws Exception {
        // 1.创建一个类
        ClassPool classPool = new ClassPool();
        classPool.appendSystemPath();
        CtClass impl = classPool.makeClass("$proxy" + count++);
        impl.addInterface(classPool.get(calInterface.getName()));
        // 2.添加属性handler对象
        CtField field = CtField.
                make("public com.wyg.learn_javassist.TestJavassist.InvocationHandler handler = null;", impl);
        impl.addField(field);
        // 传递方法名称和参数,s为方法名,args为参数
        String src = "return ($r)this.handler.invoke(\"%s\", $args);";
        String voidSrc = "this.handler.invoke(\"%s\", $args);";
        // 3.创建这个接口下的所有方法实现
        for (Method method : calInterface.getMethods()) {
            CtClass returnType = classPool.get(method.getReturnType().getName());
            String name = method.getName();
            CtClass[] parameters = toCtClass(classPool, method.getParameterTypes());
            CtClass[] errors = toCtClass(classPool, method.getExceptionTypes());
            String srcImpl = method.getReturnType().equals(Void.class) ? voidSrc : src;
            CtMethod newMethod = CtNewMethod.make(returnType, name, parameters, errors, String.format(srcImpl, name), impl);
            impl.addMethod(newMethod);
        }
        // 生成字节码
        byte[] bytes = impl.toBytecode();
        // 类加载到当前的ClassLoader中
        Class aClass = classPool.toClass(impl);
        Object o = aClass.getDeclaredConstructor().newInstance();
        aClass.getField("handler").set(o, invocationHandler);
        return (T) o;
    }
	// 将普通的类转换成CtClass
    private static CtClass[] toCtClass(ClassPool classPool, Class[] classes) {
        return Arrays.stream(classes).map(c -> {
            try {
                return classPool.get(c.getName());
            } catch (NotFoundException e) {
                throw new RuntimeException();
            }
        }).collect(Collectors.toList()).toArray(new CtClass[0]);
    }
	// 我们自己的InvocationHandler对象
    public interface InvocationHandler {
        Object invoke(String methodName, Object args[]);
    }
    public class InvocationHandlerImpl implements InvocationHandler {
        @Override
        public Object invoke(String methodName, Object[] args) {
            System.out.println("test");
            return null;
        }
    }
    // 我们要代理的接口
    public interface UserService {
        void sayHello(String name);
        void addUser(String name);
    }

	// 使用jdk的动态代理来代理我们的实现类
    public static void JdkProxy() {
        ClassLoader classLoader = ProxyFactory.class.getClassLoader();
        Class<?>[] interfaces = new Class[]{ UserService.class };
        java.lang.reflect.InvocationHandler handler = ((proxy, method, args) -> {
            System.out.println("hello wyg");
            return null;
        });
        UserService service = (UserService) Proxy.newProxyInstance(classLoader, interfaces, handler);
        service.sayHello("wyg");
    }
}

动态代理的使用

动态代理有增强型动态代理和链接型动态代理:

  • 增强型代理:aop、日志打印、声明式事务、监控等,这些都在原有类的基础上增加了一定的功能
  • 链接型代理:远程调用(如rpc)、Mybatis的mapper映射,链接型动态代理帮助我们简化了访问某个类的步骤

参考

鲁班大叔

文章首发于个人博客,欢迎访问啊!风在哪个人博客

### Java 动态代理概述 Java 动态代理是一种在运行时动态创建代理类的技术,主要通过 `java.lang.reflect.Proxy` 类和 `java.lang.reflect.InvocationHandler` 接口实现。这种技术允许开发者在不修改原始类的情况下增强其行为。 #### 动态代理的实现机制 动态代理的核心在于利用反射机制,在运行时生成代理类并拦截方法调用。具体来说: - **Proxy 类**:用于创建动态代理实例的对象工厂。 - **InvocationHandler 接口**:定义了一个处理程序接口,所有的方法调用都会被转发到这里进行统一处理[^1]。 以下是基于 JDK 的动态代理实现方式的一个简单示例: ```java import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface GreetingService { void sayHello(String name); } class GreetingServiceImpl implements GreetingService { @Override public void sayHello(String name) { System.out.println("Hello, " + name); } } 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 call"); Object result = method.invoke(target, args); // 调用实际的目标方法 System.out.println("After method call"); return result; } } public class DynamicProxyExample { public static void main(String[] args) { GreetingService greetingService = new GreetingServiceImpl(); GreetingService proxyInstance = (GreetingService) Proxy.newProxyInstance( greetingService.getClass().getClassLoader(), greetingService.getClass().getInterfaces(), new MyInvocationHandler(greetingService)); proxyInstance.sayHello("World"); // 输出 Before/After 日志以及 Hello World } } ``` 上述代码展示了如何使用 JDK 动态代理为一个简单的服务接口增加日志记录功能。 #### 使用场景分析 动态代理广泛应用于各种框架中,尤其是在需要解耦合、扩展性和灵活性的地方。常见的使用场景包括但不限于以下几种: - **Spring 框架中的依赖注入与 AOP** Spring 利用了动态代理实现了依赖注入的功能,并支持面向切面编程(AOP)。例如,事务管理可以通过动态代理自动开启和关闭数据库连接[^2]。 - **Hibernate ORM 映射** Hibernate 借助于动态代理将数据库表结构映射成 Java 对象模型。当查询数据时,返回的是由代理对象封装的实际实体。 #### CGLIB 动态代理简介 除了 JDK 提供的标准动态代理外,还有另一种流行的解决方案叫做 CGLIB(Code Generation Library)。相比于 JDK 动态代理仅能操作实现了接口的类,CGLIB 可以针对任意普通类生成子类作为代理[^3]。 需要注意的是,由于 CGLIB 是通过继承的方式工作,因此无法代理标记为 `final` 或者含有 `private final/static` 方法的类。 下面是一个基本的 CGLIB 示例片段: ```java import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; class SimpleClass { public void performAction() { System.out.println("Performing action..."); } } public class CglibDynamicProxyDemo { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(SimpleClass.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Intercepted before calling real method."); Object result = proxy.invokeSuper(obj, args); System.out.println("Intercepted after calling real method."); return result; } }); SimpleClass proxyInstance = (SimpleClass) enhancer.create(); proxyInstance.performAction(); // 执行带有前后置逻辑的操作 } } ``` 此段代码演示了如何借助 CGLIB 创建非接口类型的代理实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值