参考视频
拦截器自定义注解
AOP自定义注解
通过AOP获取属性
拦截器、过滤器、AOP的区别和联系
个人学习笔记及源码
注:这里仅讲怎么使用,具体原理请参考个人学习笔记
可以通过javadoc命令生成API文档
javadoc XXX.java
#{元注解}
public @interface #{注解名称} {
#{属性列表}
}
注解名称:自定义的注解名称
元注解:用于定义注解的注解,常用的元注解有如下四个:
@Target : 描述该注解作用范围,即应该标注在java文件的什么上面,常用的值为:
- ElementType.Type : 作用于类
- ElementType.METHOD: 作用于方法
- ElementType.FIELD:作用于字段
当然还有其他值可以进行查阅
@Retention:描述注解被保留的阶段,常用的值为:
- RetentionPolicy.RUNTIME:当前描述的注解,会保留到class字节码文件中,并被jvm读取到
当然还有其他值可以进行查阅
@Documented:描述注解是否被抽取到api文档中,选填,填上就表示参与到API文档中
@Inherited:描述注解是否可以被继承,选填,填上就表示可以被继承
属性列表:注解的属性,需要抽象方法来定义,且必须要有返回值,关于原理下面会讲,这里仅仅讲使用规则
- 抽象方法必须有返回结果,且返回结果必须是如下类型:
① 基本数据类型
② String类型
③ 枚举类型
④ 注解
⑤ 以上类型的数组
属性赋值注意点:
下面将结合例子进行说明
下面演示在不结合SpringBoot的情况下,使用自定义注解进行简单的测试
package com.example.demo.localAnnotation.selfDemo.annoUse;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnno {
String shuXing1() default "zhangsan";
int shuXing2();
}
package com.example.demo.localAnnotation.selfDemo.annoUse;
/**
* @program: AnnotationDemo
* @description:
* @author: wjl
* @create: 2023-12-04 23:48
**/
@TestAnno(shuXing2 = 12315)
public class AnnoMethods {
@TestAnno(shuXing1 = "XXX",shuXing2 = 66)
private String str = "testShuXing";
@TestAnno(shuXing2 = 10086)
public int method01(int a,int b){
return a+b;
}
@TestAnno(shuXing1 = "lisi",shuXing2 = 10010)
public String method02(String str1,String str2,String str3){
return str1+"======"+str2+"======"+str3;
}
@TestAnno(shuXing1 = "wanglong",shuXing2 = 123456)
public void method03(){
}
}
package com.example.demo.localAnnotation.selfDemo.annoUse;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @program: AnnotationDemo
* @description:
* @author: wjl
* @create: 2023-12-05 00:14
**/
@SuppressWarnings("all")
public class TestAnnoMethods {
@Test
public void test01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
String mathodName = "method01";
// 一定要创建一个实例
AnnoMethods methods = new AnnoMethods();
// 确定执行方法
Class> methodsClass = methods.getClass();
Class>[] classArr = new Class[]{int.class,int.class};
Method method = methodsClass.getDeclaredMethod(mathodName, classArr);
// 设置私有方法可达
method.setAccessible(true);
// 确定方法的参数
Object[] valueArr = new Object[]{1,100};
// 执行方法 和返回值
Object result = method.invoke(methods, valueArr);
// 获取 该方法上面的注解 以及相关值
TestAnno anno = method.getAnnotation(TestAnno.class);
String shuXing1 = anno.shuXing1();
int shuXing2 = anno.shuXing2();
// 遍历进行输出
System.out.println("下面是获取到的相关值,以及通过反射获取到的结果");
System.out.println("执行方法为:"+mathodName);
System.out.println("方法参数为:"+valueArr[0]+"******"+valueArr[1]);
System.out.println("执行结果为:"+result);
System.out.println("注解属性1的值为:"+shuXing1);
System.out.println("注解属性2的值为:"+shuXing2);
}
@Test
public void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
String mathodName = "method02";
// 一定要创建一个实例
AnnoMethods methods = new AnnoMethods();
// 确定执行方法
Class> methodsClass = methods.getClass();
Class>[] classArr = new Class[]{String.class,String.class,String.class};
Method method = methodsClass.getDeclaredMethod(mathodName, classArr);
// 设置私有方法可达
method.setAccessible(true);
// 确定方法的参数
Object[] valueArr = new Object[]{"peter","wanglili","xuanfeng"};
// 执行方法 和返回值
Object result = method.invoke(methods, valueArr);
// 获取 该方法上面的注解 以及相关值
TestAnno anno = method.getAnnotation(TestAnno.class);
String shuXing1 = anno.shuXing1();
int shuXing2 = anno.shuXing2();
// 遍历进行输出
System.out.println("下面是获取到的相关值,以及通过反射获取到的结果");
System.out.println("执行方法为:"+mathodName);
System.out.println("方法参数为:"+valueArr[0]+"******"+valueArr[1]+"******"+valueArr[2]);
System.out.println("执行结果为:"+result);
System.out.println("注解属性1的值为:"+shuXing1);
System.out.println("注解属性2的值为:"+shuXing2);
}
@Test
public void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
String mathodName = "method03";
// 一定要创建一个实例
AnnoMethods methods = new AnnoMethods();
// 确定执行方法
Class> methodsClass = methods.getClass();
Class>[] classArr = new Class[]{};
Method method = methodsClass.getDeclaredMethod(mathodName, classArr);
// 设置私有方法可达
method.setAccessible(true);
// 确定方法的参数
Object[] valueArr = new Object[]{};
// 执行方法 和返回值
Object result = method.invoke(methods, valueArr);
// 获取 该方法上面的注解 以及相关值
TestAnno anno = method.getAnnotation(TestAnno.class);
String shuXing1 = anno.shuXing1();
int shuXing2 = anno.shuXing2();
// 遍历进行输出
System.out.println("下面是获取到的相关值,以及通过反射获取到的结果");
System.out.println("执行方法为:"+mathodName);
System.out.println("方法参数为:");
System.out.println("执行结果为:"+result);
System.out.println("注解属性1的值为:"+shuXing1);
System.out.println("注解属性2的值为:"+shuXing2);
}
@Test
public void test04(){
Class methodsClass = AnnoMethods.class;
TestAnno annotation = methodsClass.getAnnotation(TestAnno.class);
String shuXing1 = annotation.shuXing1();
int shuXing2 = annotation.shuXing2();
System.out.println("类上注解属性1的值为:"+shuXing1);
System.out.println("类上注解属性2的值为:"+shuXing2);
}
@Test
public void test05() throws NoSuchFieldException, IllegalAccessException {
AnnoMethods methods = new AnnoMethods();
// 确定执行方法
Class> methodsClass = methods.getClass();
Field field = methodsClass.getDeclaredField("str");
field.setAccessible(Boolean.TRUE);
String fieldName = field.getName();
Object o = field.get(methods);
TestAnno annotation = field.getAnnotation(TestAnno.class);
String shuXing1 = annotation.shuXing1();
int shuXing2 = annotation.shuXing2();
System.out.println(fieldName);
System.out.println(o);
System.out.println(shuXing1);
System.out.println(shuXing2);
}
}
AOP+自定义注解,是以自定义注解作为切面,进入动态代理,获取到被加注解的类、方法、变量的信息,结合注解的值,进行相关操作,比如:记录日志等;
参考文章
配置流程如下(具体见本人demo):
org.springframework.boot
spring-boot-starter-aop
package com.example.demo.springAnnotation.AOP;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
String upDbName();
}
package com.example.demo.springAnnotation.AOP;
import com.example.demo.utils.IPAddressUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
*/
@Component
@Aspect
@Slf4j
public class MyLogAspect {
/**
* IP 地址 解析工具类
*/
@Autowired
private IPAddressUtils ipAddressUtils;
/**
* 只要用到了com.javaxl.p2.annotation.springAop.MyLog这个注解的,就是目标类
*/
@Pointcut("@annotation(MyLog)")
private void MyValid() {
}
/* @Before("MyValid()")
public void before(JoinPoint joinPoint) {
log.info("----------Before开始-----------");
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String classType = joinPoint.getTarget().getClass().getName();
MyLog myLog = method.getAnnotation(MyLog.class);
String desc = myLog.upDbName();
//获取当前请求request对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
log.info("方法名:"+ signature.getName());
log.info("参数值集合:"+ Arrays.asList(joinPoint.getArgs()));
log.info("参数值类型:"+ joinPoint.getArgs()[0].getClass().getTypeName());
log.info("获取目标方法所在类:"+ classType);
log.info("指定注解MyLog的属性值为:"+desc);
log.info("获取URI地址:"+request.getRequestURI());
log.info("获取URL地址:"+request.getRequestURL());
log.info("获取请求方式:"+request.getMethod());
log.info("获取请求的ip地址:"+IPAddressUtils.getIpAdrress(request));
log.info("----------Before结束-----------");
}*/
/* @After("MyValid()")
public void after(JoinPoint joinPoint){
log.info("后置通知");
}*/
@Around("MyValid()")
public void around(ProceedingJoinPoint joinPoint)throws Throwable {
log.info("----------Around开始-----------");
log.info("=========================================================");
log.info("==========================================================");
log.info("===你可以根据获取到的信息进行任何操作,比如:进行日志的记录等!!!===");
log.info("==========================================================");
log.info("==========================================================");
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String classType = joinPoint.getTarget().getClass().getName();
MyLog myLog = method.getAnnotation(MyLog.class);
String desc = myLog.upDbName();
//获取当前请求request对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
log.info("方法名:"+ signature.getName());
log.info("参数值集合:"+ Arrays.asList(joinPoint.getArgs()));
log.info("参数值类型:"+ joinPoint.getArgs()[0].getClass().getTypeName());
log.info("获取目标方法所在类:"+ classType);
log.info("指定注解MyLog的属性值为:"+desc);
log.info("获取URI地址:"+request.getRequestURI());
log.info("获取URL地址:"+request.getRequestURL());
log.info("获取请求方式:"+request.getMethod());
log.info("获取请求的ip地址:"+IPAddressUtils.getIpAdrress(request));
Object result = joinPoint.proceed();
log.info("方法的执行结果:"+result);
log.info("----------Around结束-----------");
}
}
package com.example.demo.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
@Slf4j
@Component
public class IPAddressUtils {
/**
* 获取IP地址
*/
public static String getIpAdrress(HttpServletRequest request) {
String ipAddress = request.getHeader("X-Forwarded-For");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if ("127.0.0.1".equals(ipAddress) || "0:0:0:0:0:0:0:1".equals(ipAddress)) {
// 根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (Exception e) {
log.error("根据网卡获取本机配置的IP异常=>", e.getMessage());
}
if (inet.getHostAddress() != null) {
ipAddress = inet.getHostAddress();
}
}
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) {
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
return ipAddress;
}
}
package com.example.demo.service;
public interface LogService {
String test01(String str1,String str2);
Integer test02(Integer num1,Integer num2);
}
package com.example.demo.service.impl;
import com.example.demo.service.LogService;
import com.example.demo.springAnnotation.AOP.MyLog;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @program: AnnotationDemo
* @description:
* @author: wjl
* @create: 2023-12-13 00:49
**/
@Service
@Slf4j
public class LogServiceImpl implements LogService {
@Override
@MyLog(upDbName = "redis")
public String test01(String str1, String str2) {
log.info("进入方法,参数方法分别为: {} ,{}",str1,str2);
String str = str1+"==="+str2;
return str;
}
@Override
@MyLog(upDbName = "mysql")
public Integer test02(Integer num1,Integer num2) {
log.info("进入方法,参数方法分别为: {} ,{}",num1,num2);
Integer num = num1+num2;
return num;
}
}
package com.example.demo.controller;
import com.example.demo.service.LogService;
import com.example.demo.springAnnotation.AOP.MyLog;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @program: AnnotationDemo
* @description:
* @author: wjl
* @create: 2023-12-01 00:02
**/
@RestController
public class AopController {
@Autowired
private LogService logService;
@RequestMapping("test_01")
public String testLogAspect01(@RequestParam("str1")String str1,@RequestParam("str2")String str2){
String str = logService.test01(str1, str2);
return str;
}
@RequestMapping("test_02")
public Integer testLogAspect02(@RequestParam("num1")Integer num1,@RequestParam("num2")Integer num2){
Integer num = logService.test02(num1, num2);
return num;
}
}
拦截器+自定义注解:拦截器通常是获取链接的入口方法(一半以controller方法为主),然后通过反射获取到方法信息以及相关注解的信息进行相关操作,比如登录验证等
参考文章
配置流程如下(具体见本人demo):
搭建SpringBoot项目,参考
package com.example.demo.springAnnotation.INTERCEPT;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
String userName();
}
package com.example.demo.springAnnotation.INTERCEPT;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @program: AnnotationDemo
* @description:
* @author: wjl
* @create: 2023-12-01 10:24
**/
@Configuration
public class InterceptorTrainConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SourceAccessInterceptor())
.addPathPatterns("/**") //需要拦截 验证的的
.excludePathPatterns("/test_01","/test_02"); //需要放过的
}
}
package com.example.demo.springAnnotation.INTERCEPT;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* @program: AnnotationDemo
* @description:
* @author: wjl
* @create: 2023-12-01 10:21
**/
public class SourceAccessInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("进入拦截器了");
// 反射获取方法上的LoginRequred注解
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
if (loginRequired == null) {
return true;
}
// 有LoginRequired注解说明需要登录,提示用户登录
String userName = loginRequired.userName();
response.setContentType("application/json; charset=utf-8");
response.getWriter().println("你访问的资源需要登录");
response.getWriter().println("注解的属性名为:"+userName);
response.getWriter().println("访问的方法名为:"+method.getName());
response.getWriter().println("返回的类型为:"+method.getReturnType().getName());
response.getWriter().println("=========================================================");
response.getWriter().println("=========================================================");
response.getWriter().println("=====拦截器同样可以获取到各种属性,根据自身需要进行调用=============");
response.getWriter().println("=========================================================");
response.getWriter().println("=========================================================");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
package com.example.demo.controller;
import com.example.demo.springAnnotation.INTERCEPT.LoginRequired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @program: AnnotationDemo
* @description:
* @author: wjl
* @create: 2023-12-01 10:15
**/
@RestController
public class InterceptController {
@GetMapping("/sourceA")
public String sourceA(){
return "你正在访问sourceA资源";
}
@GetMapping("/sourceB")
@LoginRequired(userName = "peter")
public String sourceB(){
return "你正在访问sourceB资源";
}
}