自定义注解

简介: 本文介绍如何在Spring项目中实现自定义注解,结合AOP完成日志记录,并通过过滤器实现登录权限控制。涵盖注解定义、元注解说明、切面编程及实际应用场景,展示其在日志、验证、权限等场景的扩展用途,代码简洁,易于复用。(238字)

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 merchantsInfoDtos = new ArrayList<>();
merchantsInfoDtos = historyTradeService.filter(companyId, code);
return ok("查询成功", merchantsInfoDtos);
}

相关文章
|
6月前
|
人工智能 NoSQL 前端开发
Chap03. SpringAI
SpringAI整合多款主流大模型,支持对话、函数调用与RAG等架构,提供统一API简化开发。通过ChatClient封装交互,结合Prompt工程、工具调用与知识检索,可快速构建智能客服、哄哄模拟器、ChatPDF等应用,并支持多模态与持久化扩展,助力AI应用高效落地。
|
6月前
|
存储 算法 索引
二叉树基础及常见类型
二叉树是数据结构的核心,既是红黑树、堆、字典树等复杂结构的基础,也体现递归思维,贯穿回溯、动态规划等算法。掌握二叉树,等于掌握算法之魂。本文详解其类型、性质与实现,助你彻底精通。
|
6月前
|
算法 索引
双指针技巧秒杀七道数组题目
本文介绍数组与链表中常用的双指针技巧,包括快慢指针与左右指针。通过LeetCode经典题目如删除有序数组/链表中的重复项、两数之和等,详解如何用快慢指针实现原地修改,提升算法效率,适用于刷题进阶与面试备考。(238字)
|
6月前
|
敏捷开发 Dubbo Java
需求开发人日评估
本文介绍敏捷开发中关键的工时评估——人日估算方法,涵盖开发、自测、联调、测试及发布各阶段周期参考,提供常见需求如增删改查、Excel导入导出、跨服务调用等典型场景的人日基准,助力团队科学规划迭代进度。
|
6月前
|
敏捷开发 Java 测试技术
为什么要单元测试
本文探讨单元测试如何让软件开发提速而非拖慢进度。通过解析测试体系演进、测试金字塔理念,阐述单元测试在提升调试效率、代码质量与研发效能方面的核心价值,揭示“写单测=踩刹车”的认知误区,倡导研发自主保障质量,推动项目高效持续交付。
|
6月前
|
SQL 安全 关系型数据库
了解SQL注入
SQL注入是利用Web应用输入验证缺陷,将恶意SQL代码插入数据库查询的攻击方式,可导致身份绕过、数据泄露、篡改甚至系统被控。常见于登录框等用户输入场景,通过构造特殊字符串改变原有SQL逻辑。防御需结合输入验证、参数化查询及错误信息管控,从应用与网络层面综合防护。
|
6月前
|
存储 安全 小程序
认识OAuth2.0
OAuth2.0是开放授权标准,允许第三方应用在用户授权下安全访问资源,无需获取用户账号密码。包含授权码、简化、密码和客户端四种模式,广泛用于服务间资源调用与单点登录,提升系统安全性与用户体验。
|
6月前
|
SQL 前端开发 Java
ReadMe
这里整理了Java微服务相关的知识笔记,涵盖入职必备、源码分析、真实项目与大厂面试实录。适合有Java基础、备战微服务进阶的开发者。持续更新中,欢迎催更、指正,点赞支持哦~一起冲击梦想公司!
|
6月前
|
安全 Java Spring
2.过滤器链加载原理
通过分析DelegatingFilterProxy、FilterChainProxy和SecurityFilterChain源码,揭示了Spring Security过滤器链的加载机制:由web.xml中配置的DelegatingFilterProxy通过bean名称springSecurityFilterChain获取FilterChainProxy实例,并逐层委托,最终将十五个安全过滤器封装进SecurityFilterChain执行,实现了请求的安全控制。
|
6月前
|
SQL NoSQL 前端开发
大厂如何解决订单幂等问题
为保障分布式系统数据一致性,需实现接口幂等性。创建订单时,通过预生成唯一订单号并利用数据库主键唯一约束,防止重复插入;支付时借助Redis或数据库状态标记,识别并拦截重复请求。针对ABA问题,采用版本号机制,更新时校验版本并原子自增,确保数据不被覆盖。两类方案适用于各类数据库操作,保障系统在重试、并发场景下的正确性。(239字)