google-site-verification: google61fe8ba583a51912.html
Annotations in Java

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:

  1. Built-in Annotations: @Override, @Deprecated, @SuppressWarnings, etc.
  2. Meta-annotations: @Target, @Retention, @Documented, @Inherited, @Repeatable
  3. Custom Annotations: Create your own with @interface
  4. Runtime Processing: Use reflection to process annotations at runtime
  5. 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!

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper