相关名词的解释:通知定义了要织入目标对象的逻辑,以及执行时机。
Spring 中对应了 5 种不同类型的通知:
· 前置通知(Before):在目标方法执行前,执行通知
· 后置通知(After):在目标方法执行后,执行通知,此时不关系目标方法返回的结果是什么
· 返回通知(After-returning):在目标方法执行后,执行通知
· 异常通知(After-throwing):在目标方法抛出异常后执行通知
· 环绕通知(Around): 目标方法被通知包裹,通知在目标方法执行前和执行后都被会调用
切点(Pointcut):如果说通知定义了在何时执行通知,那么切点就定义了在何处执行通知。所以切点的作用就是通过匹配规则查找合适的连接点(Joinpoint),AOP 会在这些连接点上织入通知。
切面(Aspect):切面包含了通知和切点,通知和切点共同定义了切面是什么,在何时,何处执行切面逻辑。
实现步骤:
- 定义一个包含切面逻辑的对象,这里假设叫 logMethodInvocation
- 定义一个 Advice 对象(实现了 InvocationHandler 接口),并将上面的 logMethodInvocation 和 目标对象传入
- 将上面的 Adivce 对象和目标对象传给 JDK 动态代理方法,为目标对象生成代理
MethodInvocation 接口 // 实现类包含了切面逻辑,如上面的 logMethodInvocation
Advice 接口 // 继承了 InvocationHandler 接口
BeforeAdvice 类 // 实现了 Advice 接口,是一个前置通知
SimpleAOP 类 // 生成代理类
SimpleAOPTest // SimpleAOP 从测试类
HelloService 接口 // 目标对象接口
HelloServiceImpl // 目标对象
MethodInvocation 接口代码:
public interface MethodInvocation {
void invoke();
}
Advice 接口代码:
public interface Advice extends InvocationHandler {}
BeforeAdvice 实现代码:
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 proxy, Method method, Object[] args) throws Throwable {
// 在目标方法执行前调用通知
methodInvocation.invoke();
return method.invoke(bean, args);
}
}
SimpleAOP 实现代码:
public class SimpleAOP {
public static Object getProxy(Object bean, Advice advice) {
return Proxy.newProxyInstance(SimpleAOP.class.getClassLoader(),
bean.getClass().getInterfaces(), advice);
}
}
HelloService 接口,及其实现类代码:
public interface HelloService {
void sayHelloWorld();
}
public class HelloServiceImpl implements HelloService {
@Override
public void sayHelloWorld() {
System.out.println("hello world!");
}
}
SimpleAOPTest 代码:
public class SimpleAOPTest {
@Test
public void getProxy() throws Exception {
// 1. 创建一个 MethodInvocation 实现类
MethodInvocation logTask = () -> System.out.println("log task start");
HelloServiceImpl helloServiceImpl = new HelloServiceImpl();
// 2. 创建一个 Advice
Advice beforeAdvice = new BeforeAdvice(helloServiceImpl, logTask);
// 3. 为目标对象生成代理
HelloService helloServiceImplProxy = (HelloService) SimpleAOP.getProxy(helloServiceImpl,beforeAdvice);
helloServiceImplProxy.sayHelloWorld();
}
}