自定义注解

简介: 本文介绍如何在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);
}

相关文章
|
2月前
|
Java 大数据 Apache
Excel工具-HUTOOL-输出Excel
Hutool基于Apache POI封装了Excel读写功能,提供ExcelWriter和BigExcelWriter类,支持写出List、Map、Bean等数据类型到Excel,可自定义样式、多sheet操作,并解决大数据量导出时的内存溢出问题,适用于文件导出、客户端下载等场景。
|
2月前
|
Apache
Excel工具-HUTOOL-读取Excel
基于Hutool和Apache POI,封装Excel读取工具,支持xls/xlsx格式。可读取为List、Map或Bean,提供Sax模式(Excel03SaxReader/Excel07SaxReader)高效处理大文件,避免内存溢出,适用于海量数据流式读取。
|
2月前
|
存储 缓存 Java
|
2月前
|
存储 安全 Java
Java泛型类型擦除以及类型擦除带来的问题
Java泛型在编译时会进行类型擦除,仅保留原始类型(如Object或限定类型)。例如,List&lt;String&gt;和List&lt;Integer&gt;在运行时均为List,导致无法通过instanceof判断泛型类型。类型检查在编译期完成,基于引用而非对象本身。擦除后,编译器自动插入强制类型转换以保证类型安全。但这也引发与多态冲突、静态成员不能使用类泛型等问题。反射可绕过泛型限制,证明类型信息已丢失。基本类型不可作为泛型参数,需使用包装类。
|
2月前
|
NoSQL Redis
Redis集群伸缩,转移插槽失败
Redis集群出现配置不一致,提示slot 0处于导入状态。需登录对应实例,执行`cluster setslot 0 stable`命令,将异常slot状态恢复稳定,修复后方可重新平衡集群。注意根据实际slot ID调整命令参数。
|
2月前
|
消息中间件 存储 Unix
Mac系统安装教程
RabbitMQ是基于AMQP协议的开源消息代理,支持异步通信与解耦分布式系统。它具备高可靠性、灵活路由、持久化及可扩展性,广泛用于微服务架构。支持多种安装方式,如Homebrew和二进制,便于本地部署与管理。
|
2月前
|
Dubbo Java 应用服务中间件
Feign远程调用
本章介绍如何用Feign替代RestTemplate实现更优雅的HTTP跨服务调用。通过引入Feign,解决服务地址硬编码、代码可读性差等问题,结合注册中心实现声明式远程调用。内容涵盖Feign基本使用、自定义配置(如日志级别)、连接池优化(如Apache HttpClient),并探讨继承与抽取两种最佳实践方案,提升微服务间通信的可维护性与扩展性。
|
2月前
|
敏捷开发 Java 测试技术
为什么要单元测试
单元测试是保障代码质量的基石。它通过快速、稳定的自动化测试,提升代码可读性、可维护性,助力高效调试与重构。尽管短期看似耗时,长远却显著提升研发效率,支撑持续交付,让软件开发从“爬行”迈向“奔跑”。
|
2月前
|
JSON 缓存 前端开发
什么是跨域
CORS(跨域资源共享)是W3C标准,允许浏览器向跨源服务器发送XMLHttpRequest请求,突破AJAX同源限制。浏览器自动处理通信,关键在于服务器需支持CORS接口。请求分为简单和非简单两类,后者需预检。相比仅支持GET的JSONP,CORS更强大灵活,兼容现代浏览器。
|
2月前
|
SQL 安全 网络协议
常见的网络攻击
恶意软件、网络钓鱼、中间人攻击、DDoS攻击、SQL注入、零日漏洞及DNS隧道是常见网络安全威胁。恶意软件窃取数据或破坏系统;网络钓鱼伪装可信来源骗取信息;中间人攻击窃听通信;DDoS以海量流量瘫痪服务;SQL注入操控数据库;零日攻击利用未修复漏洞;DNS隧道则隐蔽传输恶意数据,均需严密防范。