SoFunction
Updated on 2025-04-22

Implementation solutions of 5 dynamic proxy in SpringBoot

Dynamic proxy allows us to add additional behavior to objects without modifying the source code. In SpringBoot applications, dynamic proxy is widely used to implement cross-cutting concerns such as transaction management, caching, security control, and logging.

1. JDK dynamic proxy: Java native proxy solution

Implementation principle

JDK dynamic proxy is a proxy mechanism provided by the Java standard library, based onClass andInvocationHandlerInterface implementation. It dynamically creates proxy instances of interfaces at runtime by reflection.

Core code examples

public class JdkDynamicProxyDemo {
    interface UserService {
        void save(User user);
        User find(Long id);
    }
    
    // Implementation class    static class UserServiceImpl implements UserService {
        @Override
        public void save(User user) {
            ("Save User: " + ());
        }
        
        @Override
        public User find(Long id) {
            ("Find User ID: " + id);
            return new User(id, "user" + id);
        }
    }
    
    // Call the processor    static class LoggingInvocationHandler implements InvocationHandler {
        private final Object target;
        
        public LoggingInvocationHandler(Object target) {
             = target;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            ("Before: " + ());
            long startTime = ();
            
            Object result = (target, args);
            
            long endTime = ();
            ("After: " + () + ", time consuming: " + (endTime - startTime) + "ms");
            
            return result;
        }
    }
    
    public static void main(String[] args) {
        // Create target object        UserService userService = new UserServiceImpl();
        
        // Create proxy object        UserService proxy = (UserService) (
            ().getClassLoader(),
            ().getInterfaces(),
            new LoggingInvocationHandler(userService)
        );
        
        // Call proxy method        (new User(1L, "Zhang San"));
        User user = (2L);
    }
}

advantage

  • JDK standard library comes with: No need to introduce additional dependencies, reducing project size
  • Generate code is simple: Agent logic is centralized in InvocationHandler, easy to understand and maintain
  • Relatively stable performance: In the version after JDK 8, the performance has been significantly improved

limitation

  • Only proxy interfaces: The proxy class must implement the interface, and the proxy class cannot be proxyed
  • Reflection call overhead: Each method call needs to pass the reflection mechanism, which has certain performance losses
  • Unable to intercept final method: Cannot proxy the method of final modification

Applications in Spring

In Spring, when the bean implements an interface, the JDK dynamic proxy is used by default. This can be modified by configuration:

@EnableAspectJAutoProxy(proxyTargetClass = false) // The default value is false, indicating that the JDK dynamic proxy is preferred

2. CGLIB proxy: a powerful proxy based on bytecode

Implementation principle

CGLIB (Code Generation Library) is a powerful high-performance bytecode generation library. It implements proxy by inheriting the proxy class to generate subclasses. Spring integrates CGLIB directly into the framework starting from version 3.2.

Core code examples

public class CglibProxyDemo {
    // Classes that do not need to implement interfaces    static class UserService {
        public void save(User user) {
            ("Save User: " + ());
        }
        
        public User find(Long id) {
            ("Find User ID: " + id);
            return new User(id, "user" + id);
        }
    }
    
    // CGLIB method interceptor    static class LoggingMethodInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            ("Before: " + ());
            long startTime = ();
            
            // Call the original method            Object result = (obj, args);
            
            long endTime = ();
            ("After: " + () + ", time consuming: " + (endTime - startTime) + "ms");
            
            return result;
        }
    }
    
    public static void main(String[] args) {
        // Create a CGLIB proxy        Enhancer enhancer = new Enhancer();
        ();
        (new LoggingMethodInterceptor());
        
        // Create proxy object        UserService proxy = (UserService) ();
        
        // Call proxy method        (new User(1L, "Zhang San"));
        User user = (2L);
    }
}

advantage

  • Can proxy classes: The target class does not require the implementation of interfaces, and the application scenarios are more extensive
  • High performance: Method calling performance is better than JDK proxy by generating bytecode rather than reflective calls
  • Rich features: Supports multiple callback types, such as LazyLoader, Dispatcher, etc.
  • Integrate into Spring: Spring framework has built-in CGLIB, no additional dependencies are required

limitation

  • Cannot proxy final classes and methods: Due to the use of inheritance mechanism, it is impossible to proxy the class or method that final modified
  • Constructor call: The constructor of the target class will be called when generating the proxy object, which may lead to unexpected behavior
  • Increased complexity: The generated bytecode is complex and difficult to debug
  • Sensitive to Java versions: There may be compatibility issues between different Java versions

Applications in Spring

In Spring, when the bean does not implement the interface or is configuredproxyTargetClass=trueWhen using CGLIB proxy:

@EnableAspectJAutoProxy(proxyTargetClass = true) // Force use of CGLIB proxy

3. ByteBuddy: Modern bytecode operation library

Implementation principle

ByteBuddy is a relatively new library of bytecode generation and operation, with a more modern design and a more API-friendly one. It can create and modify Java classes without understanding the underlying JVM instruction set.

Core code examples

public class ByteBuddyProxyDemo {
    interface UserService {
        void save(User user);
        User find(Long id);
    }
    
    static class UserServiceImpl implements UserService {
        @Override
        public void save(User user) {
            ("Save User: " + ());
        }
        
        @Override
        public User find(Long id) {
            ("Find User ID: " + id);
            return new User(id, "user" + id);
        }
    }
    
    static class LoggingInterceptor {
        @RuntimeType
        public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable,
                                       @AllArguments Object[] args) throws Exception {
            ("Before: " + ());
            long startTime = ();
            
            Object result = ();
            
            long endTime = ();
            ("After: " + () + ", time consuming: " + (endTime - startTime) + "ms");
            
            return result;
        }
    }
    
    public static void main(String[] args) throws Exception {
        UserService userService = new UserServiceImpl();
        
        // Create ByteBuddy Agent        UserService proxy = new ByteBuddy()
            .subclass()
            .method(())
            .intercept((new LoggingInterceptor()))
            .make()
            .load(())
            .getLoaded()
            .getDeclaredConstructor()
            .newInstance();
        
        // Call proxy method        (new User(1L, "Zhang San"));
        User user = (2L);
    }
}

advantage

  • Smooth API: Provides a chain programming style, and the code is more readable
  • Excellent performance: Performance better than CGLIB and JDK proxy in multiple benchmarks
  • Type safety: API design focuses more on type safety and reduces runtime errors
  • Support new Java features: Better support for new features such as Java 9+ module system
  • Rich features: Supports method redirection, field access, constructor interception and other scenarios

limitation

  • Extra dependencies: Additional dependency libraries need to be introduced
  • Learning curve: Although the API is smooth, it has many concepts and has certain learning costs
  • Not very integrated in Spring: Custom configuration is required to replace the default proxy mechanism in Spring

Applications in Spring

Although ByteBuddy is not Spring's default proxy implementation, it can be customizedProxyFactoryTo integrate:

@Configuration
public class ByteBuddyProxyConfig {
    @Bean
    public AopConfigurer byteBuddyAopConfigurer() {
        return new AopConfigurer() {
            @Override
            public void configureProxyCreator(Object bean, String beanName) {
                // Configure ByteBuddy as a proxy creator                // Slightly implement details            }
        };
    }
}

4. Javassist: an easier-to-use bytecode editing library

Implementation principle

Javassist is an open source Java bytecode operation library that provides two levels of API: source code level and bytecode level. It allows developers to edit bytecode directly with simple Java syntax without having to dig deep into JVM specifications.

Core code examples

public class JavassistProxyDemo {
    interface UserService {
        void save(User user);
        User find(Long id);
    }
    
    static class UserServiceImpl implements UserService {
        @Override
        public void save(User user) {
            ("Save User: " + ());
        }
        
        @Override
        public User find(Long id) {
            ("Find User ID: " + id);
            return new User(id, "user" + id);
        }
    }
    
    public static void main(String[] args) throws Exception {
        ProxyFactory factory = new ProxyFactory();
        
        // Set up the proxy interface        (new Class[] {  });
        
        // Create method filter        MethodHandler handler = new MethodHandler() {
            private UserService target = new UserServiceImpl();
            
            @Override
            public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable {
                ("Before: " + ());
                long startTime = ();
                
                Object result = (target, args);
                
                long endTime = ();
                ("After: " + () + ", time consuming: " + (endTime - startTime) + "ms");
                
                return result;
            }
        };
        
        // Create proxy object        UserService proxy = (UserService) (new Class<?>[0], new Object[0], handler);
        
        // Call proxy method        (new User(1L, "Zhang San"));
        User user = (2L);
    }
}

advantage

  • Source code-level API: You can operate bytecode instruction set without understanding the bytecode in the form of Java code
  • Lightweight library: Smaller size than other bytecode libraries
  • Comprehensive functions: Supports creating classes, modifying classes, dynamically compiling Java source code, etc.
  • Good performance: The generated proxy code performance is close to CGLIB
  • Long-term maintenance: The library has a long history, stable and reliable

limitation

  • The API is not intuitive enough: Some API designs are relatively old and are not as smooth as ByteBuddy
  • Insufficient documentation: There are fewer documents and examples than other libraries
  • Memory consumption: It may consume more memory when operating a large number of classes

Applications in Spring

Javassist is not Spring's default proxy implementation, but some Spring-based frameworks use it to implement dynamic proxying, such as Hibernate:

// Custom Javassist agent factory examplepublic class JavassistProxyFactory implements ProxyFactory {
    @Override
    public Object createProxy(Object target, Interceptor interceptor) {
        // Javassist proxy implementation        // ...
    }
}

5. AspectJ: Complete AOP Solution

Implementation principle

AspectJ is a complete AOP framework. Unlike the previous dynamic proxy scheme, it provides two ways:

  • Compile time weaving: directly modify the bytecode when compiling the source code
  • Weaving in during loading: Modify bytecode when the class is loaded into the JVM

AspectJ has a special facet language and is more powerful than Spring AOP.

Core code examples

AspectJ aspect definition:

@Aspect
public class LoggingAspect {
    @Before("execution(* .*(..))")
    public void logBefore(JoinPoint joinPoint) {
        ("Before: " + ().getName());
    }
    
    @Around("execution(* .*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        ("Before: " + ().getName());
        long startTime = ();
        
        Object result = ();
        
        long endTime = ();
        ("After: " + ().getName() + 
                          ", time consuming: " + (endTime - startTime) + "ms");
        
        return result;
    }
}

Spring configuration AspectJ:

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

Compile-time weaving configuration (maven):

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

advantage

  • The most comprehensive functions: Supports almost all AOP scenarios, including constructors, field access, exceptions, etc.
  • Best performance: No runtime overhead when weaving, performance is close to native code
  • Full language support: Have special facet language and grammar, strong expression skills
  • More flexible entry points: You can define facets for interfaces, implementation classes, constructors, fields, etc.

limitation

  • High complexity: The learning curve is steep, you need to master the AspectJ syntax
  • The construction process changes: A special compiler or class loader is required
  • Debugging difficulty increases: Modified bytecode may be difficult to debug

Applications in Spring

Spring can use AspectJ in the following ways:

proxy-target-class mode: A proxy generated using Spring AOP but CGLIB

@EnableAspectJAutoProxy(proxyTargetClass = true)

LTW (Load-Time Weaving) mode: Weaving in when class loads

@EnableLoadTimeWeaving

Compile time weaving: Requires configuration of AspectJ compiler

Comparison of usage scenarios and selection suggestions

Agent implementation The most suitable scenario Not applicable
JDK dynamic proxy Simple interface-based proxy, lightweight application No class implementing interface, performance-sensitive scenarios
CGLIB Classes that do not implement interfaces need to take into account both performance and convenience Final class/method, high security environment
ByteBuddy Modern projects, focusing on performance optimization, complex proxy logic Simple project that pursues minimal dependencies
Javassist Complex scenarios that require dynamic generation/modification of classes API design sensitive projects for beginners
AspectJ Enterprise-level applications, performance-critical scenarios, complex AOP requirements Simple project, fast prototype, learning cost-sensitive

This is the article about the implementation solutions of 5 dynamic proxy solutions in SpringBoot. For more relevant SpringBoot dynamic proxy content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!