导语:由于本人是某大学网络中心java组组长,在和学弟学妹们传授完spring的相关知识之后,看到一脸茫然的他们,仿佛看到自己大一的时候,所以为了帮助学弟学妹更好的了解IOC以及 AOP 两大原理,并且帮助自己更加深层次的学习spring,决定自己简单的仿写这两个功能。这篇是AOP,上一篇是IOC。
源码已在github开源:Github地址
百度云 提取码:zanx
1.前序
由于上次仿写Spring IOC之后,帮学弟学妹讲了一下,收获赞许,说这样一下就好理解多了,于是乎Spring仿写第二弹来了,仿写Spring AOP。
当然:仿写之前还是需要对AOP的原理、机制有一定的了解,比如说代理模式呀,切点呀,切面呀等前缀知识
2.前缀知识
通知(Advice):主要是用来定义方法增强执行的时机以及如何增强
通知类型:
- 前置通知(Before):在目标方法执行前,执行通知
- 后置通知(After):在目标方法执行后,执行通知,此时不关系目标方法返回的结果是什么
- 返回通知(After-returning):在目标方法执行后,执行通知
- 异常通知(After-throwing):在目标方法抛出异常后执行通知
- 环绕通知(Around): 目标方法被通知包裹,通知在目标方法执行前和执行后都被会调用
切点(Pointcut)
切点的作用主要是告诉系统我的那些方法需要增强;
通过匹配规则查找合适的连接点(Joinpoint),AOP 会在这些连接点上织入通知
切面(Aspect)
切面其实算是一个概念,他其实就是切点+通知的组合
3.实现步骤
(1) 确定都有哪些角色
- 目标对象(被代理对象)
- 目标对象接口(被代理对象实现的接口)
- AOP类(通过代理创建代理对象的主战场)
- 当然还有一些辅助类下面会一一解释
(2)代码实现(所有代码咋EasyAOP包下)
首先:得有一个目标对象实现的接口,这个接口具有需要增强的方法
/**
* @description: 目标对象实现的接口
* @author:
* @date: 2021/3/12 16:49
**/
public interface Study {
/**
* 阅读方法
*/
void read();
}
然后就是目标对象,也就是被代理对象
/**
* @description: 目标对象学生类,实现了学习接口
* @author:
* @date: 2021/3/12 16:52
**/
public class Student implements Study{
@Override
public void read() {
System.out.println("具有汉语阅读的能力!");
}
}
然后我们需要定义一个接口,接口中的方法提供增强逻辑,也就是切面逻辑
/**
* @description: 切面增强逻辑接口,是一个函数式接口
* @author:
* @date: 2021/3/12 16:55
**/
public interface MethodInvocation {
void invoke();
}
有了切面逻辑之后,我们就得定义通知接口,这里继承了InvocationHandler接口,因为我们使用的是jdk的动态代理形式
/**
* @description: 通知接口
* @author:
* @date: 2021/3/12 16:58
**/
public interface Advice extends InvocationHandler {}
然后就是简单定义具体的某种通知类型,这里我定义的是后置通知类型
/**
* @description: 后置通知类,实现通知接口,其他通知也和这个差不多
* @author:
* @date: 2021/3/12 16:59
**/
public class AfterAdvice implements Advice{
/**
* 目标对象
*/
private Object bean;
/**
* 切面逻辑
*/
private MethodInvocation methodInvocation;
public AfterAdvice(Object bean,MethodInvocation methodInvocation) {
this.bean = bean;
this.methodInvocation = methodInvocation;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method.invoke就是代理对象调用目标对象的read方法
Object result = method.invoke(bean,args);
//在目标方法之后调用通知
methodInvocation.invoke();
return result;
}
}
最后当然就是我们的主角EasyAop类了
/**
* @description: AOP类,主要作用是获取代理对象
* @author:
* @date: 2021/3/12 17:09
**/
public class EasyAop {
public static Object getProxy(Object bean,Advice advice){
return Proxy.newProxyInstance(EasyAop.class.getClassLoader(),bean.getClass().getInterfaces(),advice);
}
}
来个小小的单元测试吧
/**
* @description: Aop单元测试类
* @author:
* @date: 2021/3/12 17:14
**/
public class EasyAopTest {
@Test
public void getProxy(){
//目标对象
Student student = new Student();
//切面逻辑对象,
MethodInvocation strentMethod = () -> {
//在这里书写增强逻辑,因为MethodInvocation是函数式接口,所以用来lambda表达式
System.out.println("阅读之后,具有理解的能力");
};
//后置通知对象
AfterAdvice afterAdvice = new AfterAdvice(student, strentMethod);
//生成代理对象
Study proxy = (Study) EasyAop.getProxy(student, afterAdvice);
proxy.read();
}
}
来看看结果
4.小结
看看上面的结果,perfect! 这次AOP的仿写也是实现的比较简单,当然其中的思路还是对的,在动手之前需要先去了解代理模式,然后手动写写JDK的动态代理,再来实现AOP就会简单很多。哈哈,又是一次勇敢的尝试,嗯。。。好像还有点小上瘾,下次该仿写哪个小玩意呢?[坏笑]