jdk動的プロキシに基づくAOP実装
jdkの動的プロキシはインターフェースに基づいており、特定の制限があり、実装用のインターフェースを提供する必要があります
jdkダイナミックプロキシの概要
まず、プロキシクラスはInvocationHandlerインターフェイスを実装する必要があります
委任されたクラスがメソッドを呼び出すと、このメソッドのmethod.invoke()ステートメントを通じて呼び出されます。
これから、次のようなツールクラスを実装できます。
パラメータのプロキシとインスタンスは同じメソッドを持っていますが、呼び出しメソッドの最初のパラメータをプロキシに渡すことができない場合、スタックオーバーフローが発生することに注意してください。
InvocationHandlerのインスタンスであるプロキシクラスのinvokeメソッドが呼び出されるため、これは再帰呼び出しであるため、上記のjava.lang.StackOverflowErrorが表示されます。
/**
*
* @param <T> 由于JDK动态代理的特性,需要一个实现接口,这里用于
* @see DynamicProxy#getProxyInstance() 的类型转换
*/
public class DynamicProxy<T> implements InvocationHandler {
//由于JDK动态代理的特性,需要一个实现接口,这里用于类型转换
private final Class<T> instanceInterfaceClass;
//被代理的类
private final Object instance;
/**
* 被代理的类调用方法时都通过这个方法内的method.invoke()语句调用
* 注意虽然参数proxy和instance拥有相同的方法,但是如果invoke方法的第一个参数不能传入
* proxy,会导致堆栈溢出
* @param proxy 当前代理类
* @param method 当前方法
* @param args 当前方法参数列表
* @return 方法返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
try {
res = method.invoke(instance, args);
} catch (IllegalAccessException e) {
//处理异常
}
return rs;
}
/**
* 获取代理类
* @return 代理类
*/
public T getProxyInstance(){
Class<?> instanceClass = instance.getClass();
return (T) Proxy.newProxyInstance(instanceClass.getClassLoader(), instanceClass.getInterfaces(), this);
}
复制代码
最後に、このクラスをインスタンス化してgetProxyInstanceメソッドを呼び出すことにより、プロキシクラスを取得します。
特定の時間ノードにコールバックを追加する
私たちが呼び出す対応するメソッドはプロキシを追加するので、メソッドが呼び出される前、呼び出しの後、例外をスローした後、そして戻った後にコールバックを追加することもできます
メソッドが呼び出される前に、通常はパラメーターの検証や変更に使用できるため、次のようなマッピングを確立できます。
オブジェクト[]->オブジェクト[]、つまり、そのコールバック関数(インターフェイスは)関数<オブジェクト[]、オブジェクト[]>
呼び出し後(戻り値の前)、通常は戻り値を処理して変更できます。つまり、そのようなマッピングを確立できます。
Object-> Object、つまり、そのコールバック関数(インターフェースは)Function <Object、Object>
例外がスローされた後、スローされた例外の例外情報を消費できるため、このようなマッピングを確立できますException-> void
つまり、そのコールバック関数(インターフェースは)です。
値を返した後、いくつかの情報を記録できます。理論的には、コンシューマーインターフェイスも使用できますが、別のエンティティークラスを自分で作成する必要があります。
、それで私は自分でインターフェースを作ることを選びました
もちろん、すべてのメソッドをプロキシできるわけではないので、メソッドを拡張するかどうかを制御するフィルターを追加して、
メソッド->ブールマッピング、つまり関数<メソッド、ブール>
/**
* 返回值后的回调,包含被代理的实例,代理类,当前的方法,返回值,参数列表
*/
@FunctionalInterface
public interface AfterResultCallback{
/**
*
* @param instance 被代理的实例
* @param proxy 当前代理
* @param method 被增强的方法
* @param res 返回值
* @param arg 参数
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
void callback(Object instance, Object proxy, Method method,Object res,Object... arg) throws InvocationTargetException, IllegalAccessException;
}
复制代码
したがって、上記のコールバックツールクラスは次のように書き換えることができます。
/**
*
* @param <T> 由于JDK动态代理的特性,需要一个实现接口,这里用于
* @see DynamicProxy#getProxyInstance() 的类型转换
*/
public class DynamicProxy<T> implements InvocationHandler {
//异常时的回调
private Consumer<Exception> exceptionCallback;
//方法调用时的回调,可以用于修改入参或者验证入参
private Function<Object[],Object[]> beforeInvokeCallback;
//在方法执行后返回值之前的回调,可以修改返回值(不能绕开类型检测)
//也就说原函数返回T类型不能修改为返回R类型(T和R没有继承关系)
private Function<Object,Object> beforeResultCallback;
//返回值后的回调
//Object instance, Object proxy, Method method,Object res,Object... arg)
private AfterResultCallback afterResultCallback;
//方法拦截的条件
private Function<Method,Boolean> methodVerify;
//由于JDK动态代理的特性,需要一个实现接口,这里用于类型转换
private final Class<T> instanceInterfaceClass;
//被代理的类
private final Object instance;
/**
*
* @param proxy 当前代理类
* @param method 当前方法
* @param args 当前方法参数列表
* @return 方法返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//如果没有拦截条件,或者不满足条件直接执行
if (methodVerify == null || !methodVerify.apply(method)) {
return method.invoke(instance, args);
}
Object res = null;
try {
//如果存在回调,那么调用
if (beforeInvokeCallback != null) {
args = beforeInvokeCallback.apply(args);
}
res = method.invoke(instance, args);
if (beforeResultCallback != null){
res = beforeResultCallback.apply(res);
}
if (afterResultCallback != null){
afterResultCallback.callback(instance, proxy, method, res, args);
}
} catch (IllegalAccessException e) {
if (exceptionCallback != null) {
exceptionCallback.accept(e);
}
}
return res;
}
/**
* 获取代理类
* @return 代理类
*/
public T getProxyInstance(){
Class<?> instanceClass = instance.getClass();
return (T) Proxy.newProxyInstance(instanceClass.getClassLoader(), instanceClass.getInterfaces(), this);
}
/**
*
* @param instanceInterfaceClass 代理接口
* @param instance 被代理的类
*/
public DynamicProxy(Class<T> instanceInterfaceClass, Object instance) {
this.instanceInterfaceClass = instanceInterfaceClass;
this.instance = instance;
}
//以下均为注册回调函数,用builder模式构建
public DynamicProxy<T> setExceptionCallback(Consumer<Exception> exceptionCallback) {
this.exceptionCallback = exceptionCallback;
return this;
}
public DynamicProxy<T> setBeforeInvokeCallback(Function<Object[], Object[]> beforeInvokeCallback) {
this.beforeInvokeCallback = beforeInvokeCallback;
return this;
}
public DynamicProxy<T> setBeforeResultCallback(Function<Object, Object> beforeResultCallback) {
this.beforeResultCallback = beforeResultCallback;
return this;
}
public DynamicProxy<T> setAfterResultCallback(AfterResultCallback afterResultCallback) {
this.afterResultCallback = afterResultCallback;
return this;
}
public DynamicProxy<T> setMethodVerify(Function<Method,Boolean> methodVerify) {
this.methodVerify = methodVerify;
return this;
}
}
复制代码
AOP機能の実現
動的プロキシのツールクラスを作成しました。ここで、拡張メソッドを拡張メソッドに織り込む方法を検討する必要があります
アスペクトメソッドアノテーション
アノテーションを使用して、切断面の各ステージのコールバック関数にマークを付け、対応するメソッドを反射によって上記の対応するインターフェイスにマップできます。
つまり、マークされた関数インスタンス(method.invoke())は、対応するインターフェイスの実装クラスで呼び出されます
@AfterResult @BeforeInvoke @BeforeResult @ExceptionCallBack
元のメソッドが呼び出される前、戻り値の後、例外をスローした後の戻り値に対応
すべてが空の注釈であり、注釈にのみ使用され、データを運ばないため、例を挙げます
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterResult {
}
复制代码
フィルター方式モードの注釈
単に書き込むだけで、戻り値の型とメソッド名のみを制御します
後で検出メソッドシグネチャに拡張できます
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MethodPatten {
//返回值类型
Class[] res() default{Object.class};
//方法名,为空则放行所有方法
String[] methodName() default {""};
}
复制代码
Aopコンポーネントを初期化する
スライスをスキャンするすべての方法は、注釈と関係を確立する方法をマークします
//切面方法
private Object aspect;
private Object instance;
private Class<T> instanceInterface;
//切面方法与对应注解的关系
private Map<Class,Method> annotationToMethod;
/**
* @param aspect 切面类
* @param instance 被代理实例
*/
public AOP(Object aspect, Object instance) {
this.aspect = aspect;
this.instance = instance;
this.annotationToMethod = new HashMap<>();
Method[] methods = aspect.getClass().getMethods();
//只获取第一个注解作为键,方法作为值
for (Method method : methods) {
Annotation[] annotations = method.getAnnotations();
if (annotations.length > 0){
//重复注解的只保留第一个扫描到的
//必须使用annotationType(),如果使用getClass则是获取的代理类
annotationToMethod.putIfAbsent(annotations[0].annotationType(),method);
}
}
}
复制代码
メソッドフィルタリングインターフェイスの例を取得する
//获取拦截条件
private Function<Method,Boolean> getMethodVerify(){
//如果没有MethodPattern指定条件 则全不拦截
MethodPatten patten = aspect.getClass().getAnnotation(MethodPatten.class);
if (patten == null) {
return (m) -> false;
}
return (m) -> {
//验证被增强的方法(被代理的类)的返回值是否符合条件
boolean isResPass = false;
for (Class re : patten.res()) {
if (re.equals(m.getReturnType())){
isResPass = true;
break;
}
}
//验证被增强的方法(被代理的类)的方法名是否符合条件
boolean isMethodNamePass =false;
for (String s : patten.methodName()) {
//条件注解中指定的方法名为空则直接放行
if (s.isEmpty() || s.equals(m.getName())){
isMethodNamePass = true;
break;
}
}
//全通过才放行
return isMethodNamePass && isResPass;
};
}
复制代码
関数呼び出しの前にコールバックインスタンスを取得する
//对应织入方法必须为同参数列表长度,必须返回值一致
private Function<Object[],Object[]> getBeforeInvokeCallback(){
Function<Object[],Object[]> res = null;
//获取对应方法,没有就跳过
Method method = annotationToMethod.get(BeforeInvoke.class);
if (method == null){
return null;
}
res = (arg) -> {
try {
//强制防止展开
//这个method是切面增强的方法,通过调用这个方法对arg处理
return (Object[]) method.invoke(aspect,(Object)arg);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
};
return res;
}
复制代码
例外がスローされたコールバックインスタンスを取得する
private Consumer<Exception> getExceptionCallback(){
Method method = annotationToMethod.get(ExceptionCallback.class);
if (method == null){
return null;
}
Consumer<Exception> res = (e) -> {
try {
method.invoke(aspect, e);
} catch (IllegalAccessException illegalAccessException) {
illegalAccessException.printStackTrace();
} catch (InvocationTargetException invocationTargetException) {
invocationTargetException.printStackTrace();
}
};
return res;
}
复制代码
戻り値を取得する前のコールバック
private Function<Object,Object> getBeforeResultCallback(){
Method method = annotationToMethod.get(BeforeResult.class);
if (method == null){
return null;
}
Function function = (res) -> {
try {
return method.invoke(aspect, res);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
};
return function;
}
复制代码
戻り値を取得した後のコールバック
private AfterResultCallback getAfterResultCallback(){
Method method = annotationToMethod.get(AfterResult.class);
if (method == null){
return null;
}
AfterResultCallback afterResultCallback = (proxied,proxy,proxyMethod,res,arg) ->{
method.invoke(aspect, proxied,proxy,proxyMethod,res,arg);
};
return afterResultCallback;
}
复制代码
プロキシクラスを取得する
public T getInstance(){
//利用工具类获取代理类并注册回调函数,返回值为代理类
DynamicProxy<T> proxy = new DynamicProxy<>(instanceInterface, instance);
proxy.setMethodVerify(getMethodVerify())
.setBeforeInvokeCallback(getBeforeInvokeCallback())
.setExceptionCallback(getExceptionCallback())
.setBeforeResultCallback(getBeforeResultCallback())
.setAfterResultCallback(getAfterResultCallback());
return proxy.getProxyInstance();
}
复制代码
Aop機能テスト
インターフェース
public interface I {
Object dosome(Integer integer1,Integer i2);
default double add(Integer integer1,Integer integer2,Integer i3){
return integer1+integer2+i3;
}
}
复制代码
クラス
public class Impl implements I {
@Override
public Object dosome(Integer integer1,Integer integer2) {
return (Object) Integer.max(integer1, integer2);
}
}
复制代码
切断面
@MethodPatten(res = double.class)
public class aspect{
@BeforeInvoke
public Object[] beforeinvoke(Object...objects){
System.out.println("参数列表为"+ Arrays.toString(objects));
return objects;
}
@BeforeResult
public Object before(Object o){
System.out.println("修改为double max");
return Double.MAX_VALUE;
}
@AfterResult
public void after(Object instance, Object proxy, Method method, Object res, Object...arg){
System.out.println("instance is "+instance.getClass());
System.out.println("proxy is "+proxy.getClass());
System.out.println("method is "+method.getName());
System.out.println("return value is "+res);
System.out.println("arg is "+Arrays.toString(arg));
}
}
复制代码
試験方法
public static void main(String[] args)throws InterruptedException {
AOP<I> aop = new AOP<>(new aspect(), new Impl());
I i = aop.getInstance();
System.out.println(i.add(100, 10,2000));
System.out.println("\n"+i.dosome(100, 312));
}
复制代码
コンソール出力
二重の戻り値を返すメソッドのみが傍受されていることがわかります
参数列表为[100, 10, 2000]
修改为double max
instance is class MySpring.Aop.Test.Impl
proxy is class com.sun.proxy.$Proxy6
method is add
return value is 1.7976931348623157E308
arg is [100, 10, 2000]
1.7976931348623157E308
312
复制代码