仿照 Spring 实现简单的 AOP

一、AOP相关概念

代理模式是 AOP 实现的基础。在介绍 AOP 的实现步骤之前,先引入 Spring AOP 中的一些概念,接下来我们会用到这些概念。

1.通知(Advice)

通知定义了要织入目标对象的逻辑,以及执行时机。

Spring 中对应了 5 种不同类型的通知:

  • 前置通知(Before):在目标方法执行前,执行通知
  • 后置通知(After):在目标方法执行后,执行通知,此时不关系目标方法返回的结果是什么
  • 返回通知(After-returning):在目标方法执行后,执行通知
  • 异常通知(After-throwing):在目标方法抛出异常后执行通知
  • 环绕通知(Around): 目标方法被通知包裹,通知在目标方法执行前和执行后都被会调用

2.切点(Pointcut)

如果说通知定义了在何时执行通知,那么切点就定义了在何处执行通知。所以切点的作用就是通过匹配规则查找合适的连接点(Joinpoint),AOP 会在这些连接点上织入通知。

3.切面(Aspect)

切面包含了通知和切点,通知和切点共同定义了切面是什么,在何时,何处执行切面逻辑。

二、AOP实现

简单 AOP 实现的步骤。这里 AOP 是基于 JDK 动态代理实现的,只需 3 步即可完成:

  • 定义一个包含切面逻辑的对象,这里假设叫 logMethodInvocation
  • 定义一个 Advice 对象(实现了 InvocationHandler 接口),并将上面的logMethodInvocation 和 目标对象传入
  • 将上面的 Adivce 对象和目标对象传给 JDK 动态代理方法,为目标对象生成代理

上面步骤比较简单,不过在实现过程中,还是有一些难度的,这里要引入一些辅助接口才能实现。接下来就来介绍一下简单 AOP 的代码结构:

MethodInvocation 接口  // 实现类包含了切面逻辑
Advice 接口        // 继承了 InvocationHandler 接口
BeforeAdvice 类    // 实现了 Advice 接口,是一个前置通知
Main      // 测试环境
HelloService 接口   // 目标对象接口
HelloServiceImpl   // 目标对象

【主业务代码】

package learn.spring.aop;// 主业务接口
public interface HelloService {void sayHelloWorld();
}
------------------------------------------------------
package learn.spring.aop;// 主业务实现类
public class HelloServiceImpl implements HelloService {@Overridepublic void sayHelloWorld() {System.out.println("hello world !");}
}

【切面逻辑接口】

package learn.spring.aop;/*** 切面逻辑的抽象接口,* 即在主业务之外的附加业务的抽象接口* */
public interface MethodInvocation {void invoke();
}

【整合主业务和附加业务,实现代理】

package learn.spring.aop;import java.lang.reflect.InvocationHandler;/*** 通知抽象接口,继承代理接口InvocationHandler
* 对于需要在执行主业务之前加通知的类,都需要实现该接口
* */
public interface Advice extends InvocationHandler {
}
--------------------------------------------------package learn.spring.aop;import java.lang.reflect.Method;/*** 通知逻辑具体实现类,* 需要在执行之前加通知的对象,作为构造器参数传入* */
public class BeforeAdvice implements Advice {// 主业务对象
private Object bean;
// 切面逻辑的对象,即在主业务之外的附加业务
private MethodInvocation methodInvocation;public BeforeAdvice(Object bean, MethodInvocation methodInvocation) {this.bean = bean;this.methodInvocation = methodInvocation;
}@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {// 在主业务执行先执行 MethodInvocation中的方法methodInvocation.invoke();// 执行主业务方法return method.invoke(bean,objects);
}

}

【测试环境】

package learn.spring.aop;import java.lang.reflect.Proxy;// 测试环境
public class Main {
public static void main(String[] args) {// 切面逻辑具体实现,这里使用的是java Lambda语法MethodInvocation logTask =() -> System.out.println("log task start");// 主业务实现HelloServiceImpl helloService = new HelloServiceImpl();// 创建切面,确定通知和切点Advice beforeAdvice = new BeforeAdvice(helloService,logTask);// 为目标对象生成代理HelloService proxy = (HelloService) Proxy.newProxyInstance(helloService.getClass().getClassLoader(),helloService.getClass().getInterfaces(),beforeAdvice);// 执行方法proxy.sayHelloWorld();}
}

【运行结果】

log task start
hello world !


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部