摸鱼的时候继续复刻demo,没错,同之前一篇文章,在写aop时又发现自己对aop只停留在面试阶段,甚至还不如,完全不会实践,所以在此记录复刻aop用到的的一些且自己已经遗忘的知识。
那么复刻的一个需求点我以最简单来说对于此次要学习的内容:就是通过joinPoint获取方法上的特定注解。
注解代码如下:
import com.uum.common.core.enums.BusinessType;
import java.lang.annotation.*;
/**
* 自定义注解--操作日志记录
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Operation {
/**
* 模块
*/
String description() default "";
/**
* 功能
*/
BusinessType businessType() default BusinessType.OTHER;
/**
* 是否保存请求的参数
*/
boolean isSaveRequestData() default true;
/**
* 是否保存返回结果
*/
boolean isSaveResponseData() default true;
}
那么这个就是系统操作日志的注解,主要到时候用aop的joinPoint获取标注在方法上该注解联合方法的相关内容将其转化为系统日志并进行存储。那么问题来了,什么是joinPoint?
方法名 | 功能 |
---|---|
Signature getSignature(); | 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息 |
Object[] getArgs(); | 获取传入目标方法的参数对象 |
Object getTarget(); | 获取被代理的对象 |
Object getThis(); | 获取代理对象 |
其中关键方法就是这个getSignature(),该方法返回Signature对象,而通过该对象我们可以获得被增强方法的信息。 |
ProceedingJoinPoint继承JoinPoint,是它的子接口,在joinPoint的基础上对其增强功能。其实主要是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。
public interface ProceedingJoinPoint extends JoinPoint {
public Object proceed() throws Throwable;
public Object proceed(Object[] args) throws Throwable;
}
环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的
public Object around(ProceedingJoinPoint point) throws Throwable {
Signature signature = point.getSignature();
// AopUtils.getTargetClass(point.getTarget())获取原始对象,例如对于Mapper而言,它获取的是具体代理的Mapper如com.b.mapper.DefaultDsMapper(如果前者继承了后者的话)而不是定义该方法的Mapper如com.b.base.BaseMapper,如下图
Type[] types = AopUtils.getTargetClass(point.getTarget()).getGenericInterfaces(); // getGenericInterfaces方法能够获取类/接口实现的所有接口
Annotation nologgingAnno = ((Class)types[0]).getAnnotation(Nologging.class); // type是所有类型的父接口
MethodSignature methodSignature = (MethodSignature)signature;
Method targetMethod = methodSignature.getMethod();
}
此接口通常用于跟踪或记录应用程序以获取有关连接点的反射信息,下面是官方给的一个使用的例子
aspect Logging {
Logger logger = Logger.getLogger("MethodEntries");
before(): within(com.bigboxco..*) && execution(public * *(..)) {
Signature sig = thisJoinPoint.getSignature();
logger.entering(sig.getDeclaringType().getName(),
sig.getName());
}
}
源码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.aspectj.lang;
public interface Signature {
String toString();
//返回此签名的缩写字符串表示形式
String toShortString();
//返回此签名的扩展字符串表示形式
String toLongString();
//返回此签名的标识符部分。对于方法,这将返回方法名称。
String getName();
//返回表示为 int 的此签名上的修饰符。使用 java.lang.reflect.Modifier 上定义的常量和辅助方法来操作它,
// 检查此签名是否公开 java.lang.reflect.Modifier.isPublic(sig.getModifiers());
// 打印出修饰符 java.lang.reflect.Modifier.toString(sig.getModifiers());
int getModifiers();
//返回一个 java.lang.Class 对象,表示声明此成员的类、接口或方面。对于成员内声明,这将是声明成员的类型,而不是按词法写入声明的类型。使用 SourceLocation.getWithinType() 获取在词法上出现声明的类型。
//为了与 java.lang.reflect.Member 保持一致,这个方法应该被命名为 getDeclaringClass()
Class getDeclaringType();
//返回声明类型的完全限定名称。这等效于调用 getDeclaringType().getName(),但是为了更高的效率缓存了结果
String getDeclaringTypeName();
}
HttpClient请求
GET http://localhost:8080/test/testAnno?name=Indian frends
Accept: application/json
运行结果
I'm fine ,thank you
toString; String com.mz.testSpring.controler.testSpringControler.sayHello(String)
toShortString; testSpringControler.sayHello(..)
toLongString; public java.lang.String com.mz.testSpring.controler.testSpringControler.sayHello(java.lang.String)
getName; sayHello
getModifiers; 1
getDeclaringType; com.mz.testSpring.controler.testSpringControler
getDeclaringTypeName; com.mz.testSpring.controler.testSpringControler
可以看出,Signature 也是为了获取原始方法的各种信息而存在的,并且信息更加的简洁,方便我们截取,他与JoinPoint合作可以让我们更方便的获取原始方法的各种信息
当然我们通过Signature上面的方法还是不能很方便达成我们获取方法上的注解等操作。因为我们获取不了方法本身这个Method类。
那么这时MethodSignature就出现了,顾名思义,它就是方法签名,可以看下它的源码:
public interface MethodSignature extends CodeSignature {
Class getReturnType(); /* name is consistent with reflection API */
Method getMethod();
}
它就提供了两个方法,其中关键点就在于getMethod()方法,它可以获取被增强的方法本身Method这个类,通过该类就类似反射操作了,可以通过它的API完成获取注解等操作。
那怎么通过Signature得到MethodSignature呢
其实很简单,可以看一下它的依赖关系图:
所以你应该就明白了,强转即可。
那么我们就可以完成通过Signature获取注解的操作,方法如下:
private Optional<Operation> getAnnotationLog(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
if(method != null){
return Optional.ofNullable(method.getAnnotation(Operation.class));
}
return Optional.empty();
}
四、参考链接
https://blog.csdn.net/M_amazing/article/details/121747188
https://blog.csdn.net/qq_46940224/article/details/125960508
https://blog.csdn.net/qq_15037231/article/details/80624064