自定义注解

简介: 本文介绍如何在Spring框架中实现自定义注解,结合AOP与过滤器应用于日志记录、权限控制等场景。通过定义注解、使用@Target和@Retention等元注解,并配合AOP切面或拦截器,实现对方法的增强处理,提升代码可读性与复用性。

1.前言

自定义注解目前在我使用过的项目中,主要用用作日志丰富,参数处理,其核心还是借助于Spring的AOP进行实现,本文将结合具体代码演示简单的自定义注解实现流程。

2.实现

2.1 定义User

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
}

2.2 定义UserDAO

@Component
public class UserDao {
    public User findUserById(Integer id) {
        if(id > 10) {
            return null;
        }
        return new User(id, "user-" + id);
    }
}

2.3 定义UserService

@Service
public class UserService {
    private final UserDao userDao;
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    public User findUserById(Integer id) {
        return userDao.findUserById(id);
    }
}

2.4 定义Controller

@RequestMapping(value = "user/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public User findUser(@PathVariable("id") Integer id) {
    return userService.findUserById(id);
}

此时浏览器访问:http://{domain}/user/1即可出现对应效果

{
    "id": 1,
    "name": "user-1"
}

2.5 定义自定义注解

import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CustomAnnotation {
    String name() default "";
    String value() default "";
}

说明:

  • @interface 不是interface,是注解类  定义注解
  • Documented
  • 这个Annotation可以被写入javadoc  
  • @Retention
  • 修饰注解,是注解的注解,称为元注解
  • SOURCE,     // 编译器处理完Annotation后不存储在class中  
  • CLASS,       // 编译器把Annotation存储在class中,这是默认值  
  • RUNTIME  // 编译器把Annotation存储在class中,可以由虚拟机读取,反射需要
  • @Target
  • 注解的作用目标
  • @Target(ElementType.TYPE)                                           //接口、类、枚举、注解
  • @Target(ElementType.FIELD)                                         //字段、枚举的常量
  • @Target(ElementType.METHOD)                                   //方法
  • @Target(ElementType.PARAMETER)                              //方法参数
  • @Target(ElementType.CONSTRUCTOR)                        //构造函数
  • @Target(ElementType.LOCAL_VARIABLE)                     //局部变量
  • @Target(ElementType.ANNOTATION_TYPE)                //注解
  • @Target(ElementType.PACKAGE)                                 //包    


  • 可以定义多个方法,每个方法在使用时参照下面的Controller使用即可,实际就是类似于@PostMapping这样的注解中使用过的value,method,produces等,如下:

2.6 AOP+Controller使用自定义注解

@CustomAnnotation(name = "findUser", value = "根据ID查找用户")
@RequestMapping(value = "user/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public User findUser(@PathVariable("id") Integer id) {
    return userService.findUserById(id);
}
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class TestLogAspect {
    @Pointcut("@annotation(cn.test.CustomAnnotation)")
    private void pointcut() {}
    @Before("pointcut() && @annotation(annotation)")
    public void advice(JoinPoint joinPoint, CustomAnnotation annotation) {
        System.out.println(
            "类名:["
            + joinPoint.getSignature().getDeclaringType().getSimpleName()
            + "],方法名:[" + joinPoint.getSignature().getName()
            + "]-日志内容-[" + annotation.value() + ", "+annotation.name()+ "]");
    }
}

3.总结

自定义注解其核心是借助于:@Target 和 @Rentention,@Documented组合实现,其实现还是需要依赖于Spring的AOP进行具体体现,除了上面的用作日志拦截,还可以自定义:数据验证注解,权限注解,缓存注解等多种用途,但其实现基本都遵循上述步骤。

4.自定义注解+过滤器实现登陆相关

4.1 定义自定义注解@Login

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.zhicall.majordomo.core.common.enums.YesOrNo;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Login {
  YesOrNo value();
}

4.2 过滤器匹配

package com.zhicall.majordomo.core.security.interceptor;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.alibaba.fastjson.JSON;
import com.zhicall.care.realtime.util.ResultMessageBuilder;
import com.zhicall.care.realtime.util.ResultMessageBuilder.ResultMessage;
import com.zhicall.care.system.basic.BeanFactory;
import com.zhicall.majordomo.core.common.constant.GlobalCst;
import com.zhicall.majordomo.core.common.enums.YesOrNo;
import com.zhicall.majordomo.core.security.annotation.Login;
import com.zhicall.majordomo.core.security.constant.Cst;
import com.zhicall.majordomo.core.security.util.UserAuthHelper;
public class UserLoginInterceptor extends HandlerInterceptorAdapter {
  @SuppressWarnings({ "unchecked", "rawtypes" })
  protected RedisTemplate<String, String> redisTemplate = (RedisTemplate) BeanFactory.getInstance().getBean("redisTemplate");
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    HandlerMethod handlerMethod = (HandlerMethod) handler;
    Login login = handlerMethod.getMethodAnnotation(Login.class);
    // 方法被 @Login(YesOrNo.No)标记 表示不需要登陆即可访问 否者都要登录
    if (login != null && YesOrNo.NO.equals(login.value())) {
      return true;
    }
    // 做鉴权
        ......
  }
}

4.3 Controller中具体使用

@Login(YesOrNo.NO)
@RequestMapping(value = "/filter", method = RequestMethod.POST)
public @ResponseBody ResultMessageBuilder.ResultMessage filter(String companyId, String code) {
    List<TabInfoVo> merchantsInfoDtos = new ArrayList<>();
    merchantsInfoDtos = historyTradeService.filter(companyId, code);
    return ok("查询成功", merchantsInfoDtos);
}
目录
相关文章
|
4月前
|
人工智能 缓存 运维
【本不该故障系列】从 runC 到 runD:SAE 如何化解安全泄露风险
阿里云SAE默认采用runD安全容器,通过轻量虚拟化实现硬件级隔离,彻底解决runC共享内核导致的逃逸、噪声邻居、侧信道攻击等多租户安全风险。
|
2月前
|
存储 人工智能 开发工具
Claude Code自动记忆来了!配合老金三层记忆系统全开源!加强Plus!
昨天晚上,老金我照例打开 Claude Code 准备写代码。 随便聊了几句项目架构,Claude突然冒出一句: "Based on our previous discussions, this project uses pnpm and TypeScript strict mode." 老金我愣了一下。 上次提到pnpm是三天前的事了,这中间重启了好几次。 打开 ~/.claude/p
|
4月前
|
SQL Java 数据库连接
MyBatis-Flex 实战:极简 CRUD + 高性能分页,吊打传统 MyBatis 的新一代持久层框架
MyBatis-Flex作为新一代Java持久层框架,在MyBatis的灵活性和MyBatis-Plus的便捷性之间实现了完美平衡。本文详细介绍了MyBatis-Flex的环境搭建、核心特性和实战应用,包括:1. 5分钟快速初始化项目配置;2. 通过注解实现零XML的CRUD操作;3. 灵活的QueryWrapper动态SQL构建;4. 高效分页查询实现;5. 关联查询解决方案;6. 编程式事务管理。相比MyBatis-Plus,MyBatis-Flex具有更简洁的API、更高的性能和更低的学习成本。
1066 1
|
11月前
|
Arthas 监控 Java
Arthas thread(查看当前JVM的线程堆栈信息)
Arthas thread(查看当前JVM的线程堆栈信息)
1825 10
MyBatis-Plus-Join关联查询
MyBatis-Plus-Join关联查询
1015 2
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
10576 114
|
人工智能 自然语言处理 安全
【claude官网入口】体验claude 3.5 Sonnet 的强大交互能力
Claude 是由 Anthropic 公司精心打造的人工智能系统,以其卓越的自然语言处理能力、深刻的上下文理解和优越的安全性而闻名于世
开发指南040-swagger加header
swagger可以在线生成接口文档,便于前后端沟通,而且还可以在线调用接口,方便后台调试
|
消息中间件 中间件 Kafka
MQ四兄弟:如何实现延时消息
本文介绍了几种常见的消息队列系统(RabbitMQ、RocketMQ、Kafka和Pulsar)实现延时消息的方式。RabbitMQ通过死信队列或延时插件实现;RocketMQ内置延时消息支持,可通过设置`delayTimeLevel`属性实现;Kafka不直接支持延时消息,但可以通过时间戳、延时Topic、Kafka Streams等方法间接实现;Pulsar自带延时消息功能,提供`deliverAfter`和`deliverAt`两种方式。每种方案各有优劣,适用于不同的应用场景。
1531 0
|
前端开发 JavaScript Java
idea中如何不重启tomcat 即可看到修改内容变化
这篇文章介绍了在IntelliJ IDEA中实现对Java Web应用的热部署(Hot Swap)的多种方法,包括启用自动构建、使用Exploded Artifact、安装JRebel插件、更改静态资源、使用Spring Boot的devtools以及IDEA的HotSwap功能,以便在不重启Tomcat服务器的情况下看到修改内容的变化。
idea中如何不重启tomcat 即可看到修改内容变化

热门文章

最新文章