eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Overview

When we use the dynamic proxy, the JDK will dynamically generate a $Proxy class. Usually, the fully qualified class name of this $Proxy class is somewhat similar to com.sun.proxy.$Proxy0. As the Java Documentation says, the “$Proxy” is a reserved name prefix for proxy classes.

In this tutorial, we’re going to explore this $Proxy class.

2. The $Proxy Class

Before getting started, let’s distinguish between the java.lang.reflect.Proxy class and $Proxy class. The java.lang.reflect.Proxy is a JDK built-in class. And, in contrast, the $Proxy class is dynamically generated at runtime. From a class hierarchy perspective, the $Proxy class inherits the java.lang.reflect.Proxy class.

2.1. A Dynamic Proxy Example

To have a basis for discussion, let’s define two interfaces: BasicOperation and AdvancedOperation. The BasicOperation interface contains the add and subtract methods:

public interface BasicOperation {
    int add(int a, int b);

    int subtract(int a, int b);
}

And, the AdvancedOperation interface has the multiply and divide methods:

public interface AdvancedOperation {
    int multiply(int a, int b);

    int divide(int a, int b);
}

To get a newly generated proxy class – the $Proxy class – we can invoke the Proxy::getProxyClass method:

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?>[] interfaces = {BasicOperation.class, AdvancedOperation.class};
Class<?> proxyClass = Proxy.getProxyClass(classLoader, interfaces);

However, the above proxyClass only exists in a running JVM and we cannot directly view its class members.

2.2. Dump $Proxy Class

For close scrutiny of this $Proxy class, we’d better dump it to disk. When using Java 8, we can specify the “sun.misc.ProxyGenerator.saveGeneratedFiles” option on the command line:

-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

Or we can set this option by invoking the System::setProperty method:

System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

In Java 9 and later versions, we should use the “jdk.proxy.ProxyGenerator.saveGeneratedFiles” option instead. Why is there such a difference? Because of the Java Module System, the package of the ProxyGenerator class has changed. In Java 8, the ProxyGenerator is in the “sun.misc” package; however, since Java 9, the ProxyGenerator has moved into the “java.lang.reflect” package.

In case we still don’t know which option is suitable, we can look up the saveGeneratedFiles field of the ProxyGenerator class to determine the correct one.

Be careful here: The ProxyGenerator class reads this property only once. And, this implies that the System::setProperty method will have no effect after the JVM has explicitly or implicitly produced any $Proxy classes. To be specific, invoking the Proxy::getProxyClass or Proxy::newProxyInstance method will explicitly generate the $Proxy class. On the other hand, when we read annotations, especially within the Unit Test framework, the JVM will implicitly or automatically generate the $Proxy class to represent annotation instances.

The exact location of the dumped class file is directly related to its fully qualified class name. For example, if the newly generated class name is “com.sun.proxy.$Proxy0“, then the dumped class file will be “com/sun/proxy/$Proxy0.class” in the current directory:

p1

2.3. The Members of $Proxy Class

Now, it’s time to inspect the class members of this generated $Proxy class.

Let’s first inspect the class hierarchy. The $Proxy0 class has java.lang.reflect.Proxy as its superclass, which implicitly explains why dynamic proxy only supports interfaces. Also, the $Proxy0 class implements our previously defined BasicOperation and AdvancedOperation interfaces:

public final class $Proxy0 extends Proxy implements BasicOperation, AdvancedOperation

For readability, we have changed the field names of the $Proxy0 class into more meaningful ones. The hashCodeMethod, equalsMethod, and toStringMethod fields trace back to the Object class; the addMethod and subtractMethod fields are related to the BasicOperation interface; the multiplyMethod and divideMethod fields map to the AdvanceOperation interface:

private static Method hashCodeMethod;
private static Method equalsMethod;
private static Method toStringMethod;
private static Method addMethod;
private static Method subtractMethod;
private static Method multiplyMethod;
private static Method divideMethod;

Finally, the methods defined in the $Proxy0 class follow the same logic: All their implementations delegate to the InvocationHandler::invoke method. And, the $Proxy0 class will get an InvocationHandler instance from its constructor:

public $Proxy0(InvocationHandler handler) {
    super(handler);
}

public final int hashCode() {
    try {
        return (Integer) super.h.invoke(this, hashCodeMethod, (Object[]) null);
    }
    catch (RuntimeException | Error ex1) {
        throw ex1;
    }
    catch (Throwable ex2) {
        throw new UndeclaredThrowableException(ex2);
    }
}

3. How Proxy Works

After we have inspected the $Proxy class itself, it’s time to go one step further: how to generate the $Proxy class and how to load the $Proxy class? The key logic lies in the java.lang.reflect.Proxy and ProxyGenerator classes.

As new Java versions are released, the implementation details of the Proxy and ProxyGenerator classes continue to evolve. Roughly speaking, the ProxyGenerator is responsible for generating the $Proxy class’ byte array, and the Proxy class is responsible for loading this byte array into the JVM.

Now, let’s use Java 8, Java 11, and Java 17 for our discussion because they are LTS (Long-Term Support) editions.

3.1. Java 8

In Java 8, we can describe the $Proxy class generation process in five steps:

p8
The Proxy::getProxyClass or Proxy::newProxyInstance method is our starting point — either one will invoke the Proxy::getProxyClass0 method. And, the Proxy::getProxyClass0 method is a private method and will further invoke the ProxyClassFactory::apply method.

The ProxyClassFactory is a static nested class defined in the Proxy class. And, its apply method figures out the package name, class name, and access flags of the upcoming class. Then, the apply method will invoke the ProxyGenerator::generateProxyClass method.

In Java 8, the ProxyGenerator class is a public class defined in the “sun.misc” package. It has migrated to the “java.lang.reflect” package since Java 9. And, the generateProxyClass method will create a ProxyGenerator instance, invoke its generateClassFile method whose responsibility is bytecode generation, optionally dump the class file, and return the resulting byte array.

After the bytecode generation succeeds, the Proxy::defineClass0 method is responsible for loading that byte array into the running JVM. Finally, we get a dynamically generated $Proxy class.

3.2. Java 11

Compared with the Java 8 edition, Java 11 has introduced three major changes:

  • The Proxy class adds a new getProxyConstructor method and a static nested ProxyBuilder class
  • For Java Module System, the ProxyGenerator has migrated to the “java.lang.reflect” package and become a package-private class
  • To load the generated byte array into the JVM, the Unsafe::defineClass comes into play

 

p11

3.3. Java 17

Compared with the Java 11 edition, Java 17 has two major changes:

  • From the implementation perspective, the ProxyGenerator class utilizes the JDK built-in ASM to do the bytecode generation
  • The JavaLangAccess::defineClass method is responsible for loading the generated bytecode into the JVM

 

p17

4. Annotation Using Proxy

In Java, an annotation type is a special kind of interface type. But, we may be wondering how to create an annotation instance. In fact, we don’t need to. When we use the Java Reflection API to read an annotation, the JVM will dynamically generate a $Proxy class as the annotation type’s implementation:

FunctionalInterface instance = Consumer.class.getDeclaredAnnotation(FunctionalInterface.class);
Class<?> clazz = instance.getClass();

boolean isProxyClass = Proxy.isProxyClass(clazz);
assertTrue(isProxyClass);

In the above code snippet, we use the Consumer class to get its FunctionalInterface instance, then get the instance’s class, and finally, use the Proxy::isProxyClass method to check whether the class is a $Proxy class.

5. Conclusion

In this tutorial, we first introduced a dynamic proxy example, then dumped the generated $Proxy class and inspected its members. To go one step further, we explained how the Proxy and ProxyGenerator classes work together to generate and load the $Proxy class in different Java versions. Finally, we mentioned that an annotation type is also implemented by using the $Proxy class.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)