自定义注解

在Java中,自定义和使用注解可以分为以下几个步骤:

定义注解:使用@interface关键字定义注解。
注解的元素:定义注解中的属性(可带默认值)。
指定目标和生命周期:通过元注解如@Target和@Retention指定注解的适用范围和生命周期。
使用注解:在类、方法或字段等位置使用自定义注解。
处理注解:通过反射机制读取并处理注解。
以下是一个完整的示例,演示如何自定义和使用注解:

示例代码

import java.lang.annotation.*;
import java.lang.reflect.Method;

// 1. 定义注解
@Retention(RetentionPolicy.RUNTIME) // 指定注解的生命周期为运行时
@Target(ElementType.METHOD)         // 指定注解的目标为方法
public @interface MyAnnotation {
    String value(); // 注解的一个属性,默认为无值
    int count() default 1; // 注解的另一个属性,带有默认值
}

// 2. 使用注解
class MyService {
    @MyAnnotation(value = "Hello", count = 3)
    public void sayHello() {
        System.out.println("Hello, world!");
    }

    @MyAnnotation(value = "Goodbye")
    public void sayGoodbye() {
        System.out.println("Goodbye, world!");
    }
}

// 3. 处理注解
public class AnnotationExample {
    public static void main(String[] args) {
        Class<MyService> clazz = MyService.class;
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(MyAnnotation.class)) {
                MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
                System.out.println("Method: " + method.getName());
                System.out.println("Value: " + annotation.value());
                System.out.println("Count: " + annotation.count());
                // 调用被注解的方法
                try {
                    for (int i = 0; i < annotation.count(); i++) {
                        method.invoke(clazz.getDeclaredConstructor().newInstance());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

输出示例
运行上述程序后,将输出如下内容:

Method: sayHello
Value: Hello
Count: 3
Hello, world!
Hello, world!
Hello, world!
Method: sayGoodbye
Value: Goodbye
Count: 1
Goodbye, world!

说明
@Retention(RetentionPolicy.RUNTIME):使注解在运行时可用,便于通过反射机制访问。
@Target(ElementType.METHOD):限制注解只能用于方法。
注解处理部分:通过Method.isAnnotationPresent()检查方法是否使用了指定注解,并通过Method.getAnnotation()获取注解实例,从而访问其属性。
动态调用:通过反射机制调用被注解的方法。
这种方式可以扩展为复杂场景,例如验证输入参数、生成文档或实现自定义逻辑。

参数验证注解
创建注解,用于约束方法参数的值范围。

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME) // 在运行时可用
@Target(ElementType.PARAMETER)      // 作用于方法参数
public @interface Range {
    int min(); // 最小值
    int max(); // 最大值
}

定义一个类,其中的方法参数使用@Range注解。

public class Calculator {

    public int add(
        @Range(min = 0, max = 100) int a, 
        @Range(min = 0, max = 100) int b
    ) {
        return a + b;
    }

    public int subtract(
        @Range(min = 0, max = 100) int a, 
        @Range(min = 0, max = 100) int b
    ) {
        return a - b;
    }
}

通过反射机制检查注解并验证方法参数。

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class AnnotationProcessor {

    public static void process(Object obj, String methodName, Object... args) throws Exception {
        Class<?> clazz = obj.getClass();
        Method method = null;

        // 查找目标方法
        for (Method m : clazz.getDeclaredMethods()) {
            if (m.getName().equals(methodName) && m.getParameterCount() == args.length) {
                method = m;
                break;
            }
        }

        if (method == null) {
            throw new NoSuchMethodException("Method " + methodName + " not found.");
        }

        // 验证参数
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Annotation[] annotations = parameter.getAnnotations();

            for (Annotation annotation : annotations) {
                if (annotation instanceof Range) {
                    Range range = (Range) annotation;
                    int value = (int) args[i];
                    if (value < range.min() || value > range.max()) {
                        throw new IllegalArgumentException(
                            String.format("Parameter %d is out of range (%d - %d). Provided: %d", 
                                          i + 1, range.min(), range.max(), value));
                    }
                }
            }
        }

        // 调用方法
        Object result = method.invoke(obj, args);
        System.out.println("Result: " + result);
    }
}

通过注解处理器调用并验证方法。

public class Main {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        try {
            // 调用有效参数
            AnnotationProcessor.process(calculator, "add", 50, 30);

            // 调用无效参数
            AnnotationProcessor.process(calculator, "subtract", 120, 30);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行程序时,输出如下:

Result: 80
java.lang.IllegalArgumentException: Parameter 1 is out of range (0 - 100). Provided: 120

通过反射解析类和方法注解,生成Markdown文档。

import java.io.FileWriter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class DocumentationGenerator {

    public static void generateMarkdown(Class<?> clazz, String outputFile) throws IOException {
        StringBuilder markdown = new StringBuilder();
        markdown.append("# API Documentation\n\n");
        markdown.append("## Class: ").append(clazz.getSimpleName()).append("\n\n");

        for (Method method : clazz.getDeclaredMethods()) {
            markdown.append("### Method: ").append(method.getName()).append("\n");
            markdown.append("- **Return Type**: ").append(method.getReturnType().getSimpleName()).append("\n");
            markdown.append("- **Parameters**:\n");

            Parameter[] parameters = method.getParameters();
            for (Parameter parameter : parameters) {
                markdown.append("  - **").append(parameter.getName()).append("**: ")
                        .append(parameter.getType().getSimpleName());

                // 提取注解信息
                for (Annotation annotation : parameter.getAnnotations()) {
                    if (annotation instanceof Range) {
                        Range range = (Range) annotation;
                        markdown.append(" (Range: ").append(range.min())
                                .append(" - ").append(range.max()).append(")");
                    }
                }
                markdown.append("\n");
            }
            markdown.append("\n");
        }

        // 写入文件
        try (FileWriter writer = new FileWriter(outputFile)) {
            writer.write(markdown.toString());
        }
    }

    public static void main(String[] args) {
        try {
            generateMarkdown(Calculator.class, "API_Documentation.md");
            System.out.println("Documentation generated successfully.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

基于正则表达式的自定义验证逻辑
创建一个支持正则验证的注解@PatternCheck:

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface PatternCheck {
    String pattern();       // 正则表达式
    String message() default "Invalid input format."; // 验证失败提示信息
}

定义一个方法,参数需要满足正则验证:

public class UserService {

    public void register(
        @PatternCheck(pattern = "^[a-zA-Z0-9]{6,12}$", message = "Username must be 6-12 alphanumeric characters.") String username,
        @PatternCheck(pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}$", message = "Invalid email format.") String email
    ) {
        System.out.println("User registered successfully: " + username + ", " + email);
    }
}

通过反射机制处理注解逻辑,动态验证输入参数是否符合规则。

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class CustomLogicProcessor {

    public static void process(Object obj, String methodName, Object... args) throws Exception {
        Class<?> clazz = obj.getClass();
        Method method = null;

        // 查找目标方法
        for (Method m : clazz.getDeclaredMethods()) {
            if (m.getName().equals(methodName) && m.getParameterCount() == args.length) {
                method = m;
                break;
            }
        }

        if (method == null) {
            throw new NoSuchMethodException("Method " + methodName + " not found.");
        }

        // 验证参数
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Annotation[] annotations = parameter.getAnnotations();

            for (Annotation annotation : annotations) {
                if (annotation instanceof PatternCheck) {
                    PatternCheck patternCheck = (PatternCheck) annotation;
                    String value = args[i].toString();
                    if (!value.matches(patternCheck.pattern())) {
                        throw new IllegalArgumentException(
                            String.format("Parameter %d validation failed: %s", i + 1, patternCheck.message()));
                    }
                }
            }
        }

        // 调用方法
        method.invoke(obj, args);
    }
}

通过CustomLogicProcessor调用并验证输入参数。

public class Main {
    public static void main(String[] args) {
        UserService userService = new UserService();

        try {
            // 验证通过
            CustomLogicProcessor.process(userService, "register", "User123", "[email protected]");

            // 验证失败(用户名格式错误)
            CustomLogicProcessor.process(userService, "register", "U", "[email protected]");

            // 验证失败(邮箱格式错误)
            CustomLogicProcessor.process(userService, "register", "User123", "invalid-email");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

AOP整合:将注解处理逻辑交给Spring AOP自动拦截,无需显式调用CustomLogicProcessor。
使用@PatternCheck作为输入验证注解。

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface PatternCheck {
    String pattern();       // 正则表达式
    String message() default "Invalid input format."; // 验证失败提示信息
}

定义业务逻辑类

import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void register(String username, String email) {
        System.out.println("User registered successfully: " + username + ", " + email);
    }
}

切面拦截方法参数,动态处理注解。

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@Aspect
@Component
public class ValidationAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void validateParameters(JoinPoint joinPoint) throws Exception {
        Method method = getMethodFromJoinPoint(joinPoint);
        Object[] args = joinPoint.getArgs();
        Parameter[] parameters = method.getParameters();

        for (int i = 0; i < parameters.length; i++) {
            Annotation[] annotations = parameters[i].getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation instanceof PatternCheck) {
                    PatternCheck patternCheck = (PatternCheck) annotation;
                    String value = args[i].toString();
                    if (!value.matches(patternCheck.pattern())) {
                        throw new IllegalArgumentException(
                            String.format("Parameter %d validation failed: %s", i + 1, patternCheck.message()));
                    }
                }
            }
        }
    }

    // 从 JoinPoint 中提取目标方法
    private Method getMethodFromJoinPoint(JoinPoint joinPoint) throws NoSuchMethodException {
        String methodName = joinPoint.getSignature().getName();
        Class<?>[] parameterTypes = ((Method) joinPoint.getSignature()).getParameterTypes();
        return joinPoint.getTarget().getClass().getMethod(methodName, parameterTypes);
    }
}

在 Spring Boot 中启用 AOP:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy
public class AopValidationApplication {
    public static void main(String[] args) {
        SpringApplication.run(AopValidationApplication.class, args);
    }
}

编写测试代码,调用UserService.register方法。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class ApplicationRunner implements CommandLineRunner {

    @Autowired
    private UserService userService;

    @Override
    public void run(String... args) throws Exception {
        try {
            // 验证通过
            userService.register("User123", "[email protected]");

            // 验证失败:用户名格式错误
            userService.register("U", "[email protected]");

            // 验证失败:邮箱格式错误
            userService.register("User123", "invalid-email");
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }
}

示例输出

User registered successfully: User123, [email protected]
Parameter 1 validation failed: Username must be 6-12 alphanumeric characters.
Parameter 2 validation failed: Invalid input format.

优势
分离关注点:将验证逻辑与业务逻辑完全分离,业务代码更加简洁。
全局处理:切面可以应用到所有带注解的方法,无需单独调用验证逻辑。
可扩展性:支持更多自定义注解,如@NotNull、@MinLength、@MaxLength,统一处理。
动态注解:根据方法或参数注解动态实现不同的逻辑。

你可能感兴趣的:(java,开发语言)