Let's get started with a Microservice Architecture with Spring Cloud:
Java Reflection Beans Property API
Last updated: October 2, 2025
1. Overview
Java Reflection provides powerful capabilities to inspect and manipulate objects at runtime. Two commonly used tools for working with object properties dynamically are the Beans Property API (java.beans.PropertyDescriptor) and Apache Commons BeanUtils. While PropertyDescriptor enables low-level access to properties via getter and setter methods, Commons BeanUtils simplifies common operations such as reading, writing, populating from maps, and copying bean properties.
In this tutorial, we’ll discuss Java Reflections Beans Property API in detail.
2. A Real-World Example: Working with the Post Bean
Let’s begin by creating a practical and straightforward Java bean to represent a blog post entity. Real-world software often requires the dynamic inspection or manipulation of object properties, especially in frameworks and libraries that rely heavily on metadata and configurations, such as Object-Relational Mapping (ORM) tools, serialization libraries, and UI frameworks. The following Post class illustrates a typical Java bean with properties for title, author, and metadata:
public class Post {
private String title;
private String author;
private Metadata metadata;
// Getters and Setters
public static class Metadata {
private int wordCount;
// Getters and Setters
}
}
Next, we’ll examine in detail how we can dynamically inspect and manipulate these properties at runtime, demonstrating common use cases in practical development scenarios.
3. Using PropertyDescriptor and BeanInfo
Before diving deeper into specific methods, it’s beneficial to understand the two primary Java introspection classes we’ll be working with—BeanInfo and PropertyDescriptor. The BeanInfo class encapsulates information about a Java Bean, detailing its properties, methods, and events. On the other hand, PropertyDescriptor represents the metadata of a single property, providing dynamic access to getter and setter methods. These classes form the backbone of Java’s introspection capability, allowing us to perform sophisticated, runtime property manipulations effortlessly.
3.1. Inspecting Bean Properties with Introspector.getBeanInfo()
We can inspect the structure of beans through the Introspector class:
BeanInfo beanInfo = Introspector.getBeanInfo(Post.class);
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
System.out.println(pd.getName());
}
In this example, Java introspection identifies all the properties within the Post bean, including the implicit “class” property that comes from Java’s Object superclass:
author
class
metadata
title
Using this class, we can inspect bean properties at runtime. It’s a practical way to make applications more flexible and adaptable.
3.2. Locating Property Metadata
When we want to manipulate a single property at runtime, the first step is to create a PropertyDescriptor for the target property and bean class:
PropertyDescriptor titlePd = new PropertyDescriptor("title", Post.class);
We can think of this descriptor as a little “cheat sheet” that tells Java everything it needs to know about the property – right down to where its getter and setter methods live.
3.3. Extracting Accessor Methods
Once we have the descriptor, we can retrieve the property’s accessor methods:
Method write = titlePd.getWriteMethod();
Method read = titlePd.getReadMethod();
The setter (write) updates the property’s value, while the getter (read) retrieves it. This step bridges the gap between metadata and runtime execution.
Since these may return null if a getter or setter doesn’t exist, we should always check before invoking.
3.4. Invoking Methods Reflectively
Finally, we use Method.invoke() to call the accessors on a specific bean instance:
if (write != null) {
write.invoke(post, "Reflections in Java");
}
if (read != null) {
String value = (String) read.invoke(post);
System.out.println(value);
}
Since Method.invoke() always returns an Object, we must cast the result to the correct type when reading.
By following this three‑step recipe: descriptor creation, accessor extraction, invocation—we can read or write any Java‑Bean property dynamically while still executing the bean’s business logic (validation, events, logging) hidden inside its getters and setters.
4. Working Faster with Apache Commons BeanUtils
Apache Commons BeanUtils builds on the same reflection mechanisms but hides much of the boilerplate. Before diving into individual operations, we’ll declare the Maven dependency that pulls the library:
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.11.0</version>
</dependency>
4.1. Simple Property Access
The first improvement BeanUtils offers is BeanUtils.setProperty() and BeanUtils.getProperty(). These helper methods save us from creating a PropertyDescriptor by hand:
Post post = new Post();
BeanUtils.setProperty(post, "title", "Commons BeanUtils Rocks");
String title = BeanUtils.getProperty(post, "title");
Underneath, the library still discovers the proper getter and setter through reflection, but the call site stays short and expressive.
4.2. Populating a Bean from a Map
Web code often receives data as key-value pairs (for example, request parameters or JSON objects deserialized into a Map<String, Object>). BeanUtils can hydrate an entire bean in one line:
Map<String, Object> data = Map.of("title", "Map → Bean", "author", "Baeldung Team");
BeanUtils.populate(post, data);
Internally, populate() iterates over the map and delegates to setProperty() for every entry, giving us a concise bridge between dynamically‑typed input and strongly‑typed domain models.
4.3. Copying Bean Properties
Transferring state between two Java objects is another frequent task. BeanUtils.copyProperties() performs a shallow copy where matching names are copied and unmatched ones are ignored:
Post source = new Post();
source.setTitle("Source");
source.setAuthor("Alice");
Post target = new Post();
BeanUtils.copyProperties(target, source);
Because the method is reflective, we can execute the statement above without the two beans sharing a common interface. This pattern shines when mapping between DTOs and JPA entities.
4.4. Type Conversion and Nested Paths
Beyond plain strings, BeanUtils utilizes ConvertUtils to automatically coerce primitive types. We can register additional converters for domain‑specific classes:
ConvertUtils.register(new LocalDateConverter(), LocalDate.class);
Moreover, the library understands dotted property paths:
if (post.getMetadata() == null) {
post.setMetadata(new Post.Metadata());
}
BeanUtils.setProperty(post, "metadata.wordCount", 850);
Since nested properties may be null, we should initialize them before using BeanUtils to avoid a NullPointerException.
And indexed properties:
BeanUtils.setProperty(post, "tags[0]", "reflection");
4.5. Handling Checked Exceptions Gracefully
Every BeanUtils call declares IllegalAccessException, InvocationTargetException, and NoSuchMethodException. In production code, we will wrap those in an application‑specific unchecked exception to avoid cluttering service signatures:
try {
BeanUtils.copyProperties(target, source);
} catch (ReflectiveOperationException ex) {
throw new IllegalStateException(ex);
}
5. Security Considerations When Using Reflection
Reflection is a powerful tool, but it comes with security implications that we should not overlook:
- Bypassing Access Controls – Reflection can access private fields and methods if setAccessible(true) is used. This can break encapsulation and expose sensitive data
- Injection Risks – If method or property names from user input (e.g., HTTP parameters, JSON keys), an attacker could manipulate them to call unintended setters or getters
- Mass Assignment Vulnerabilities – Utilities like BeanUtils.populate() can set multiple properties at once. Without restrictions, this may lead to unauthorized changes in security-sensitive fields
6. Conclusion
Java Reflection, through tools like PropertyDescriptor and Apache Commons BeanUtils, empowers us to build flexible, metadata-driven applications with minimal boilerplate. These capabilities prove invaluable when working with frameworks, serializers, or data-mapping layers where dynamic behavior is a must. Although we need to handle reflection’s checked exceptions and performance trade-offs, the expressive power and decoupling these tools provide make them essential. Whether we need precision or convenience, both approaches serve us well in modern Java development.
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.

















