spring的自定义注解

在 Spring 中,自定义注解可以帮助我们实现自定义的功能,比如切面逻辑、权限控制、数据校验等。自定义注解通常结合 Spring 的 AOP 或其他功能使用,以增强业务逻辑。下面是创建自定义注解的一般步骤,以及使用示例。


一、创建自定义注解

1.1 定义自定义注解

创建一个自定义注解需要使用 @interface 关键字,同时可以添加一些元注解来控制注解的行为:

  • @Target:指定注解可以应用的位置(类、方法、字段等)。
  • @Retention:指定注解的生命周期。
  • @Documented:将注解包含在 Javadoc 中。
  • @Inherited:允许子类继承父类的注解。
示例:定义一个自定义注解 @LogExecutionTime
java 复制代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD) // 只能作用于方法
@Retention(RetentionPolicy.RUNTIME) // 在运行时可见
public @interface LogExecutionTime {
}

二、使用 AOP 处理自定义注解

可以通过 AOP 来处理带有 @LogExecutionTime 注解的方法,记录方法执行时间。

2.1 创建切面类

在切面类中,通过 @Around 注解拦截标注了 @LogExecutionTime 的方法,计算并打印执行时间。

java 复制代码
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    @Around("@annotation(LogExecutionTime)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        // 执行目标方法
        Object proceed = joinPoint.proceed();
        
        long executionTime = System.currentTimeMillis() - start;
        System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
        
        return proceed;
    }
}
2.2 在方法上使用注解

可以将 @LogExecutionTime 注解添加到任何方法上,Spring AOP 会自动拦截并记录执行时间。

java 复制代码
import org.springframework.stereotype.Service;

@Service
public class MyService {

    @LogExecutionTime
    public void serve() {
        // 模拟耗时操作
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("Service executed");
    }
}

三、实现基于自定义注解的权限控制示例

下面示例通过自定义注解实现简单的权限控制,模拟权限验证功能。

3.1 定义权限控制注解

创建 @RoleRequired 注解,指定需要的角色。

java 复制代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RoleRequired {
    String value(); // 指定需要的角色
}
3.2 创建权限控制的切面类

在切面类中,通过拦截 @RoleRequired 注解的方法,实现权限验证的逻辑。

java 复制代码
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class RoleAspect {

    // 假设当前用户角色(在实际中,这个信息可能来自上下文或会话)
    private static final String currentUserRole = "USER";

    @Before("@annotation(roleRequired)")
    public void checkRole(JoinPoint joinPoint, RoleRequired roleRequired) {
        String requiredRole = roleRequired.value();
        
        // 检查当前用户的角色
        if (!currentUserRole.equals(requiredRole)) {
            throw new SecurityException("Access Denied: insufficient permissions");
        }
        System.out.println("Access Granted: " + joinPoint.getSignature());
    }
}
3.3 使用权限控制注解

@RoleRequired 注解添加到需要权限控制的方法上。

java 复制代码
import org.springframework.stereotype.Service;

@Service
public class AdminService {

    @RoleRequired("ADMIN")
    public void performAdminTask() {
        System.out.println("Admin task performed");
    }
}

在运行时,如果当前用户的角色不匹配 @RoleRequired 指定的角色,将抛出异常 Access Denied: insufficient permissions


四、使用自定义注解进行参数校验

自定义注解还可以用于参数校验,结合 Spring 的 @Validated 注解,利用 AOP 实现校验逻辑。

4.1 定义注解 @NotEmpty

创建一个 @NotEmpty 注解,用于校验字段不为空。

java 复制代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotEmpty {
    String message() default "Field cannot be empty";
}
4.2 创建校验器

编写 AOP 切面类,检测带有 @NotEmpty 注解的字段是否为空。

java 复制代码
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;

@Aspect
@Component
public class ValidationAspect {

    @Before("execution(* com.example..*.*(..))")
    public void validateNotEmptyFields(JoinPoint joinPoint) throws IllegalAccessException {
        Object[] args = joinPoint.getArgs();
        
        for (Object arg : args) {
            if (arg != null) {
                Field[] fields = arg.getClass().getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(NotEmpty.class)) {
                        field.setAccessible(true);
                        Object value = field.get(arg);
                        if (value == null || (value instanceof String && ((String) value).trim().isEmpty())) {
                            NotEmpty notEmpty = field.getAnnotation(NotEmpty.class);
                            throw new IllegalArgumentException(notEmpty.message());
                        }
                    }
                }
            }
        }
    }
}
4.3 使用校验注解

在实体类中使用 @NotEmpty 注解标记需要校验的字段。

java 复制代码
public class User {

    @NotEmpty(message = "Username must not be empty")
    private String username;

    @NotEmpty(message = "Password must not be empty")
    private String password;

    // Constructors, getters, and setters
}
4.4 在服务中进行校验

调用带有 @NotEmpty 注解的对象时,如果字段为空,会触发校验异常。

java 复制代码
import org.springframework.stereotype.Service;

@Service
public class UserService {

    public void registerUser(User user) {
        System.out.println("User registered: " + user.getUsername());
    }
}

总结

自定义注解结合 AOP 可以极大地简化代码,并添加业务逻辑。以上示例展示了几种常见的自定义注解应用场景:

  • 方法执行时间记录@LogExecutionTime
  • 权限控制@RoleRequired
  • 字段校验@NotEmpty

自定义注解使代码更加简洁、可读、可复用。根据业务需求,可以灵活实现多种注解应用。

相关推荐
冰帝海岸12 分钟前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象38 分钟前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了1 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·1 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic2 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王2 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康2 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神2 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
qq_327342733 小时前
Java实现离线身份证号码OCR识别
java·开发语言
阿龟在奔跑4 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list