Annotations in Java

Table of Contents

  1. Introduction to Annotations
  2. Built-in Annotations
  3. Custom Annotations
  4. Annotation Retention
  5. Annotation Targets
  6. Annotation Parameters
  7. Repeatable Annotations
  8. Annotation Processing
  9. Best Practices
  10. Complete Examples

Introduction to 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 operation of the code they annotate.

Basic Syntax:

@AnnotationName public class MyClass { @AnnotationName private String field; @AnnotationName public void method() { // method body } }

Simple Example:

// Using built-in annotation @Deprecated public class OldClass { @SuppressWarnings("unchecked") public void oldMethod() { List list = new ArrayList(); // Raw type - generates warning list.add("test"); } } public class AnnotationDemo { public static void main(String[] args) { OldClass old = new OldClass(); old.oldMethod(); } }

Built-in Annotations

1. @Override:

public class OverrideExample { static class Animal { public void makeSound() { System.out.println("Animal makes a sound"); } public void eat() { System.out.println("Animal is eating"); } } static class Dog extends Animal { // ✅ Correct - overrides parent method @Override public void makeSound() { System.out.println("Dog barks"); } // ❌ Compile-time error - method doesn't exist in parent // @Override // public void makeNoise() { // This would cause error // System.out.println("Dog makes noise"); // } // No @Override - but if parent method changes, no error public void eat() { System.out.println("Dog is eating"); } } public static void main(String[] args) { Dog dog = new Dog(); dog.makeSound(); // Dog barks dog.eat(); // Dog is eating } }

2. @Deprecated:

public class DeprecatedExample { /** * Old way of greeting - use {@link #newGreet(String)} instead * @deprecated This method is deprecated because it uses old formatting */ @Deprecated(since = "2.0", forRemoval = true) public static void oldGreet(String name) { System.out.println("Hello, " + name + "!"); } /** * New way of greeting with better formatting */ public static void newGreet(String name) { System.out.println("🌟 Hello, " + name + "! 🌟"); } public static void main(String[] args) { // This will show warning in IDE oldGreet("Alice"); // Deprecated method call // Recommended way newGreet("Bob"); // Using reflection to check annotation try { Method oldMethod = DeprecatedExample.class.getMethod("oldGreet", String.class); Deprecated deprecated = oldMethod.getAnnotation(Deprecated.class); if (deprecated != null) { System.out.println("oldGreet is deprecated since: " + deprecated.since()); System.out.println("Scheduled for removal: " + deprecated.forRemoval()); } } catch (NoSuchMethodException e) { e.printStackTrace(); } } }

3. @SuppressWarnings:

import java.util.*; public class SuppressWarningsExample { // Suppress unchecked warnings for this entire class @SuppressWarnings("unchecked") public void processRawList() { List rawList = new ArrayList(); // Raw type usage rawList.add("String"); rawList.add(123); // Mixing types String str = (String) rawList.get(0); // Unchecked cast } public void methodWithMultipleWarnings() { // Suppress multiple warnings @SuppressWarnings({"rawtypes", "unchecked"}) List list = new ArrayList(); list.add("test"); // Suppress deprecation warning @SuppressWarnings("deprecation") Date date = new Date(2020, 1, 1); // Deprecated constructor } // Common warning types: // - "unchecked" - for generic type safety // - "deprecation" - for use of deprecated items // - "rawtypes" - for use of raw types // - "unused" - for unused variables // - "all" - for all warnings public static void main(String[] args) { SuppressWarningsExample example = new SuppressWarningsExample(); example.processRawList(); example.methodWithMultipleWarnings(); } }

4. @FunctionalInterface:

public class FunctionalInterfaceExample { // Valid functional interface - has exactly one abstract method @FunctionalInterface interface Calculator { int calculate(int a, int b); // Can have default methods default void printResult(int result) { System.out.println("Result: " + result); } // Can have static methods static String getOperationName() { return "Calculation"; } } // ❌ Invalid - will cause compile error // @FunctionalInterface // interface InvalidFunctional { // void method1(); // void method2(); // More than one abstract method // } public static void main(String[] args) { // Using lambda with functional interface Calculator adder = (a, b) -> a + b; Calculator multiplier = (a, b) -> a * b; System.out.println("Addition: " + adder.calculate(5, 3)); System.out.println("Multiplication: " + multiplier.calculate(5, 3)); adder.printResult(adder.calculate(10, 20)); System.out.println("Operation: " + Calculator.getOperationName()); } }

Custom Annotations

Creating Custom Annotations:

import java.lang.annotation.*; import java.lang.reflect.*; // Marker annotation (no parameters) @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Development { // No elements - marker annotation } // Single-value annotation @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) @interface Author { String value(); // Special case - can use without name when only one element } // Multi-value annotation @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface TestCase { int id(); String description(); String[] tags() default {}; // Optional element with default value boolean enabled() default true; } // Using the custom annotations @Development @Author("John Doe") class PaymentProcessor { @TestCase( id = 1, description = "Test successful payment", tags = {"payment", "success"}, enabled = true ) public void processPayment(double amount) { System.out.println("Processing payment: $" + amount); } @TestCase( id = 2, description = "Test failed payment", tags = {"payment", "failure"}, enabled = false // This test is disabled ) public void processFailedPayment(double amount) { System.out.println("Processing failed payment: $" + amount); } @Author("Jane Smith") public void refundPayment(double amount) { System.out.println("Refunding payment: $" + amount); } } public class CustomAnnotationDemo { public static void main(String[] args) { // Process annotations using reflection Class<PaymentProcessor> processorClass = PaymentProcessor.class; // Check class-level annotations if (processorClass.isAnnotationPresent(Development.class)) { System.out.println("PaymentProcessor is in development"); } Author classAuthor = processorClass.getAnnotation(Author.class); if (classAuthor != null) { System.out.println("Class author: " + classAuthor.value()); } // Process method annotations for (Method method : processorClass.getDeclaredMethods()) { System.out.println("\nMethod: " + method.getName()); // Check TestCase annotation TestCase testCase = method.getAnnotation(TestCase.class); if (testCase != null) { System.out.println(" Test ID: " + testCase.id()); System.out.println(" Description: " + testCase.description()); System.out.println(" Enabled: " + testCase.enabled()); System.out.println(" Tags: " + String.join(", ", testCase.tags())); } // Check Author annotation Author author = method.getAnnotation(Author.class); if (author != null) { System.out.println(" Author: " + author.value()); } } } }

Output:

PaymentProcessor is in development Class author: John Doe Method: processPayment Test ID: 1 Description: Test successful payment Enabled: true Tags: payment, success Method: processFailedPayment Test ID: 2 Description: Test failed payment Enabled: false Tags: payment, failure Method: refundPayment Author: Jane Smith

Annotation Retention

Retention Policies:

import java.lang.annotation.*; // SOURCE - discarded by compiler, not available at runtime @Retention(RetentionPolicy.SOURCE) @interface SourceLevelAnnotation { String value(); } // CLASS - stored in .class file but not available at runtime (default) @Retention(RetentionPolicy.CLASS) @interface ClassLevelAnnotation { String value(); } // RUNTIME - stored in .class file and available at runtime @Retention(RetentionPolicy.RUNTIME) @interface RuntimeLevelAnnotation { String value(); } @SourceLevelAnnotation("This won't be available at runtime") @ClassLevelAnnotation("This might be available depending on JVM") @RuntimeLevelAnnotation("This will be available at runtime") class RetentionDemo { @SourceLevelAnnotation("source method") @ClassLevelAnnotation("class method") @RuntimeLevelAnnotation("runtime method") public void demoMethod() { System.out.println("Method demonstration"); } } public class RetentionExample { public static void main(String[] args) { Class<RetentionDemo> demoClass = RetentionDemo.class; // Check runtime annotations System.out.println("=== Runtime Annotations ==="); RuntimeLevelAnnotation runtime = demoClass.getAnnotation(RuntimeLevelAnnotation.class); if (runtime != null) { System.out.println("Class Runtime: " + runtime.value()); } // Check class-level annotations (may not be available) System.out.println("\n=== Class-level Annotations ==="); ClassLevelAnnotation classLevel = demoClass.getAnnotation(ClassLevelAnnotation.class); if (classLevel != null) { System.out.println("Class Class-level: " + classLevel.value()); } else { System.out.println("Class-level annotation not available at runtime"); } // Check source-level annotations (won't be available) System.out.println("\n=== Source-level Annotations ==="); SourceLevelAnnotation source = demoClass.getAnnotation(SourceLevelAnnotation.class); if (source != null) { System.out.println("Class Source: " + source.value()); } else { System.out.println("Source-level annotation not available at runtime"); } // Check method annotations try { Method method = demoClass.getMethod("demoMethod"); System.out.println("\n=== Method Annotations ==="); RuntimeLevelAnnotation methodRuntime = method.getAnnotation(RuntimeLevelAnnotation.class); if (methodRuntime != null) { System.out.println("Method Runtime: " + methodRuntime.value()); } } catch (NoSuchMethodException e) { e.printStackTrace(); } } }

Output:

=== Runtime Annotations === Class Runtime: This will be available at runtime === Class-level Annotations === Class-level annotation not available at runtime === Source-level Annotations === Source-level annotation not available at runtime === Method Annotations === Method Runtime: runtime method

Annotation Targets

Target Elements:

import java.lang.annotation.*; // Annotation that can be applied to different elements @Target({ ElementType.TYPE, // Class, interface, enum ElementType.FIELD, // Field (including enum constants) ElementType.METHOD, // Method ElementType.PARAMETER, // Method parameter ElementType.CONSTRUCTOR, // Constructor ElementType.LOCAL_VARIABLE, // Local variable ElementType.ANNOTATION_TYPE,// Annotation type ElementType.PACKAGE, // Package ElementType.TYPE_PARAMETER, // Type parameter (Java 8+) ElementType.TYPE_USE // Type use (Java 8+) }) @Retention(RetentionPolicy.RUNTIME) @interface MultiTarget { String value(); } // Specific target examples @Target(ElementType.TYPE) @interface ClassInfo { String author(); String version(); } @Target(ElementType.METHOD) @interface Operation { String name(); boolean transactional() default true; } @Target(ElementType.FIELD) @interface Column { String name(); boolean nullable() default true; } @Target(ElementType.PARAMETER) @interface NotNull { String message() default "Parameter cannot be null"; } @Target(ElementType.TYPE_USE) @interface NonNull { // For type checking } // Using the targeted annotations @ClassInfo(author = "Alice", version = "1.0") class UserService { @Column(name = "user_id", nullable = false) private Long userId; @Column(name = "username") private String username; public UserService(@NotNull String initialUser) { this.username = initialUser; } @Operation(name = "createUser", transactional = true) public void createUser(@NotNull String username, @NotNull String email) { this.username = username; System.out.println("Creating user: " + username); } @Operation(name = "deleteUser") public void deleteUser(Long userId) { System.out.println("Deleting user: " + userId); } // Using TYPE_USE annotation public @NonNull String getUserName() { return username; } public void setUserName(@NonNull String username) { this.username = username; } } public class TargetExample { public static void main(String[] args) throws NoSuchMethodException { Class<UserService> serviceClass = UserService.class; // Class-level annotation ClassInfo classInfo = serviceClass.getAnnotation(ClassInfo.class); if (classInfo != null) { System.out.println("Class Author: " + classInfo.author()); System.out.println("Class Version: " + classInfo.version()); } // Method annotations Method createUserMethod = serviceClass.getMethod("createUser", String.class, String.class); Operation operation = createUserMethod.getAnnotation(Operation.class); if (operation != null) { System.out.println("\nMethod Operation: " + operation.name()); System.out.println("Transactional: " + operation.transactional()); } // Parameter annotations Annotation[][] paramAnnotations = createUserMethod.getParameterAnnotations(); for (int i = 0; i < paramAnnotations.length; i++) { for (Annotation annotation : paramAnnotations[i]) { if (annotation instanceof NotNull) { System.out.println("Parameter " + i + " has @NotNull: " + ((NotNull) annotation).message()); } } } // Field annotations for (java.lang.reflect.Field field : serviceClass.getDeclaredFields()) { Column column = field.getAnnotation(Column.class); if (column != null) { System.out.println("\nField: " + field.getName()); System.out.println("Column: " + column.name()); System.out.println("Nullable: " + column.nullable()); } } } }

Output:

Class Author: Alice Class Version: 1.0 Method Operation: createUser Transactional: true Parameter 0 has @NotNull: Parameter cannot be null Parameter 1 has @NotNull: Parameter cannot be null Field: userId Column: user_id Nullable: false Field: username Column: username Nullable: true

Annotation Parameters

Parameter Types and Defaults:

import java.lang.annotation.*; import java.util.*; // Annotation with various parameter types @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface TestConfig { // Primitive types int timeout() default 30; // seconds boolean enabled() default true; // String String name(); String description() default ""; // Class Class<?> expectedException() default None.class; // Enum Priority priority() default Priority.MEDIUM; // Array String[] tags() default {}; // Annotation Author author() default @Author("Unknown"); // Special marker for no exception class None extends Throwable { private None() {} } } // Enum for priority enum Priority { LOW, MEDIUM, HIGH, CRITICAL } // Nested annotation @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface Author { String value(); String email() default ""; } // Using the annotation with different parameter combinations class TestConfigurationExample { @TestConfig( name = "userCreationTest", timeout = 60, priority = Priority.HIGH, tags = {"user", "creation", "integration"}, author = @Author(value = "Bob", email = "[email protected]") ) public void testUserCreation() { System.out.println("Testing user creation..."); } @TestConfig( name = "quickTest", description = "A quick sanity check", expectedException = IllegalArgumentException.class ) public void testWithException() { System.out.println("Testing exception case..."); throw new IllegalArgumentException("Expected exception"); } @TestConfig( name = "disabledTest", enabled = false ) public void disabledTest() { System.out.println("This test is disabled"); } // Using default values for everything @TestConfig(name = "defaultTest") public void defaultTest() { System.out.println("Test with default values"); } } public class ParameterExample { public static void main(String[] args) throws NoSuchMethodException { Class<TestConfigurationExample> testClass = TestConfigurationExample.class; // Process each method with @TestConfig annotation for (Method method : testClass.getDeclaredMethods()) { TestConfig config = method.getAnnotation(TestConfig.class); if (config != null) { System.out.println("\n=== " + method.getName() + " ==="); System.out.println("Name: " + config.name()); System.out.println("Description: " + config.description()); System.out.println("Timeout: " + config.timeout() + "s"); System.out.println("Enabled: " + config.enabled()); System.out.println("Priority: " + config.priority()); System.out.println("Expected Exception: " + config.expectedException().getSimpleName()); System.out.println("Tags: " + Arrays.toString(config.tags())); Author author = config.author(); System.out.println("Author: " + author.value() + (author.email().isEmpty() ? "" : " (" + author.email() + ")")); // Simulate test execution based on configuration if (config.enabled()) { System.out.println("✅ Test would be executed"); } else { System.out.println("❌ Test is disabled"); } } } } }

Output:

=== testUserCreation === Name: userCreationTest Description: Timeout: 60s Enabled: true Priority: HIGH Expected Exception: None Tags: [user, creation, integration] Author: Bob ([email protected]) ✅ Test would be executed === testWithException === Name: quickTest Description: A quick sanity check Timeout: 30s Enabled: true Priority: MEDIUM Expected Exception: IllegalArgumentException Tags: [] Author: Unknown ✅ Test would be executed === disabledTest === Name: disabledTest Description: Timeout: 30s Enabled: false Priority: MEDIUM Expected Exception: None Tags: [] Author: Unknown ❌ Test is disabled === defaultTest === Name: defaultTest Description: Timeout: 30s Enabled: true Priority: MEDIUM Expected Exception: None Tags: [] Author: Unknown ✅ Test would be executed

Repeatable Annotations

Java 8+ Repeatable Annotations:

import java.lang.annotation.*; import java.util.*; // Container annotation @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Roles { Role[] value(); } // Repeatable annotation @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Repeatable(Roles.class) @interface Role { String name(); String[] permissions() default {}; } // Pre-Java 8 way (without @Repeatable) @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface OldRoles { String[] value(); } // Using repeatable annotations (Java 8+) @Role(name = "ADMIN", permissions = {"READ", "WRITE", "DELETE"}) @Role(name = "USER", permissions = {"READ"}) @Role(name = "GUEST", permissions = {"READ_PUBLIC"}) class SecureService { public void adminOperation() { System.out.println("Admin operation"); } public void userOperation() { System.out.println("User operation"); } } // Old way (pre-Java 8) @OldRoles({"ADMIN", "USER", "GUEST"}) class OldSecureService { // ... } public class RepeatableExample { public static void main(String[] args) { Class<SecureService> serviceClass = SecureService.class; // Get roles using container annotation Roles rolesContainer = serviceClass.getAnnotation(Roles.class); if (rolesContainer != null) { System.out.println("=== Roles using Container ==="); for (Role role : rolesContainer.value()) { printRole(role); } } // Get roles directly (Java 8+) System.out.println("\n=== Roles Directly ==="); Role[] rolesDirect = serviceClass.getAnnotationsByType(Role.class); for (Role role : rolesDirect) { printRole(role); } // Demonstrate the difference System.out.println("\n=== Annotation Details ==="); Annotation[] annotations = serviceClass.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("Annotation: " + annotation.annotationType().getSimpleName()); if (annotation instanceof Roles) { System.out.println(" - This is the container annotation"); } else if (annotation instanceof Role) { System.out.println(" - This is a repeatable annotation"); } } } private static void printRole(Role role) { System.out.println("Role: " + role.name()); System.out.println(" Permissions: " + Arrays.toString(role.permissions())); } }

Output:

=== Roles using Container === Role: ADMIN Permissions: [READ, WRITE, DELETE] Role: USER Permissions: [READ] Role: GUEST Permissions: [READ_PUBLIC] === Roles Directly === Role: ADMIN Permissions: [READ, WRITE, DELETE] Role: USER Permissions: [READ] Role: GUEST Permissions: [READ_PUBLIC] === Annotation Details === Annotation: Roles - This is the container annotation

Annotation Processing

Runtime Annotation Processing:

import java.lang.annotation.*; import java.lang.reflect.*; import java.util.*; // Custom annotations for validation @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"; } // Validation processor class Validator { public static List<String> validate(Object obj) { List<String> errors = new ArrayList<>(); Class<?> clazz = obj.getClass(); for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); try { Object value = field.get(obj); // Check @NotNull if (field.isAnnotationPresent(NotNull.class)) { NotNull notNull = field.getAnnotation(NotNull.class); if (value == null) { errors.add(field.getName() + ": " + notNull.message()); } } // Check @Size if (field.isAnnotationPresent(Size.class) && value != null) { Size size = field.getAnnotation(Size.class); int length = value.toString().length(); if (length < size.min() || length > size.max()) { errors.add(field.getName() + ": " + size.message() + " (min: " + size.min() + ", max: " + size.max() + ", actual: " + length + ")"); } } // Check @Email if (field.isAnnotationPresent(Email.class) && value != null) { String email = value.toString(); if (!isValidEmail(email)) { Email emailAnnotation = field.getAnnotation(Email.class); errors.add(field.getName() + ": " + emailAnnotation.message() + " (" + email + ")"); } } } catch (IllegalAccessException e) { errors.add("Cannot access field: " + field.getName()); } } return errors; } private static boolean isValidEmail(String email) { return email != null && email.matches("^[A-Za-z0-9+_.-]+@(.+)$"); } } // Class to validate class User { @NotNull(message = "Username is required") @Size(min = 3, max = 20, message = "Username must be 3-20 characters") private String username; @NotNull @Email private String email; @Size(min = 8, message = "Password must be at least 8 characters") private String password; private String optionalField; public User(String username, String email, String password) { this.username = username; this.email = email; this.password = password; } // Getters and setters 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 String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } public class AnnotationProcessingExample { public static void main(String[] args) { // Test cases User[] testUsers = { new User("jo", "invalid-email", "short"), // Multiple errors new User(null, "[email protected]", "validpassword123"), // Null username new User("validuser", "invalid", "validpassword123"), // Invalid email new User("validuser", "[email protected]", "validpassword123") // Valid }; for (int i = 0; i < testUsers.length; i++) { System.out.println("\n=== Test User " + (i + 1) + " ==="); System.out.println("Username: " + testUsers[i].getUsername()); System.out.println("Email: " + testUsers[i].getEmail()); System.out.println("Password: " + testUsers[i].getPassword()); List<String> errors = Validator.validate(testUsers[i]); if (errors.isEmpty()) { System.out.println("✅ Validation passed!"); } else { System.out.println("❌ Validation errors:"); errors.forEach(error -> System.out.println(" - " + error)); } } // Demonstrate reflection with annotations System.out.println("\n=== Reflection Analysis ==="); Class<User> userClass = User.class; for (Field field : userClass.getDeclaredFields()) { System.out.println("\nField: " + field.getName()); System.out.println(" Type: " + field.getType().getSimpleName()); Annotation[] annotations = field.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(" Annotation: @" + annotation.annotationType().getSimpleName()); } } } }

Output:

=== Test User 1 === Username: jo Email: invalid-email Password: short ❌ Validation errors: - username: Username must be 3-20 characters (min: 3, max: 20, actual: 2) - email: Invalid email format (invalid-email) - password: Size constraint violated (min: 8, max: 2147483647, actual: 5) === Test User 2 === Username: null Email: [email protected] Password: validpassword123 ❌ Validation errors: - username: Username is required === Test User 3 === Username: validuser Email: invalid Password: validpassword123 ❌ Validation errors: - email: Invalid email format (invalid) === Test User 4 === Username: validuser Email: [email protected] Password: validpassword123 ✅ Validation passed! === Reflection Analysis === Field: username Type: String Annotation: @NotNull Annotation: @Size Field: email Type: String Annotation: @NotNull Annotation: @Email Field: password Type: String Annotation: @Size Field: optionalField Type: String

Best Practices

1. Use Meaningful Names:

// ✅ Good - clear and descriptive @interface CacheConfig { int duration(); TimeUnit unit(); boolean refreshable(); } // ❌ Avoid - vague names @interface Config { int value(); String data(); }

2. Provide Sensible Defaults:

// ✅ Good - sensible defaults @interface ApiEndpoint { String path(); String method() default "GET"; boolean secure() default true; int timeout() default 30; } // ❌ Avoid - no defaults when they make sense @interface BadEndpoint { String path(); String method(); // Should have default boolean secure(); // Should have default }

3. Use Enums for Fixed Values:

// ✅ Good - using enums enum HttpMethod { GET, POST, PUT, DELETE, PATCH } @interface RequestMapping { String path(); HttpMethod method() default HttpMethod.GET; } // ❌ Avoid - using strings for fixed values @interface BadMapping { String path(); String method(); // Could be any string }

4. Document Your Annotations:

/** * Marks a method as an API endpoint that can be called remotely * * @author Developer * @version 1.0 * @since 2024 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface ApiMethod { /** * The HTTP path for this endpoint * @return the endpoint path */ String value(); /** * HTTP method to use * @return the HTTP method */ HttpMethod method() default HttpMethod.GET; /** * Whether authentication is required * @return true if authentication is required */ boolean requiresAuth() default true; }

Complete Examples

Complete Framework Example:

```java
import java.lang.annotation.; import java.lang.reflect.;
import java.util.*;

// Framework annotations
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Service {
String name() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Command {
String value();
String description() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Inject {
String value() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Scheduled {
long fixedRate() default -1;
String cron() default "";
}

// Service classes
@Service(name = "userService")
class UserService {

@Command("createUser") public String createUser(String username, String email) { return "User created: " + username + " (" + email + ")"; } @Command("deleteUser") public String deleteUser(String username) { return "User deleted: " + username; } @Scheduled(fixedRate = 5000) public void cleanupInactiveUsers() { System.out.println("Cleaning up inactive users..."); }

}

@Service(name = "emailService")
class EmailService {

@Inject private UserService userService; @Command("sendEmail") public String sendEmail(String to, String subject) { return "Email sent to: " + to + " - " + subject; } @Scheduled(cron = "0 0 9 * * *") // Daily at 9 AM public void sendDailyReports() { System.out.println("Sending daily reports..."); }

}

// Simple DI Container and Framework
class ApplicationContext {
private Map services = new HashMap<>();
private Map commands = new HashMap<>();

public ApplicationContext(Class<?>... configClasses) { // Scan and instantiate services for (Class<?> configClass : configClasses) { scanPackage(configClass.getPackage()); } // Inject dependencies injectDependencies(); // Register commands registerCommands(); } private void scanPackage(Package pkg) { // In real implementation, you would scan the package // For this example, we'll manually register our services registerService(new UserService()); registerService(new EmailService()); } private void registerService(Object service) { Class<?> serviceClass = service.getClass(); Service serviceAnnotation = serviceClass.getAnnotation(Service.class); String serviceName = serviceAnnotation != null && !serviceAnnotation.name().isEmpty() ? serviceAnnotation.name() : serviceClass.getSimpleName(); services.put(serviceName, service); System.out.println("Registered service: " + serviceName); } private void injectDependencies() { for (Object service : services.values()) { for (Field field : service.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(Inject.class)) { Inject inject = field.getAnnotation(Inject.class); String dependencyName = inject.value().isEmpty() ? field.getType().getSimpleName() : inject.value(); Object dependency = services.get(dependencyName); if (dependency != null) { field.setAccessible(true); try { field.set(service, dependency); System.out.println("Injected " + dependencyName + " into " + service.getClass().getSimpleName()); } catch (IllegalAccessException e) { System.err.println("Failed to inject " + dependencyName + ": " + e.getMessage()); } } } } } } private void registerCommands() { for (Object service : services.values()) { for (Method method : service.getClass().getDeclaredMethods()) { Command command = method.getAnnotation(Command.class); if (command != null) { commands.put(command.value(), method); System.out.println("Registered command: " + command.value() + " - " + command.description()); } } } } public Object executeCommand(String commandName, Object... args) { Method method = commands.get(commandName); if (method == null) { throw new IllegalArgumentException("Unknown command: " + commandName); } try { Object service = services.values().stream() .

Leave a Reply

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


Macro Nepal Helper