自定义注解

简介: 本文介绍如何在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);
}
相关文章
|
2月前
|
人工智能 缓存 运维
【本不该故障系列】从 runC 到 runD:SAE 如何化解安全泄露风险
阿里云SAE默认采用runD安全容器,通过轻量虚拟化实现硬件级隔离,彻底解决runC共享内核导致的逃逸、噪声邻居、侧信道攻击等多租户安全风险。
|
2月前
|
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、更高的性能和更低的学习成本。
485 1
|
2月前
|
存储 安全 Java
6.鉴权
本文介绍基于Spring Security与JWT的客户端Token认证方案,涵盖实现思路、核心代码及完整流程。通过自定义过滤器与认证逻辑,结合RBAC权限模型,实现安全的Token生成、校验与访问控制,保护Spring Boot应用接口。
 6.鉴权
|
2月前
|
安全 Java 开发工具
1.工程搭建与验证
本文介绍如何基于阿里云脚手架快速搭建SpringBoot工程,并整合SpringSecurity实现基础安全控制。涵盖项目创建、代码导入、Web依赖配置及登录验证流程,附完整代码仓库地址。
|
2月前
|
安全 数据安全/隐私保护
1.什么是权限管理
权限管理包含认证与授权两大核心:认证确认用户身份(如登录),授权则分配资源访问权限。通过角色叠加实现菜单动态展示,保障系统安全,避免操作越权或数据泄露。
|
2月前
|
JSON 数据格式
4. 不定参数入参
本文介绍了接口参数的两种处理方式:非JSON格式与JSON格式入参。前者支持基础类型、对象等直接传参,后者通过@RequestBody接收JSON数据,可封装为对象并结合@Valid进行校验,提升代码规范性与可维护性。
|
2月前
|
存储 监控 Java
2. 整合切面,参数拦截+过滤
该Java类基于Spring AOP实现请求参数的前置拦截与日志记录,自动捕获Controller层请求的URL、方式、参数及响应方法,并记录执行耗时,便于调试与监控,支持后续扩展如数据脱敏或存储。
|
9月前
|
Arthas 监控 Java
Arthas thread(查看当前JVM的线程堆栈信息)
Arthas thread(查看当前JVM的线程堆栈信息)
1624 10
MyBatis-Plus-Join关联查询
MyBatis-Plus-Join关联查询
825 2
开发指南040-swagger加header
swagger可以在线生成接口文档,便于前后端沟通,而且还可以在线调用接口,方便后台调试

热门文章

最新文章