spring如何实现重复注解和aop拦截

发布时间:2021-08-26 10:43:33 作者:小新
来源:亿速云 阅读:531
# Spring如何实现重复注解和AOP拦截

## 目录
1. [重复注解的背景与需求](#重复注解的背景与需求)
2. [Java 8的重复注解机制](#java-8的重复注解机制)
3. [Spring对重复注解的支持](#spring对重复注解的支持)
4. [AOP拦截重复注解的实现](#aop拦截重复注解的实现)
5. [完整代码示例](#完整代码示例)
6. [实际应用场景](#实际应用场景)
7. [总结](#总结)

---

## 重复注解的背景与需求

在Java 8之前,注解(Annotation)在同一个位置只能声明一次。这种限制在某些场景下会带来不便,例如:

```java
// Java 8前无法这样声明多个相同注解
@Permission(role="admin")
@Permission(role="manager")
public void sensitiveOperation() {}

Java 8通过重复注解机制解决了这个问题,允许在同一个元素上多次使用相同的注解。


Java 8的重复注解机制

Java 8通过两个关键要素实现重复注解:

  1. @Repeatable元注解:标记注解可重复
  2. 容器注解:用于存储重复注解的数组
// 1. 定义可重复注解
@Repeatable(Permissions.class) // 指定容器注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Permission {
    String role();
}

// 2. 定义容器注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Permissions {
    Permission[] value(); // 必须命名为value
}

使用示例:

@Permission(role="admin")
@Permission(role="auditor")
public void adminOperation() {}

Spring对重复注解的支持

Spring框架从4.0版本开始全面支持Java 8特性,包括重复注解。核心支持体现在:

  1. 注解扫描AnnotatedElementUtils工具类
  2. AOP支持:自动处理重复注解的拦截

获取重复注解的Spring方式:

Set<Permission> permissions = AnnotatedElementUtils
    .getMergedRepeatableAnnotations(
        method, Permission.class, Permissions.class);

与Java原生API的区别:

方式 特点
getAnnotationsByType() Java原生,只处理直接注解
getMergedRepeatableAnnotations() Spring增强,支持继承、接口等

AOP拦截重复注解的实现

1. 定义切面基础结构

@Aspect
@Component
public class PermissionAspect {
    
    @Before("@annotation(permission)")
    public void checkPermission(Permission permission) {
        // 单注解处理逻辑
    }
}

2. 处理重复注解的进阶方案

方案一:拦截容器注解

@Before("@annotation(permissions)")
public void checkPermissions(Permissions permissions) {
    for (Permission p : permissions.value()) {
        checkSinglePermission(p);
    }
}

方案二:使用Spring工具类增强

@Before("execution(* com.example..*(..))")
public void checkPermissions(JoinPoint jp) {
    Method method = ((MethodSignature) jp.getSignature()).getMethod();
    
    Set<Permission> permissions = AnnotatedElementUtils
        .getMergedRepeatableAnnotations(method, Permission.class, Permissions.class);
    
    permissions.forEach(this::checkSinglePermission);
}

3. 性能优化建议

  1. 使用@Around替代多次@Before减少代理调用
  2. 缓存注解解析结果(适用于无动态参数场景)
  3. 优先使用方法级拦截而非类级拦截

完整代码示例

1. 注解定义

// Permission.java
@Repeatable(Permissions.class)
@Retention(RUNTIME)
@Target(METHOD)
public @interface Permission {
    String value();
    String action() default "read";
}

// Permissions.java
@Retention(RUNTIME)
@Target(METHOD)
public @interface Permissions {
    Permission[] value();
}

2. 切面实现

@Aspect
@Component
public class SecurityAspect {
    private static final Logger log = LoggerFactory.getLogger(SecurityAspect.class);

    @Around("@within(org.springframework.stereotype.Controller) || " +
            "@within(org.springframework.web.bind.annotation.RestController)")
    public Object checkApiPermission(ProceedingJoinPoint pjp) throws Throwable {
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        
        // 获取所有Permission注解(包括重复注解)
        Set<Permission> permissions = AnnotatedElementUtils
            .getMergedRepeatableAnnotations(method, Permission.class, Permissions.class);
        
        if (!permissions.isEmpty()) {
            User user = CurrentUser.get();
            boolean hasPermission = permissions.stream()
                .anyMatch(p -> user.hasPermission(p.value(), p.action()));
            
            if (!hasPermission) {
                throw new AccessDeniedException("Permission denied");
            }
        }
        
        return pjp.proceed();
    }
}

3. 控制器使用示例

@RestController
@RequestMapping("/api")
public class AdminController {
    
    @Permission("user.create")
    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        // ...
    }
    
    @Permission("user.read")
    @Permission("audit.read") // 重复注解
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        // ...
    }
}

实际应用场景

  1. 权限控制:如示例中的RBAC模型
  2. 日志记录:多维度日志标记
    
    @LogOperation(type="login")
    @LogOperation(module="security")
    public void login() {...}
    
  3. API文档生成:Swagger等多标签支持
  4. 数据校验:组合校验规则
    
    @Validate(regex="\\d+")
    @Validate(minLength=6)
    private String password;
    

总结

关键技术点: 1. Java 8的@Repeatable+容器注解机制 2. Spring的AnnotatedElementUtils工具类 3. AOP中通过容器注解或工具类处理

最佳实践: ✔️ 优先使用Spring工具类处理复杂场景
✔️ 考虑注解继承关系(如接口上的注解)
✔️ 对性能敏感场景添加适当缓存

扩展思考: - 动态注解的可行性(通过APT或运行时字节码增强) - 与其他Spring特性(如SpEL)的整合 - 在响应式编程中的适配问题

通过合理运用重复注解和AOP,可以实现高度可扩展的声明式编程模型,显著提升代码的可读性和维护性。 “`

(注:实际MD文档显示的字数统计可能因渲染环境不同略有差异,本文档核心内容约3100字)

推荐阅读:
  1. 如何实现spring@aspect注解aop
  2. 使用Spring AOP怎么实现统一注解功能

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

spring aop

上一篇:Python如何做一个名片管理系统

下一篇:Python数据分析之Pandas知识点有哪些

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》