自定义注解

简介: 本文介绍如何在Spring项目中实现自定义注解,结合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 merchantsInfoDtos = new ArrayList<>();
merchantsInfoDtos = historyTradeService.filter(companyId, code);
return ok("查询成功", merchantsInfoDtos);
}

相关文章
|
4天前
|
缓存 Java 数据库连接
1.常见配置
MyBatis配置优先级:方法参数 &gt; resource/url &gt; properties体内。核心属性包括缓存、延迟加载、主键生成等。支持多环境配置,通过environments指定,默认使用default环境。事务管理支持JDBC和MANAGED,与Spring集成时由Spring接管。详情参考官方Java API文档。
|
4天前
|
Java 数据安全/隐私保护 开发者
常见加载顺序
本文通过代码示例讲解Java中各类代码块的执行顺序:静态代码块随类加载仅执行一次,优先于主函数;接着是局部代码块,最后是构造代码块与构造器的结合使用。通过实例输出结果清晰展示其执行流程,帮助开发者快速掌握初始化机制。
|
4天前
|
Java 应用服务中间件 网络安全
Eclipse运行SSM/SSH项目教程
本教程介绍如何在Eclipse中配置Java Web项目并运行。涵盖JDK、Tomcat环境搭建,项目导入与Maven配置,Eclipse中绑定服务器及启动访问步骤。附常见问题如数据库连接修改说明,助你快速部署运行项目。(238字)
|
2天前
|
Java 测试技术 API
从Google线上故障,谈灰度发布的重要性
2025年6月12日,Google Cloud因未灰度发布新配置导致全球服务中断7小时。本文分析故障根因,强调配置灰度发布重要性,并详解基于Nacos的IP与标签灰度实现方案,助力企业提升系统稳定性,防范类似风险。
|
2天前
|
监控 Java 测试技术
OOM排查之路:一次曲折的线上故障复盘
本文详述了一次线上服务因Paimon数据湖与RocksDB集成引发的三次内存溢出(OOM)故障排查全过程。从线程暴增到堆外内存泄漏,团队历经曲折,结合MAT、NMT、async-profiler等工具,最终定位至RocksDB通过JNI申请却未释放内存的根源问题。文章系统梳理了排查思路与工具使用,并总结出保留现场、分步分析、善用专家支持等实用经验,为同类技术栈问题提供宝贵借鉴。(238字)
|
2天前
|
存储 缓存 监控
EFC&CTO:缓存引发数据不一致问题排查与深度解析
EFC客户端更新缓存架构后,CTO测试出现data mismatch。排查发现因版本号回退,缓存读取旧NULL数据致pagecache污染,脏页回刷破坏文件系统。修复版本号递增机制后问题解决,期间深入理解了buffer写、setattr触发等内核行为差异。
|
2天前
|
消息中间件 监控 Java
RocketMQ:底层Netty频繁OS OOM
本文记录了一例Java应用因多ClassLoader加载多个Netty堆外内存分配器导致OS OOM的排查过程。虽设MaxDirectMemorySize为1G,但多个中间件独立使用PooledByteBufAllocator,各自绕过JVM直接内存限制,累计超用近1.5G。通过NMT、Arthas等工具定位到RocketMQ等组件占用过高,最终建议调低堆内存以保障堆外空间,并推动中间件优化。
|
2天前
|
存储 运维 NoSQL
Redis:内存陡增100%深度复盘
一次Redis崩溃事故复盘:因大KEY导致带宽占满,触发缓冲区激增,内存被耗尽。虽有淘汰策略,但仅作用于数据内存,缓冲区内存无限制,最终引发全面超时。根源在于网络与存储资源设计失衡,暴露了缓冲区管理盲区。
|
2天前
|
消息中间件 物联网 测试技术
语音通知
适用于科技公司服务器或物联网设备异常时的语音告警场景。开通语音服务后,可申请资质、话术、号码及模板,通过API调用实现自动拨打电话通知告警人,支持变量替换与多模式配置,并可通过控制台或API查询呼叫记录,推荐使用消息回执接收结果。
|
2天前
|
架构师 前端开发 中间件
技术新人成长到Leader的路径
本文为技术人成长进阶指南,涵盖新人到技术leader的四大阶段:做事立身、驱动成事、架构统筹、成就他人。强调从“靠谱”起步,构建思维框架,修炼学习、沟通与逻辑能力;逐步培养自我驱动力,打造核心竞争力;以系统性思维做架构设计,平衡人与事;最终成为善做会说、利他赋能的技术领导者,实现从个人优秀到团队成功的跨越。(239字)