1. Introduction to Annotations
What are Annotations?
Annotations are metadata that provide data about a program but are not part of the program itself. They have no direct effect on the code they annotate.
Why Use Annotations?
- Provide information to the compiler
- Compile-time and deployment-time processing
- Runtime processing via reflection
- Code generation and boilerplate reduction
- Framework configuration (Spring, Hibernate, etc.)
Built-in Annotations:
@Override,@Deprecated,@SuppressWarnings@SafeVarargs,@FunctionalInterface- Meta-annotations:
@Target,@Retention,@Documented,@Inherited,@Repeatable
2. Basic Annotations Usage
import java.util.*;
// Class demonstrating built-in annotations
public class BuiltInAnnotationsExample {
// @Override - indicates method overriding
@Override
public String toString() {
return "BuiltInAnnotationsExample instance";
}
// @Deprecated - marks method as obsolete
@Deprecated
public void oldMethod() {
System.out.println("This is an old method");
}
// @SuppressWarnings - suppresses compiler warnings
@SuppressWarnings("unchecked")
public void methodWithWarnings() {
List list = new ArrayList(); // Raw type warning suppressed
list.add("test");
}
// @SafeVarargs - suppresses heap pollution warnings
@SafeVarargs
public final <T> void safeMethod(T... elements) {
for (T element : elements) {
System.out.println(element);
}
}
// @FunctionalInterface - ensures interface has exactly one abstract method
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
// Default methods are allowed
default void description() {
System.out.println("Calculator interface");
}
}
public static void main(String[] args) {
System.out.println("=== Built-in Annotations Examples ===");
BuiltInAnnotationsExample example = new BuiltInAnnotationsExample();
// Using @Override
System.out.println(example.toString());
// Using @Deprecated - compiler will warn about usage
example.oldMethod();
// Using @SuppressWarnings
example.methodWithWarnings();
// Using @SafeVarargs
example.safeMethod(1, 2, 3);
// Using @FunctionalInterface
Calculator adder = (a, b) -> a + b;
System.out.println("Addition: " + adder.calculate(5, 3));
// Demonstrating annotation retention
demonstrateAnnotationRetention();
}
public static void demonstrateAnnotationRetention() {
System.out.println("\n=== Annotation Retention ===");
// Get annotations using reflection
Class<BuiltInAnnotationsExample> clazz = BuiltInAnnotationsExample.class;
try {
java.lang.reflect.Method method = clazz.getMethod("oldMethod");
Deprecated deprecated = method.getAnnotation(Deprecated.class);
if (deprecated != null) {
System.out.println("oldMethod is deprecated");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
3. Complete Code Examples
Example 1: Creating Custom Annotations
import java.lang.annotation.*;
import java.lang.reflect.*;
// Custom annotation with different retention policies
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface ClassInfo {
String author();
String version() default "1.0";
String description();
String[] tags() default {};
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MethodInfo {
String purpose();
String lastModified() default "N/A";
boolean tested() default false;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface FieldInfo {
String description();
boolean required() default true;
}
@Retention(RetentionPolicy.SOURCE) // Discarded during compilation
@Target(ElementType.METHOD)
@interface DevelopmentNote {
String note();
String developer();
}
// Meta-annotation examples
@Documented // Include in javadoc
@Inherited // Inherited by subclasses
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface DocumentedAnnotation {
String value();
}
// Repeatable annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Reviews {
Review[] value();
}
@Repeatable(Reviews.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Review {
String reviewer();
String comment();
int rating(); // 1-5
}
// Using custom annotations
@ClassInfo(
author = "John Doe",
version = "2.0",
description = "User management service",
tags = {"service", "user", "management"}
)
@Review(reviewer = "Alice", comment = "Good implementation", rating = 4)
@Review(reviewer = "Bob", comment = "Needs more validation", rating = 3)
@DocumentedAnnotation("This class is documented in javadoc")
class UserService {
@FieldInfo(description = "User database connection", required = true)
private String databaseUrl;
@FieldInfo(description = "Cache enabled flag")
private boolean cacheEnabled = true;
public UserService(String databaseUrl) {
this.databaseUrl = databaseUrl;
}
@MethodInfo(
purpose = "Create new user account",
lastModified = "2024-01-15",
tested = true
)
public void createUser(String username, String email) {
System.out.println("Creating user: " + username);
}
@MethodInfo(
purpose = "Validate user credentials",
lastModified = "2024-01-10"
)
public boolean validateUser(String username, String password) {
return username != null && password != null;
}
@DevelopmentNote(
note = "This method needs optimization",
developer = "Charlie"
)
public void performanceCriticalMethod() {
// Performance-critical code
}
@SuppressWarnings("unused")
private void internalMethod() {
// Internal implementation
}
}
public class CustomAnnotationsExample {
public static void main(String[] args) {
System.out.println("=== Custom Annotations Examples ===");
// Analyze annotations using reflection
analyzeClassAnnotations(UserService.class);
analyzeMethodAnnotations(UserService.class);
analyzeFieldAnnotations(UserService.class);
// Demonstrate annotation processing
processAnnotations(new UserService("jdbc:mysql://localhost:3306/users"));
}
public static void analyzeClassAnnotations(Class<?> clazz) {
System.out.println("\n=== Class Annotations ===");
System.out.println("Class: " + clazz.getSimpleName());
// Get class-level annotations
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(" " + annotation.annotationType().getSimpleName());
if (annotation instanceof ClassInfo) {
ClassInfo classInfo = (ClassInfo) annotation;
System.out.println(" Author: " + classInfo.author());
System.out.println(" Version: " + classInfo.version());
System.out.println(" Description: " + classInfo.description());
System.out.println(" Tags: " + String.join(", ", classInfo.tags()));
}
if (annotation instanceof Reviews) {
Reviews reviews = (Reviews) annotation;
for (Review review : reviews.value()) {
System.out.println(" Review by " + review.reviewer() +
": " + review.comment() + " (Rating: " + review.rating() + ")");
}
}
}
// Get repeatable annotations directly
Review[] reviews = clazz.getAnnotationsByType(Review.class);
if (reviews.length > 0) {
System.out.println(" Direct repeatable annotations: " + reviews.length);
}
}
public static void analyzeMethodAnnotations(Class<?> clazz) {
System.out.println("\n=== Method Annotations ===");
for (Method method : clazz.getDeclaredMethods()) {
MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);
if (methodInfo != null) {
System.out.println("Method: " + method.getName());
System.out.println(" Purpose: " + methodInfo.purpose());
System.out.println(" Last Modified: " + methodInfo.lastModified());
System.out.println(" Tested: " + methodInfo.tested());
}
}
}
public static void analyzeFieldAnnotations(Class<?> clazz) {
System.out.println("\n=== Field Annotations ===");
for (Field field : clazz.getDeclaredFields()) {
FieldInfo fieldInfo = field.getAnnotation(FieldInfo.class);
if (fieldInfo != null) {
System.out.println("Field: " + field.getName());
System.out.println(" Description: " + fieldInfo.description());
System.out.println(" Required: " + fieldInfo.required());
}
}
}
public static void processAnnotations(UserService service) {
System.out.println("\n=== Annotation Processing ===");
Class<?> clazz = service.getClass();
// Process class info
ClassInfo classInfo = clazz.getAnnotation(ClassInfo.class);
if (classInfo != null) {
System.out.println("Processing " + classInfo.description() + " v" + classInfo.version());
}
// Process method annotations
for (Method method : clazz.getDeclaredMethods()) {
MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);
if (methodInfo != null && !methodInfo.tested()) {
System.out.println("WARNING: Method " + method.getName() + " is not tested!");
}
}
// Process field annotations
for (Field field : clazz.getDeclaredFields()) {
FieldInfo fieldInfo = field.getAnnotation(FieldInfo.class);
if (fieldInfo != null && fieldInfo.required()) {
try {
field.setAccessible(true);
Object value = field.get(service);
if (value == null) {
System.out.println("ERROR: Required field " + field.getName() + " is null!");
}
} catch (IllegalAccessException e) {
System.err.println("Cannot access field: " + field.getName());
}
}
}
}
}
Example 2: Validation Framework with Annotations
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
// Validation annotations
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface NotNull {
String message() default "Field cannot be null";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Size {
int min() default 0;
int max() default Integer.MAX_VALUE;
String message() default "Size constraint violated";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Email {
String message() default "Invalid email format";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Pattern {
String regex();
String message() default "Pattern constraint violated";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Range {
double min() default Double.MIN_VALUE;
double max() default Double.MAX_VALUE;
String message() default "Value out of range";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface CustomValidation {
Class<? extends FieldValidator> validator();
String message() default "Custom validation failed";
}
// Validation result
class ValidationResult {
private final boolean valid;
private final List<String> errors;
public ValidationResult(boolean valid, List<String> errors) {
this.valid = valid;
this.errors = errors;
}
public boolean isValid() { return valid; }
public List<String> getErrors() { return errors; }
public static ValidationResult valid() {
return new ValidationResult(true, Collections.emptyList());
}
public static ValidationResult invalid(String... errors) {
return new ValidationResult(false, Arrays.asList(errors));
}
}
// Validator interface
interface FieldValidator {
ValidationResult validate(Object value, Field field);
}
// Custom validators
class AgeValidator implements FieldValidator {
@Override
public ValidationResult validate(Object value, Field field) {
if (value == null) return ValidationResult.valid(); // Let @NotNull handle null
if (value instanceof Integer) {
int age = (Integer) value;
if (age < 0 || age > 150) {
return ValidationResult.invalid("Age must be between 0 and 150");
}
}
return ValidationResult.valid();
}
}
class PasswordValidator implements FieldValidator {
@Override
public ValidationResult validate(Object value, Field field) {
if (value == null) return ValidationResult.valid();
if (value instanceof String) {
String password = (String) value;
if (password.length() < 8) {
return ValidationResult.invalid("Password must be at least 8 characters long");
}
if (!password.matches(".*[A-Z].*")) {
return ValidationResult.invalid("Password must contain at least one uppercase letter");
}
if (!password.matches(".*[a-z].*")) {
return ValidationResult.invalid("Password must contain at least one lowercase letter");
}
if (!password.matches(".*\\d.*")) {
return ValidationResult.invalid("Password must contain at least one digit");
}
}
return ValidationResult.valid();
}
}
// Validation engine
class Validator {
public static ValidationResult validate(Object object) {
List<String> errors = new ArrayList<>();
Class<?> clazz = object.getClass();
for (Field field : clazz.getDeclaredFields()) {
try {
field.setAccessible(true);
Object value = field.get(object);
// Check @NotNull
NotNull notNull = field.getAnnotation(NotNull.class);
if (notNull != null && value == null) {
errors.add(field.getName() + ": " + notNull.message());
continue; // Skip further validation for null values
}
// Check @Size
Size size = field.getAnnotation(Size.class);
if (size != null && value != null) {
if (value instanceof String) {
String str = (String) value;
if (str.length() < size.min() || str.length() > size.max()) {
errors.add(field.getName() + ": " + size.message() +
" (min: " + size.min() + ", max: " + size.max() + ")");
}
} else if (value instanceof Collection) {
Collection<?> collection = (Collection<?>) value;
if (collection.size() < size.min() || collection.size() > size.max()) {
errors.add(field.getName() + ": " + size.message() +
" (min: " + size.min() + ", max: " + size.max() + ")");
}
}
}
// Check @Email
Email email = field.getAnnotation(Email.class);
if (email != null && value != null && value instanceof String) {
String emailStr = (String) value;
if (!emailStr.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$")) {
errors.add(field.getName() + ": " + email.message());
}
}
// Check @Pattern
Pattern pattern = field.getAnnotation(Pattern.class);
if (pattern != null && value != null && value instanceof String) {
String str = (String) value;
if (!str.matches(pattern.regex())) {
errors.add(field.getName() + ": " + pattern.message());
}
}
// Check @Range
Range range = field.getAnnotation(Range.class);
if (range != null && value != null) {
if (value instanceof Number) {
double num = ((Number) value).doubleValue();
if (num < range.min() || num > range.max()) {
errors.add(field.getName() + ": " + range.message() +
" (min: " + range.min() + ", max: " + range.max() + ")");
}
}
}
// Check @CustomValidation
CustomValidation custom = field.getAnnotation(CustomValidation.class);
if (custom != null) {
try {
FieldValidator validator = custom.validator().getDeclaredConstructor().newInstance();
ValidationResult result = validator.validate(value, field);
if (!result.isValid()) {
errors.addAll(result.getErrors());
}
} catch (Exception e) {
errors.add(field.getName() + ": Failed to perform custom validation - " + e.getMessage());
}
}
} catch (IllegalAccessException e) {
errors.add("Cannot access field: " + field.getName());
}
}
return errors.isEmpty() ? ValidationResult.valid() : ValidationResult.invalid(
errors.toArray(new String[0])
);
}
}
// Example entity with validation annotations
class User {
@NotNull(message = "Username is required")
@Size(min = 3, max = 20, message = "Username must be between 3 and 20 characters")
private String username;
@NotNull
@Email(message = "Invalid email address")
private String email;
@NotNull
@CustomValidation(validator = PasswordValidator.class, message = "Password does not meet requirements")
private String password;
@Range(min = 18, max = 100, message = "Age must be between 18 and 100")
@CustomValidation(validator = AgeValidator.class)
private Integer age;
@Pattern(regexp = "^[A-Z]{2}\\d{7}$", message = "ID must match pattern: AB1234567")
private String idNumber;
@Size(min = 1, max = 5, message = "User can have 1-5 roles")
private List<String> roles;
public User(String username, String email, String password, Integer age, String idNumber, List<String> roles) {
this.username = username;
this.email = email;
this.password = password;
this.age = age;
this.idNumber = idNumber;
this.roles = roles;
}
// Getters and setters
public String getUsername() { return username; }
public String getEmail() { return email; }
public String getPassword() { return password; }
public Integer getAge() { return age; }
public String getIdNumber() { return idNumber; }
public List<String> getRoles() { return roles; }
}
class Product {
@NotNull
@Size(min = 1, max = 100)
private String name;
@Range(min = 0.01, max = 10000.0)
private Double price;
@Size(min = 10, max = 1000)
private String description;
public Product(String name, Double price, String description) {
this.name = name;
this.price = price;
this.description = description;
}
}
public class ValidationFrameworkExample {
public static void main(String[] args) {
System.out.println("=== Validation Framework with Annotations ===");
// Test valid user
System.out.println("\n1. Testing Valid User:");
User validUser = new User(
"john_doe",
"[email protected]",
"SecurePass123",
25,
"AB1234567",
Arrays.asList("USER", "EDITOR")
);
testValidation(validUser);
// Test invalid user
System.out.println("\n2. Testing Invalid User:");
User invalidUser = new User(
"jo", // Too short
"invalid-email", // Invalid email
"weak", // Weak password
15, // Underage
"invalid", // Invalid ID pattern
Arrays.asList() // No roles
);
testValidation(invalidUser);
// Test null values
System.out.println("\n3. Testing User with Null Values:");
User nullUser = new User(null, null, null, null, null, null);
testValidation(nullUser);
// Test product validation
System.out.println("\n4. Testing Product Validation:");
Product invalidProduct = new Product("", -10.0, "Short");
testValidation(invalidProduct);
// Performance test
System.out.println("\n5. Performance Test:");
performanceTest();
}
public static void testValidation(Object object) {
System.out.println("Validating: " + object.getClass().getSimpleName());
ValidationResult result = Validator.validate(object);
if (result.isValid()) {
System.out.println("✅ Validation PASSED");
} else {
System.out.println("❌ Validation FAILED");
result.getErrors().forEach(error -> System.out.println(" - " + error));
}
}
public static void performanceTest() {
User user = new User("test", "[email protected]", "Password123", 25, "AB1234567",
Arrays.asList("USER"));
long startTime = System.nanoTime();
int iterations = 1000;
for (int i = 0; i < iterations; i++) {
Validator.validate(user);
}
long endTime = System.nanoTime();
double avgTime = (endTime - startTime) / 1_000_000.0 / iterations;
System.out.printf("Average validation time: %.3f ms%n", avgTime);
}
}
Example 3: Dependency Injection Framework
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
// DI Framework Annotations
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Component
@interface Service {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Component
@interface Repository {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Component
@interface Controller {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Component {
// Marker annotation
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Autowired {
String value() default "";
boolean required() default true;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Configuration {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Bean {
String value() default "";
}
// Simple DI Container
class ApplicationContext {
private final Map<String, Object> beans = new HashMap<>();
private final Map<Class<?>, String> beanNames = new HashMap<>();
public ApplicationContext(String... basePackages) {
scanAndRegisterComponents(basePackages);
performDependencyInjection();
initializeBeans();
}
private void scanAndRegisterComponents(String[] basePackages) {
// Simplified scanning - in real implementation, use proper classpath scanning
System.out.println("Scanning packages: " + Arrays.toString(basePackages));
// Manually register classes for demonstration
registerBean("userService", new UserService());
registerBean("userRepository", new UserRepository());
registerBean("emailService", new EmailService());
registerBean("appConfig", new AppConfig());
}
public void registerBean(String name, Object bean) {
beans.put(name, bean);
beanNames.put(bean.getClass(), name);
System.out.println("Registered bean: " + name + " (" + bean.getClass().getSimpleName() + ")");
}
@SuppressWarnings("unchecked")
public <T> T getBean(Class<T> type) {
String beanName = beanNames.get(type);
if (beanName != null) {
return (T) beans.get(beanName);
}
// Search by assignable type
for (Object bean : beans.values()) {
if (type.isInstance(bean)) {
return (T) bean;
}
}
throw new RuntimeException("No bean found for type: " + type.getName());
}
public <T> T getBean(String name, Class<T> type) {
Object bean = beans.get(name);
if (bean != null && type.isInstance(bean)) {
return type.cast(bean);
}
throw new RuntimeException("No bean found with name: " + name);
}
private void performDependencyInjection() {
for (Object bean : beans.values()) {
injectDependencies(bean);
}
}
private void injectDependencies(Object bean) {
Class<?> clazz = bean.getClass();
for (Field field : clazz.getDeclaredFields()) {
Autowired autowired = field.getAnnotation(Autowired.class);
if (autowired != null) {
try {
field.setAccessible(true);
Object dependency;
if (!autowired.value().isEmpty()) {
// Get by name
dependency = getBean(autowired.value(), field.getType());
} else {
// Get by type
dependency = getBean(field.getType());
}
if (dependency == null && autowired.required()) {
throw new RuntimeException("Required dependency not found for field: " + field.getName());
}
field.set(bean, dependency);
System.out.println("Injected " + field.getType().getSimpleName() +
" into " + clazz.getSimpleName() + "." + field.getName());
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to inject dependency into " + field.getName(), e);
}
}
}
}
private void initializeBeans() {
for (Object bean : beans.values()) {
initializeBean(bean);
}
}
private void initializeBean(Object bean) {
// Call @PostConstruct methods
for (Method method : bean.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(PostConstruct.class)) {
try {
method.setAccessible(true);
method.invoke(bean);
System.out.println("Called @PostConstruct on " + bean.getClass().getSimpleName() + "." + method.getName());
} catch (Exception e) {
throw new RuntimeException("Failed to call @PostConstruct method", e);
}
}
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface PostConstruct {
}
// Example components
@Service
class UserService {
@Autowired
private UserRepository userRepository;
@Autowired(required = false) // Optional dependency
private EmailService emailService;
@PostConstruct
public void init() {
System.out.println("UserService initialized");
}
public void createUser(String username) {
userRepository.save(username);
if (emailService != null) {
emailService.sendWelcomeEmail(username);
}
System.out.println("User created: " + username);
}
public List<String> getUsers() {
return userRepository.findAll();
}
}
@Repository
class UserRepository {
private final List<String> users = new ArrayList<>();
@PostConstruct
public void init() {
System.out.println("UserRepository initialized");
users.add("admin"); // Default user
}
public void save(String username) {
users.add(username);
}
public List<String> findAll() {
return new ArrayList<>(users);
}
}
@Service
class EmailService {
@PostConstruct
public void init() {
System.out.println("EmailService initialized");
}
public void sendWelcomeEmail(String username) {
System.out.println("Sending welcome email to: " + username);
}
public void sendNotification(String username, String message) {
System.out.println("Notification to " + username + ": " + message);
}
}
@Configuration
class AppConfig {
@Bean
public String appName() {
return "My Application";
}
@Bean
public Integer maxUsers() {
return 1000;
}
}
@Controller
class UserController {
@Autowired
private UserService userService;
@PostConstruct
public void init() {
System.out.println("UserController initialized");
}
public void displayUsers() {
System.out.println("Users: " + userService.getUsers());
}
}
public class DependencyInjectionExample {
public static void main(String[] args) {
System.out.println("=== Dependency Injection Framework ===");
// Create application context
ApplicationContext context = new ApplicationContext("com.example");
// Get beans and use them
UserController controller = context.getBean(UserController.class);
UserService userService = context.getBean(UserService.class);
System.out.println("\n=== Using Injected Dependencies ===");
// Use the controller
controller.displayUsers();
// Create new users
userService.createUser("john_doe");
userService.createUser("alice_smith");
controller.displayUsers();
// Demonstrate getting beans by different methods
System.out.println("\n=== Bean Retrieval Methods ===");
// By type
UserService serviceByType = context.getBean(UserService.class);
System.out.println("Retrieved by type: " + (serviceByType == userService));
// By name and type
EmailService emailService = context.getBean("emailService", EmailService.class);
emailService.sendNotification("john_doe", "Test notification");
// Demonstrate reflection analysis
System.out.println("\n=== Reflection Analysis ===");
analyzeBean(UserService.class, context);
}
public static void analyzeBean(Class<?> beanClass, ApplicationContext context) {
System.out.println("Analyzing bean: " + beanClass.getSimpleName());
Object bean = context.getBean(beanClass);
// Check annotations
Component component = beanClass.getAnnotation(Component.class);
Service service = beanClass.getAnnotation(Service.class);
Repository repository = beanClass.getAnnotation(Repository.class);
Controller controller = beanClass.getAnnotation(Controller.class);
if (service != null) System.out.println(" Type: Service");
if (repository != null) System.out.println(" Type: Repository");
if (controller != null) System.out.println(" Type: Controller");
// Check autowired fields
for (Field field : beanClass.getDeclaredFields()) {
Autowired autowired = field.getAnnotation(Autowired.class);
if (autowired != null) {
System.out.println(" Injected: " + field.getType().getSimpleName() +
" " + field.getName() +
(autowired.required() ? " (required)" : " (optional)"));
}
}
// Check post construct methods
for (Method method : beanClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(PostConstruct.class)) {
System.out.println(" Initializer: " + method.getName());
}
}
}
}
Example 4: ORM Framework with Annotations
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
import java.sql.*;
import java.time.*;
// ORM Annotations
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Entity
@interface Table {
String name();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Column {
String name() default "";
boolean nullable() default true;
boolean unique() default false;
int length() default 255;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Id {
String strategy() default "AUTO";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface GeneratedValue {
String strategy() default "AUTO";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface OneToMany {
String mappedBy() default "";
CascadeType[] cascade() default {};
FetchType fetch() default FetchType.LAZY;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface ManyToOne {
CascadeType[] cascade() default {};
FetchType fetch() default FetchType.EAGER;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Transient {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Entity
@interface Entity {
}
enum CascadeType {
ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH
}
enum FetchType {
EAGER, LAZY
}
// Simple ORM Framework
class SimpleORM {
private Connection connection;
public SimpleORM(String url, String username, String password) throws SQLException {
this.connection = DriverManager.getConnection(url, username, password);
}
public <T> void createTable(Class<T> entityClass) throws SQLException {
Table table = entityClass.getAnnotation(Table.class);
if (table == null) {
throw new IllegalArgumentException("Class is not an entity: " + entityClass.getName());
}
String tableName = table.name();
List<String> columns = new ArrayList<>();
for (Field field : entityClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Transient.class)) {
continue;
}
Column column = field.getAnnotation(Column.class);
Id id = field.getAnnotation(Id.class);
String columnName = getColumnName(field);
String columnType = getSqlType(field.getType());
List<String> constraints = new ArrayList<>();
if (id != null) {
constraints.add("PRIMARY KEY");
if ("AUTO".equals(id.strategy())) {
constraints.add("AUTO_INCREMENT");
}
}
if (column != null) {
if (!column.nullable()) {
constraints.add("NOT NULL");
}
if (column.unique()) {
constraints.add("UNIQUE");
}
if (column.length() > 0 && field.getType() == String.class) {
columnType = "VARCHAR(" + column.length() + ")";
}
}
String columnDefinition = columnName + " " + columnType + " " + String.join(" ", constraints);
columns.add(columnDefinition.trim());
}
String sql = String.format("CREATE TABLE IF NOT EXISTS %s (%s)",
tableName, String.join(", ", columns));
try (Statement stmt = connection.createStatement()) {
stmt.execute(sql);
System.out.println("Created table: " + tableName);
}
}
public <T> void save(T entity) throws SQLException, IllegalAccessException {
Class<?> entityClass = entity.getClass();
Table table = entityClass.getAnnotation(Table.class);
if (table == null) {
throw new IllegalArgumentException("Class is not an entity: " + entityClass.getName());
}
String tableName = table.name();
List<String> columnNames = new ArrayList<>();
List<String> placeholders = new ArrayList<>();
List<Object> values = new ArrayList<>();
Field idField = null;
Object idValue = null;
for (Field field : entityClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Transient.class)) {
continue;
}
field.setAccessible(true);
Object value = field.get(entity);
if (field.isAnnotationPresent(Id.class)) {
idField = field;
idValue = value;
}
Column column = field.getAnnotation(Column.class);
String columnName = getColumnName(field);
columnNames.add(columnName);
placeholders.add("?");
values.add(value);
}
String sql;
if (idValue == null) {
// INSERT
sql = String.format("INSERT INTO %s (%s) VALUES (%s)",
tableName,
String.join(", ", columnNames),
String.join(", ", placeholders));
} else {
// UPDATE
List<String> updates = new ArrayList<>();
for (int i = 0; i < columnNames.size(); i++) {
updates.add(columnNames.get(i) + " = ?");
}
sql = String.format("UPDATE %s SET %s WHERE %s = ?",
tableName,
String.join(", ", updates),
getColumnName(idField));
values.add(idValue); // Add ID for WHERE clause
}
try (PreparedStatement stmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
for (int i = 0; i < values.size(); i++) {
stmt.setObject(i + 1, values.get(i));
}
int affectedRows = stmt.executeUpdate();
if (idValue == null) {
// Get generated ID
try (ResultSet generatedKeys = stmt.getGeneratedKeys()) {
if (generatedKeys.next()) {
idField.set(entity, generatedKeys.getObject(1));
}
}
}
System.out.println((idValue == null ? "Inserted" : "Updated") +
" entity in " + tableName + " (" + affectedRows + " rows)");
}
}
public <T> T findById(Class<T> entityClass, Object id) throws SQLException, IllegalAccessException, InstantiationException {
Table table = entityClass.getAnnotation(Table.class);
if (table == null) {
throw new IllegalArgumentException("Class is not an entity: " + entityClass.getName());
}
String tableName = table.name();
Field idField = null;
String idColumn = null;
for (Field field : entityClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Id.class)) {
idField = field;
idColumn = getColumnName(field);
break;
}
}
if (idField == null) {
throw new IllegalArgumentException("Entity has no @Id field: " + entityClass.getName());
}
String sql = String.format("SELECT * FROM %s WHERE %s = ?", tableName, idColumn);
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setObject(1, id);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return mapResultSetToEntity(rs, entityClass);
}
}
}
return null;
}
public <T> List<T> findAll(Class<T> entityClass) throws SQLException, IllegalAccessException, InstantiationException {
Table table = entityClass.getAnnotation(Table.class);
if (table == null) {
throw new IllegalArgumentException("Class is not an entity: " + entityClass.getName());
}
String tableName = table.name();
String sql = "SELECT * FROM " + tableName;
List<T> results = new ArrayList<>();
try (Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
results.add(mapResultSetToEntity(rs, entityClass));
}
}
return results;
}
private <T> T mapResultSetToEntity(ResultSet rs, Class<T> entityClass)
throws SQLException, IllegalAccessException, InstantiationException {
T entity = entityClass.getDeclaredConstructor().newInstance();
for (Field field : entityClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Transient.class)) {
continue;
}
field.setAccessible(true);
String columnName = getColumnName(field);
Object value = rs.getObject(columnName);
// Handle type conversion
if (value != null) {
if (field.getType() == LocalDate.class && value instanceof java.sql.Date) {
value = ((java.sql.Date) value).toLocalDate();
} else if (field.getType() == LocalDateTime.class && value instanceof java.sql.Timestamp) {
value = ((java.sql.Timestamp) value).toLocalDateTime();
}
}
field.set(entity, value);
}
return entity;
}
private String getColumnName(Field field) {
Column column = field.getAnnotation(Column.class);
if (column != null && !column.name().isEmpty()) {
return column.name();
}
return field.getName();
}
private String getSqlType(Class<?> type) {
if (type == String.class) return "VARCHAR(255)";
if (type == Integer.class || type == int.class) return "INT";
if (type == Long.class || type == long.class) return "BIGINT";
if (type == Double.class || type == double.class) return "DOUBLE";
if (type == Boolean.class || type == boolean.class) return "BOOLEAN";
if (type == LocalDate.class) return "DATE";
if (type == LocalDateTime.class) return "TIMESTAMP";
return "VARCHAR(255)";
}
public void close() throws SQLException {
if (connection != null) {
connection.close();
}
}
}
// Example Entities
@Table(name = "users")
class User {
@Id
@GeneratedValue
private Long id;
@Column(name = "username", nullable = false, unique = true, length = 50)
private String username;
@Column(name = "email", nullable = false, unique = true)
private String email;
@Column(name = "created_at")
private LocalDateTime createdAt;
@Transient
private String temporaryData;
// Constructors
public User() {}
public User(String username, String email) {
this.username = username;
this.email = email;
this.createdAt = LocalDateTime.now();
}
// Getters and setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public String getTemporaryData() { return temporaryData; }
public void setTemporaryData(String temporaryData) { this.temporaryData = temporaryData; }
@Override
public String toString() {
return String.format("User{id=%d, username='%s', email='%s', createdAt=%s}",
id, username, email, createdAt);
}
}
@Table(name = "products")
class Product {
@Id
@GeneratedValue
private Long id;
@Column(name = "name", nullable = false, length = 100)
private String name;
@Column(name = "price", nullable = false)
private Double price;
@Column(name = "in_stock")
private Boolean inStock;
@Column(name = "created_date")
private LocalDate createdDate;
public Product() {}
public Product(String name, Double price, Boolean inStock) {
this.name = name;
this.price = price;
this.inStock = inStock;
this.createdDate = LocalDate.now();
}
// Getters and setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Double getPrice() { return price; }
public void setPrice(Double price) { this.price = price; }
public Boolean getInStock() { return inStock; }
public void setInStock(Boolean inStock) { this.inStock = inStock; }
public LocalDate getCreatedDate() { return createdDate; }
public void setCreatedDate(LocalDate createdDate) { this.createdDate = createdDate; }
@Override
public String toString() {
return String.format("Product{id=%d, name='%s', price=%.2f, inStock=%s, createdDate=%s}",
id, name, price, inStock, createdDate);
}
}
public class ORMExample {
public static void main(String[] args) {
System.out.println("=== ORM Framework with Annotations ===");
// Use in-memory H2 database for demonstration
String url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";
String username = "sa";
String password = "";
try (SimpleORM orm = new SimpleORM(url, username, password)) {
// Create tables
orm.createTable(User.class);
orm.createTable(Product.class);
// Save entities
System.out.println("\n=== Saving Entities ===");
User user1 = new User("john_doe", "[email protected]");
User user2 = new User("alice_smith", "[email protected]");
orm.save(user1);
orm.save(user2);
Product product1 = new Product("Laptop", 999.99, true);
Product product2 = new Product("Mouse", 25.50, false);
orm.save(product1);
orm.save(product2);
// Find by ID
System.out.println("\n=== Finding by ID ===");
User foundUser = orm.findById(User.class, user1.getId());
System.out.println("Found user: " + foundUser);
Product foundProduct = orm.findById(Product.class, product1.getId());
System.out.println("Found product: " + foundProduct);
// Find all
System.out.println("\n=== Finding All Entities ===");
List<User> allUsers = orm.findAll(User.class);
System.out.println("All users:");
allUsers.forEach(System.out::println);
List<Product> allProducts = orm.findAll(Product.class);
System.out.println("All products:");
allProducts.forEach(System.out::println);
// Update entity
System.out.println("\n=== Updating Entity ===");
user1.setEmail("[email protected]");
orm.save(user1);
User updatedUser = orm.findById(User.class, user1.getId());
System.out.println("Updated user: " + updatedUser);
// Demonstrate annotation analysis
System.out.println("\n=== Entity Analysis ===");
analyzeEntity(User.class);
analyzeEntity(Product.class);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void analyzeEntity(Class<?> entityClass) {
System.out.println("Analyzing entity: " + entityClass.getSimpleName());
Table table = entityClass.getAnnotation(Table.class);
if (table != null) {
System.out.println(" Table: " + table.name());
}
for (Field field : entityClass.getDeclaredFields()) {
System.out.println(" Field: " + field.getName() + " (" + field.getType().getSimpleName() + ")");
if (field.isAnnotationPresent(Id.class)) {
System.out.println(" @Id");
}
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
System.out.println(" @Column(name=\"" + column.name() + "\", " +
"nullable=" + column.nullable() + ", " +
"unique=" + column.unique() + ", " +
"length=" + column.length() + ")");
}
if (field.isAnnotationPresent(Transient.class)) {
System.out.println(" @Transient");
}
}
}
}
Example 5: Advanced Annotation Processing
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.*;
// Advanced annotation examples
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface ValidationGroup {
Class<?>[] value() default {};
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface ConditionalValidation {
String dependsOn();
String condition();
String message() default "Conditional validation failed";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Cacheable {
String key() default "";
long ttl() default 3600; // Time to live in seconds
CacheStrategy strategy() default CacheStrategy.LRU;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Retryable {
int maxAttempts() default 3;
long delay() default 1000; // milliseconds
Class<? extends Throwable>[] retryOn() default {Exception.class};
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Loggable {
LogLevel level() default LogLevel.INFO;
boolean logParameters() default true;
boolean logResult() default false;
boolean logExecutionTime() default true;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Secure {
String[] roles() default {};
String permission() default "";
boolean requireAuthentication() default true;
}
enum CacheStrategy {
LRU, FIFO, LFU
}
enum LogLevel {
DEBUG, INFO, WARN, ERROR
}
// Annotation processor
class AnnotationProcessor {
public static void processAnnotations(Object target) {
Class<?> clazz = target.getClass();
// Process class-level annotations
processClassAnnotations(clazz);
// Process method annotations
for (Method method : clazz.getDeclaredMethods()) {
processMethodAnnotations(method, target);
}
// Process field annotations
for (Field field : clazz.getDeclaredFields()) {
processFieldAnnotations(field, target);
}
}
private static void processClassAnnotations(Class<?> clazz) {
System.out.println("Processing class: " + clazz.getSimpleName());
// Add class-level annotation processing here
}
private static void processMethodAnnotations(Method method, Object target) {
method.setAccessible(true);
// @Loggable processing
Loggable loggable = method.getAnnotation(Loggable.class);
if (loggable != null) {
wrapWithLogging(method, target, loggable);
}
// @Cacheable processing
Cacheable cacheable = method.getAnnotation(Cacheable.class);
if (cacheable != null) {
wrapWithCaching(method, target, cacheable);
}
// @Retryable processing
Retryable retryable = method.getAnnotation(Retryable.class);
if (retryable != null) {
wrapWithRetry(method, target, retryable);
}
// @Secure processing
Secure secure = method.getAnnotation(Secure.class);
if (secure != null) {
wrapWithSecurity(method, target, secure);
}
}
private static void processFieldAnnotations(Field field, Object target) {
field.setAccessible(true);
// Process field-level annotations
ConditionalValidation conditional = field.getAnnotation(ConditionalValidation.class);
if (conditional != null) {
validateConditionally(field, target, conditional);
}
}
private static void wrapWithLogging(Method method, Object target, Loggable loggable) {
System.out.println("Wrapping " + method.getName() + " with logging (level: " + loggable.level() + ")");
// In a real implementation, this would use AOP or dynamic proxies
}
private static void wrapWithCaching(Method method, Object target, Cacheable cacheable) {
System.out.println("Wrapping " + method.getName() + " with caching (TTL: " + cacheable.ttl() + "s)");
// In a real implementation, this would integrate with a cache system
}
private static void wrapWithRetry(Method method, Object target, Retryable retryable) {
System.out.println("Wrapping " + method.getName() + " with retry (max attempts: " + retryable.maxAttempts() + ")");
// In a real implementation, this would implement retry logic
}
private static void wrapWithSecurity(Method method, Object target, Secure secure) {
System.out.println("Wrapping " + method.getName() + " with security (roles: " +
Arrays.toString(secure.roles()) + ")");
// In a real implementation, this would check authentication and authorization
}
private static void validateConditionally(Field field, Object target, ConditionalValidation conditional) {
try {
Object value = field.get(target);
// Implement conditional validation logic
System.out.println("Conditional validation for field: " + field.getName());
} catch (IllegalAccessException e) {
System.err.println("Cannot access field: " + field.getName());
}
}
}
// Example service with advanced annotations
class AdvancedService {
@Cacheable(key = "user_{0}", ttl = 3600, strategy = CacheStrategy.LRU)
@Loggable(level = LogLevel.INFO, logParameters = true, logExecutionTime = true)
@Retryable(maxAttempts = 3, delay = 1000, retryOn = {RuntimeException.class})
public String getUserData(String userId) {
System.out.println("Fetching user data for: " + userId);
// Simulate database call
if (Math.random() < 0.3) {
throw new RuntimeException("Database temporarily unavailable");
}
return "User data for " + userId;
}
@Secure(roles = {"ADMIN", "MANAGER"}, permission = "USER_DELETE")
@Loggable(level = LogLevel.WARN, logParameters = true, logResult = false)
public void deleteUser(String userId) {
System.out.println("Deleting user: " + userId);
}
@Cacheable(key = "products", ttl = 1800)
@Loggable(level = LogLevel.DEBUG, logExecutionTime = true)
public List<String> getProducts() {
System.out.println("Fetching products");
return Arrays.asList("Product1", "Product2", "Product3");
}
@ConditionalValidation(dependsOn = "age", condition = "value > 18", message = "Must be adult")
private String driversLicense;
public void setDriversLicense(String license) {
this.driversLicense = license;
}
}
// Custom annotation-based framework
class Framework {
private Map<String, Object> cache = new HashMap<>();
private int callCount = 0;
public Object invokeMethod(Object target, Method method, Object[] args) throws Exception {
callCount++;
// Check @Secure
Secure secure = method.getAnnotation(Secure.class);
if (secure != null) {
checkSecurity(secure);
}
// Check @Cacheable
Cacheable cacheable = method.getAnnotation(Cacheable.class);
if (cacheable != null) {
String cacheKey = generateCacheKey(method, args, cacheable);
if (cache.containsKey(cacheKey)) {
System.out.println("Cache hit for key: " + cacheKey);
return cache.get(cacheKey);
}
}
// Execute with @Retryable
Retryable retryable = method.getAnnotation(Retryable.class);
if (retryable != null) {
return executeWithRetry(target, method, args, retryable);
}
// Execute normally
return method.invoke(target, args);
}
private void checkSecurity(Secure secure) {
System.out.println("Checking security: roles=" + Arrays.toString(secure.roles()) +
", auth=" + secure.requireAuthentication());
// In real implementation, check user permissions
}
private String generateCacheKey(Method method, Object[] args, Cacheable cacheable) {
if (!cacheable.key().isEmpty()) {
// Replace placeholders with actual values
String key = cacheable.key();
for (int i = 0; i < args.length; i++) {
key = key.replace("{" + i + "}", String.valueOf(args[i]));
}
return key;
}
return method.getName() + "_" + Arrays.hashCode(args);
}
private Object executeWithRetry(Object target, Method method, Object[] args, Retryable retryable)
throws Exception {
int attempts = 0;
while (attempts < retryable.maxAttempts()) {
try {
attempts++;
System.out.println("Attempt " + attempts + " of " + retryable.maxAttempts());
return method.invoke(target, args);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
boolean shouldRetry = Arrays.stream(retryable.retryOn())
.anyMatch(exceptionClass -> exceptionClass.isInstance(cause));
if (!shouldRetry || attempts == retryable.maxAttempts()) {
throw e;
}
System.out.println("Retryable exception caught, waiting " + retryable.delay() + "ms");
Thread.sleep(retryable.delay());
}
}
throw new IllegalStateException("Should not reach here");
}
public int getCallCount() {
return callCount;
}
}
public class AdvancedAnnotationProcessing {
public static void main(String[] args) throws Exception {
System.out.println("=== Advanced Annotation Processing ===");
AdvancedService service = new AdvancedService();
// Process annotations
System.out.println("1. Annotation Processing:");
AnnotationProcessor.processAnnotations(service);
// Test framework with annotations
System.out.println("\n2. Framework Execution:");
Framework framework = new Framework();
// Test cached method with retry
System.out.println("\nTesting getUserData (cached, retryable):");
for (int i = 0; i < 3; i++) {
try {
Method method = AdvancedService.class.getMethod("getUserData", String.class);
String result = (String) framework.invokeMethod(service, method, new Object[]{"user123"});
System.out.println("Result: " + result);
} catch (Exception e) {
System.err.println("Error: " + e.getCause().getMessage());
}
}
// Test secure method
System.out.println("\nTesting deleteUser (secure):");
try {
Method method = AdvancedService.class.getMethod("deleteUser", String.class);
framework.invokeMethod(service, method, new Object[]{"user456"});
} catch (Exception e) {
e.printStackTrace();
}
// Test products method
System.out.println("\nTesting getProducts (cached):");
try {
Method method = AdvancedService.class.getMethod("getProducts");
List<String> products = (List<String>) framework.invokeMethod(service, method, new Object[]{});
System.out.println("Products: " + products);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("\nTotal method calls: " + framework.getCallCount());
// Demonstrate annotation inheritance
System.out.println("\n3. Annotation Inheritance:");
demonstrateAnnotationInheritance();
// Demonstrate meta-annotations
System.out.println("\n4. Meta-annotations:");
demonstrateMetaAnnotations();
}
public static void demonstrateAnnotationInheritance() {
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(ElementType.TYPE)
@interface InheritedAnnotation {
String value();
}
@InheritedAnnotation("parent")
class ParentClass {}
class ChildClass extends ParentClass {}
// Check if annotation is inherited
InheritedAnnotation parentAnnotation = ParentClass.class.getAnnotation(InheritedAnnotation.class);
InheritedAnnotation childAnnotation = ChildClass.class.getAnnotation(InheritedAnnotation.class);
System.out.println("Parent annotation: " + (parentAnnotation != null ? parentAnnotation.value() : "null"));
System.out.println("Child annotation: " + (childAnnotation != null ? childAnnotation.value() : "null"));
System.out.println("Annotation inherited: " + (childAnnotation != null));
}
public static void demonstrateMetaAnnotations() {
// Analyze meta-annotations on built-in annotations
Class<?>[] annotationClasses = {
Override.class, Deprecated.class, SuppressWarnings.class,
Target.class, Retention.class, Documented.class, Inherited.class
};
System.out.println("Meta-annotations analysis:");
for (Class<?> annotationClass : annotationClasses) {
System.out.println("\n" + annotationClass.getSimpleName() + ":");
Retention retention = annotationClass.getAnnotation(Retention.class);
Target target = annotationClass.getAnnotation(Target.class);
Documented documented = annotationClass.getAnnotation(Documented.class);
Inherited inherited = annotationClass.getAnnotation(Inherited.class);
if (retention != null) {
System.out.println(" @Retention(" + retention.value() + ")");
}
if (target != null) {
System.out.println(" @Target(" + Arrays.toString(target.value()) + ")");
}
if (documented != null) {
System.out.println(" @Documented");
}
if (inherited != null) {
System.out.println(" @Inherited");
}
}
}
}
9. Conclusion
Key Takeaways:
- Built-in Annotations:
@Override,@Deprecated,@SuppressWarnings, etc. - Meta-annotations:
@Target,@Retention,@Documented,@Inherited,@Repeatable - Custom Annotations: Create your own with
@interface - Runtime Processing: Use reflection to process annotations at runtime
- Compile-time Processing: Use annotation processors for code generation
Annotation Retention Policies:
- SOURCE: Discarded during compilation
- CLASS: Recorded in class file but not retained at runtime
- RUNTIME: Recorded in class file and available at runtime
Common Use Cases:
- Configuration: Spring, Hibernate configuration
- Validation: Bean validation frameworks
- Code Generation: Lombok, MapStruct
- Dependency Injection: Spring, Guice
- Testing: JUnit, TestNG
- Documentation: Swagger/OpenAPI
Best Practices:
- ✅ Use meaningful annotation names
- ✅ Provide default values for annotation elements
- ✅ Document your custom annotations
- ✅ Consider performance when using runtime annotations
- ✅ Use appropriate retention policies
Advanced Features:
- Repeatable Annotations: Java 8+ feature for multiple annotations of same type
- Type Annotations: Java 8+ feature for annotating types
- Annotation Processors: Compile-time processing for code generation
Performance Considerations:
- Reflection-based annotation processing can be slow
- Compile-time processing has no runtime overhead
- Cache annotation lookups when possible
- Consider AOP for cross-cutting concerns
Final Thoughts:
Annotations have revolutionized Java development by enabling:
- Declarative programming styles
- Reduced boilerplate code
- Framework integration without invasive code
- Compile-time safety and verification
Master annotations to write more expressive, maintainable, and framework-compatible Java code!