java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot动态代理

SpringBoot中5种动态代理的实现方案

作者:风象南

在SpringBoot应用中,动态代理被广泛用于实现事务管理、缓存、安全控制、日志记录等横切关注点,下面小编为大家介绍一下SpringBoot中5种动态代理的实现方案吧

动态代理允许我们在不修改源代码的情况下,为对象增加额外的行为。在SpringBoot应用中,动态代理被广泛用于实现事务管理、缓存、安全控制、日志记录等横切关注点。

1. JDK动态代理:Java原生的代理方案

实现原理

JDK动态代理是Java标准库提供的代理机制,基于java.lang.reflect.Proxy类和InvocationHandler接口实现。它通过反射在运行时动态创建接口的代理实例。

核心代码示例

public class JdkDynamicProxyDemo {
    interface UserService {
        void save(User user);
        User find(Long id);
    }
    
    // 实现类
    static class UserServiceImpl implements UserService {
        @Override
        public void save(User user) {
            System.out.println("保存用户: " + user.getName());
        }
        
        @Override
        public User find(Long id) {
            System.out.println("查找用户ID: " + id);
            return new User(id, "用户" + id);
        }
    }
    
    // 调用处理器
    static class LoggingInvocationHandler implements InvocationHandler {
        private final Object target;
        
        public LoggingInvocationHandler(Object target) {
            this.target = target;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Before: " + method.getName());
            long startTime = System.currentTimeMillis();
            
            Object result = method.invoke(target, args);
            
            long endTime = System.currentTimeMillis();
            System.out.println("After: " + method.getName() + ", 耗时: " + (endTime - startTime) + "ms");
            
            return result;
        }
    }
    
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();
        
        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            userService.getClass().getClassLoader(),
            userService.getClass().getInterfaces(),
            new LoggingInvocationHandler(userService)
        );
        
        // 调用代理方法
        proxy.save(new User(1L, "张三"));
        User user = proxy.find(2L);
    }
}

优点

局限性

Spring中的应用

在Spring中,当Bean实现了接口时,默认使用JDK动态代理。这可以通过配置修改:

@EnableAspectJAutoProxy(proxyTargetClass = false) // 默认值为false,表示优先使用JDK动态代理

2. CGLIB代理:基于字节码的强大代理

实现原理

CGLIB(Code Generation Library)是一个强大的高性能字节码生成库,它通过继承被代理类生成子类的方式实现代理。Spring从3.2版本开始将CGLIB直接集成到框架中。

核心代码示例

public class CglibProxyDemo {
    // 不需要实现接口的类
    static class UserService {
        public void save(User user) {
            System.out.println("保存用户: " + user.getName());
        }
        
        public User find(Long id) {
            System.out.println("查找用户ID: " + id);
            return new User(id, "用户" + id);
        }
    }
    
    // CGLIB方法拦截器
    static class LoggingMethodInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("Before: " + method.getName());
            long startTime = System.currentTimeMillis();
            
            // 调用原始方法
            Object result = proxy.invokeSuper(obj, args);
            
            long endTime = System.currentTimeMillis();
            System.out.println("After: " + method.getName() + ", 耗时: " + (endTime - startTime) + "ms");
            
            return result;
        }
    }
    
    public static void main(String[] args) {
        // 创建CGLIB代理
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new LoggingMethodInterceptor());
        
        // 创建代理对象
        UserService proxy = (UserService) enhancer.create();
        
        // 调用代理方法
        proxy.save(new User(1L, "张三"));
        User user = proxy.find(2L);
    }
}

优点

局限性

Spring中的应用

在Spring中,当Bean没有实现接口或配置了proxyTargetClass=true时,使用CGLIB代理:

@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB代理

3. ByteBuddy:现代化的字节码操作库

实现原理

ByteBuddy是一个相对较新的字节码生成和操作库,设计更加现代化,API更加友好。它可以创建和修改Java类,而无需理解底层的JVM指令集。

核心代码示例

public class ByteBuddyProxyDemo {
    interface UserService {
        void save(User user);
        User find(Long id);
    }
    
    static class UserServiceImpl implements UserService {
        @Override
        public void save(User user) {
            System.out.println("保存用户: " + user.getName());
        }
        
        @Override
        public User find(Long id) {
            System.out.println("查找用户ID: " + id);
            return new User(id, "用户" + id);
        }
    }
    
    static class LoggingInterceptor {
        @RuntimeType
        public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable,
                                       @AllArguments Object[] args) throws Exception {
            System.out.println("Before: " + method.getName());
            long startTime = System.currentTimeMillis();
            
            Object result = callable.call();
            
            long endTime = System.currentTimeMillis();
            System.out.println("After: " + method.getName() + ", 耗时: " + (endTime - startTime) + "ms");
            
            return result;
        }
    }
    
    public static void main(String[] args) throws Exception {
        UserService userService = new UserServiceImpl();
        
        // 创建ByteBuddy代理
        UserService proxy = new ByteBuddy()
            .subclass(UserServiceImpl.class)
            .method(ElementMatchers.any())
            .intercept(MethodDelegation.to(new LoggingInterceptor()))
            .make()
            .load(UserServiceImpl.class.getClassLoader())
            .getLoaded()
            .getDeclaredConstructor()
            .newInstance();
        
        // 调用代理方法
        proxy.save(new User(1L, "张三"));
        User user = proxy.find(2L);
    }
}

优点

局限性

Spring中的应用

ByteBuddy虽然不是Spring默认的代理实现,但可以通过自定义ProxyFactory来集成:

@Configuration
public class ByteBuddyProxyConfig {
    @Bean
    public AopConfigurer byteBuddyAopConfigurer() {
        return new AopConfigurer() {
            @Override
            public void configureProxyCreator(Object bean, String beanName) {
                // 配置ByteBuddy作为代理创建器
                // 实现细节略
            }
        };
    }
}

4. Javassist:更易用的字节码编辑库

实现原理

Javassist是一个开源的Java字节码操作库,提供了两个层次的API:源代码级别和字节码级别。它允许开发者用简单的Java语法直接编辑字节码,无需深入了解JVM规范。

核心代码示例

public class JavassistProxyDemo {
    interface UserService {
        void save(User user);
        User find(Long id);
    }
    
    static class UserServiceImpl implements UserService {
        @Override
        public void save(User user) {
            System.out.println("保存用户: " + user.getName());
        }
        
        @Override
        public User find(Long id) {
            System.out.println("查找用户ID: " + id);
            return new User(id, "用户" + id);
        }
    }
    
    public static void main(String[] args) throws Exception {
        ProxyFactory factory = new ProxyFactory();
        
        // 设置代理接口
        factory.setInterfaces(new Class[] { UserService.class });
        
        // 创建方法过滤器
        MethodHandler handler = new MethodHandler() {
            private UserService target = new UserServiceImpl();
            
            @Override
            public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable {
                System.out.println("Before: " + method.getName());
                long startTime = System.currentTimeMillis();
                
                Object result = method.invoke(target, args);
                
                long endTime = System.currentTimeMillis();
                System.out.println("After: " + method.getName() + ", 耗时: " + (endTime - startTime) + "ms");
                
                return result;
            }
        };
        
        // 创建代理对象
        UserService proxy = (UserService) factory.create(new Class<?>[0], new Object[0], handler);
        
        // 调用代理方法
        proxy.save(new User(1L, "张三"));
        User user = proxy.find(2L);
    }
}

优点

局限性

Spring中的应用

Javassist不是Spring默认的代理实现,但一些基于Spring的框架使用它实现动态代理,如Hibernate:

// 自定义Javassist代理工厂示例
public class JavassistProxyFactory implements ProxyFactory {
    @Override
    public Object createProxy(Object target, Interceptor interceptor) {
        // Javassist代理实现
        // ...
    }
}

5. AspectJ:完整的AOP解决方案

实现原理

AspectJ是一个完整的AOP框架,与前面的动态代理方案不同,它提供两种方式:

AspectJ拥有专门的切面语言,功能比Spring AOP更强大。

核心代码示例

AspectJ切面定义:

@Aspect
public class LoggingAspect {
    @Before("execution(* com.example.service.UserService.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before: " + joinPoint.getSignature().getName());
    }
    
    @Around("execution(* com.example.service.UserService.*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before: " + joinPoint.getSignature().getName());
        long startTime = System.currentTimeMillis();
        
        Object result = joinPoint.proceed();
        
        long endTime = System.currentTimeMillis();
        System.out.println("After: " + joinPoint.getSignature().getName() + 
                          ", 耗时: " + (endTime - startTime) + "ms");
        
        return result;
    }
}

Spring配置AspectJ:

@Configuration
@EnableAspectJAutoProxy
public class AspectJConfig {
    @Bean
    public LoggingAspect loggingAspect() {
        return new LoggingAspect();
    }
}

编译时织入配置(maven):

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.14.0</version>
    <configuration>
        <complianceLevel>11</complianceLevel>
        <source>11</source>
        <target>11</target>
        <aspectLibraries>
            <aspectLibrary>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
            </aspectLibrary>
        </aspectLibraries>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

优点

局限性

Spring中的应用

Spring可以通过以下方式使用AspectJ:

proxy-target-class模式:使用Spring AOP但CGLIB生成的代理

@EnableAspectJAutoProxy(proxyTargetClass = true)

LTW(Load-Time Weaving)模式:在类加载时织入

@EnableLoadTimeWeaving

编译时织入:需要配置AspectJ编译器

使用场景对比与选择建议

代理实现最适用场景不适用场景
JDK动态代理基于接口的简单代理,轻量级应用没有实现接口的类,性能敏感场景
CGLIB没有实现接口的类,需要兼顾性能和便捷性final类/方法,高安全性环境
ByteBuddy现代化项目,关注性能优化,复杂代理逻辑追求最小依赖的简单项目
Javassist需要动态生成/修改类的复杂场景API设计敏感项目,初学者
AspectJ企业级应用,性能关键型场景,复杂AOP需求简单项目,快速原型,学习成本敏感

到此这篇关于SpringBoot中5种动态代理的实现方案的文章就介绍到这了,更多相关SpringBoot动态代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文