Understanding Java Annotations and Reflection: Built‑in Annotations, Meta‑annotations, Custom Annotations, Class Loading and Runtime Reflection
This article explains Java annotations—including built‑in, meta‑ and custom annotations—covers how they are used in Spring, describes the Java reflection mechanism, class loading process, ways to obtain Class objects, and demonstrates retrieving generic and annotation information at runtime with code examples.
Annotations are not executable code but metadata that can be read by tools such as compilers or the JVM; they are declared with @AnnotationName and may include parameters (e.g., @SuppressWarnings(value="unchecked") ). Annotations can be placed on packages, classes, methods, fields, etc., and accessed via reflection.
Built‑in (common) annotations
@Override : indicates that a method overrides a superclass method.
@RequestMapping : maps web requests to handler methods; key attributes include value (URL), params (parameter filtering), etc.
@RequestBody : binds the HTTP request body to a method parameter and can be combined with @Valid for validation.
@GetMapping : shortcut for @RequestMapping(method = RequestMethod.GET) .
@PathVariable : binds a method parameter to a URI template variable.
@RequestParam : binds a method parameter to a request query parameter.
@ComponentScan : configures the packages that Spring should scan for components.
@Component , @Service , @Repository : mark classes as Spring beans with varying semantic intent.
Meta‑annotations
The Java API provides four meta‑annotations used to define other annotations:
@Target : specifies where the annotation can be applied (e.g., ElementType.TYPE , METHOD , FIELD ).
@Retention : defines the annotation’s lifecycle; RetentionPolicy.RUNTIME keeps it available at runtime for reflection.
@Documented : indicates whether the annotation should appear in generated Javadoc.
@Inherited : determines if the annotation is inherited by subclasses.
Custom annotations
Creating a custom annotation helps understand Spring’s internals. Example:
/**
* Custom annotation example
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "";
}Usage:
@MyAnnotation(value = "explanation")
public void test() { }Reflection overview
Reflection allows a program to inspect and manipulate classes, fields, methods, and constructors at runtime. It is the key that makes Java a "quasi‑dynamic" language.
Key APIs include java.lang.Class , java.lang.reflect.Field , java.lang.reflect.Method , and java.lang.reflect.Constructor .
Class loading and ClassLoader
The JVM loads a class in three steps: Load (read bytecode and create a Class object), Link (verify and prepare), and Initialize (execute static initializers). Initialization occurs on active references such as new , static method calls, or reflective access.
There are three primary class loaders: BootstrapClassLoader, ExtensionClassLoader, and Application (System) ClassLoader. Example to obtain the system loader:
ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
ClassLoader parent = sysLoader.getParent();
System.out.println(sysLoader);
System.out.println(parent);Obtaining a Class instance
Five common ways:
Using ClassName.class (compile‑time safe).
Calling object.getClass() on an existing instance.
Calling Class.forName("full.ClassName") with the fully‑qualified name.
Using the TYPE field of primitive wrapper classes (e.g., Integer.TYPE ).
Through a ClassLoader instance.
Runtime class structure
Once a Class object is obtained, you can retrieve all fields, methods, constructors, super‑classes, interfaces, and annotations. Example:
Class
c = Class.forName("com.example.User");
Field[] fields = c.getDeclaredFields();
for (Field f : fields) System.out.println(f);
Method[] methods = c.getDeclaredMethods();
for (Method m : methods) System.out.println(m);Generic type information
Java erases generic types at compile time, but reflective APIs can recover them via ParameterizedType . Use getGenericParameterTypes() , getGenericReturnType() , and getActualTypeArguments() to inspect generic arguments of fields, method parameters, and return types.
Retrieving annotation data
Annotations on classes, fields, methods, and parameters can be accessed with isAnnotationPresent and getAnnotation . Example for field annotations:
for (Field f : User.class.getDeclaredFields()) {
if (f.isAnnotationPresent(ApiModelProperty.class)) {
System.out.println(f.getAnnotation(ApiModelProperty.class).value());
}
}Similar logic applies to method and parameter annotations.
The notes above constitute a concise reference for Java annotations and reflection, useful for developers working with Spring or any Java‑based framework.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.