各类微服务框架都提供了AOP的能力,DW也不例外。AOP特别是在框架层面使用的特别多。使用的原理基本是动态代理模式。
其实我在想,动态代理,最终是运行时,在内存中生成新的类、方法字节码并加载。倘若使用APT将其做成编译期,是否也是可行的。只是一个思考。
可以参考文章:https://blog.csdn.net/sunquan291/article/details/126779400
一、扩展点接口
DW框架提供了对应的扩展点接口,我们只需要实现接口InterceptionService并发布成Bean
@Service
public class FrameInterceptionService implements InterceptionService {
该接口中定义了三个回调接口,分别是
@Contract
public interface InterceptionService {
/**
* If the returned filter returns true then the methods
* of the service will be passed to {@link #getMethodInterceptors(Method)}
* to determine if a method should be intercepted and the
* 判断哪些服务的方法和构造函数可以被传入以判断是否要被拦截
* constructor of the service will be passed to
* {@link #getConstructorInterceptors(Constructor)} to
* determine if the constructor should be intercepted
*
* @return The filter that will be applied to a descriptor
* to determine if it is to be intercepted. Should not
* return null
*/
public Filter getDescriptorFilter();
/**
* Each non-final method of a service that passes the
* {@link #getDescriptorFilter} method will be passed
* to this method to determine if it will intercepted
*
* @param method A non-final method that may
* be intercepted
* @return if null (or an empty list) then this method should
* NOT be intercepted. Otherwise the list of interceptors to
* apply to this method
*/
public List<MethodInterceptor> getMethodInterceptors(Method method);
/**
* The single chosen constructor of a service that passes the
* {@link #getDescriptorFilter} method will be passed
* to this method to determine if it will intercepted
*
* @param constructor A constructor that may
* be intercepted
* @return if null (or an empty list) then this constructor should
* NOT be intercepted. Otherwise the list of interceptors to
* apply to this method
*/
public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> constructor);
}
getDescriptorFilter 用于判断哪些服务的方法和构造函数可以被传入以判断是否要被拦截
getMethodInterceptors 普通方法决策使用什么拦截器
getConstructorInterceptors 构造方法决策使用什么拦截器
二、使用示例
本文中FrameInterceptionService,通过判断方法上是否有注解Record,进而决定是否使用MethodInterceptor进行切面处理。
@Service
public class FrameInterceptionService implements InterceptionService {
private final static MethodInterceptor RECORD_INTERCEPTOR = new RecordInterceptor();
private final static List<MethodInterceptor> RECORD_LIST = Collections.singletonList(RECORD_INTERCEPTOR);
public Filter getDescriptorFilter() {
return BuilderHelper.allFilter();
}
public List<MethodInterceptor> getMethodInterceptors(Method method) {
if (method.isAnnotationPresent(Record.class)) {
return RECORD_LIST;
}
return null;
}
public List<ConstructorInterceptor> getConstructorInterceptors(
Constructor<?> constructor) {
return null;
}
}
方法拦截器功能实现,针对方向执行进行相关调用明细的记录,方便后序调用数据分析。
public class RecordInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method m = invocation.getMethod();
String className = m.getDeclaringClass().getName();
String methodName = m.getName();
Object retVal;
if(TestFrame.enableToRecord(className,methodName)){
retVal = onRecordInvovation(invocation, className, methodName);
}else if(TestFrame.isTestEnvironment()){
retVal = onMockInvovation(invocation, className, methodName);
}else {
retVal = invocation.proceed();
}
return retVal;
}
private Object onMockInvovation(MethodInvocation invocation, String className, String methodName) throws Throwable {
Object retVal;
Object args[] = invocation.getArguments();
if(TestFrame.isMock(className,methodName)){
retVal = TestFrame.mockReturnValueAndDelete(className,methodName);
}else{
retVal = invocation.proceed();
}
return retVal;
}
private RecordItem createRecordItem(String className,String methodName,Throwable throwable,Object retVal,Object[] args){
if(throwable==null){
return RecordItem.createRecordItem(className, methodName,retVal,args);
}else {
return RecordItem.createRecordItem(className, methodName,throwable,args);
}
}
private Object onRecordInvovation(MethodInvocation invocation, String className, String methodName) throws Throwable {
Object retVal = null;
Object args[] = invocation.getArguments();
Throwable throwable = null;
try {
retVal = invocation.proceed();
}catch (Throwable t){
throwable = t;
}
RecordItem item = createRecordItem(className, methodName,throwable,retVal,args);
TestFrame.addRecordItem(item);
if(TestFrame.isRecordStartLocation(className,methodName)){
TestFrame.saveRecordResult();
TestFrame.endRecord();
}
if(throwable!=null){
throw throwable;
}
return retVal;
}
}