自定义注解

简介: 本文介绍如何在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);
}
目录
相关文章
|
8月前
|
人工智能 缓存 运维
【本不该故障系列】从 runC 到 runD:SAE 如何化解安全泄露风险
阿里云SAE默认采用runD安全容器,通过轻量虚拟化实现硬件级隔离,彻底解决runC共享内核导致的逃逸、噪声邻居、侧信道攻击等多租户安全风险。
|
8月前
|
开发者
业务架构图
本文介绍了业务架构图的核心概念与绘制方法,涵盖业务定义、架构域分类,强调业务架构是技术、应用与数据架构的基础。通过分层、分模块、分功能三步法,梳理业务逻辑,明确模块边界与信息流,提升客户理解与开发效率。
186 0
 业务架构图
|
8月前
|
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、更高的性能和更低的学习成本。
1791 1
|
8月前
|
NoSQL Linux Shell
2-MongoDB单机部署
本文介绍了MongoDB在Windows和Linux系统下的安装、配置与启动方法,包括下载地址、版本选择、解压安装、命令行及配置文件启动方式,并详细说明了如何通过Shell和图形化工具Compass连接数据库。同时提供常见问题解决方案及附件中的各环境安装包链接,便于快速部署使用。
179 0
MyBatis-Plus-Join关联查询
MyBatis-Plus-Join关联查询
1460 2
|
Arthas 监控 Java
Arthas thread(查看当前JVM的线程堆栈信息)
Arthas thread(查看当前JVM的线程堆栈信息)
2231 10
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
11693 138
|
存储 对象存储
【阿里云OSS】You have no right to access this object because of bucket acl.
【阿里云OSS】You have no right to access this object because of bucket acl.
22049 1
【阿里云OSS】You have no right to access this object because of bucket acl.
|
人工智能 自然语言处理 安全
【claude官网入口】体验claude 3.5 Sonnet 的强大交互能力
Claude 是由 Anthropic 公司精心打造的人工智能系统,以其卓越的自然语言处理能力、深刻的上下文理解和优越的安全性而闻名于世
开发指南040-swagger加header
swagger可以在线生成接口文档,便于前后端沟通,而且还可以在线调用接口,方便后台调试

热门文章

最新文章