目录
-
环境准备
-
核心代码实现
-
测试
-
总结
环境准备
-
spring源码版本5.0.2
-
jdk 1.8
-
ide idea
-
在源码中创建子模块
友情提示:
我们1.0版本只需要实现前置,后置通知即可。由于ioc源码需要依赖spring 本身的,所以spring ioc源码有些地方需要改造下,实现方式上可能稍微有些不同,不过核心思想是一致的。后续手写ioc的时候将这块整合到一起去
核心代码实现
实现思路:
-
实现自己的注解 @Aspect @Before @After @EnableAopAspect
-
编写AopBeanPostProcessor 用于增强逻辑,将原生对象转换成代理对象返回给spring ioc
-
编写AopAnntationRegisterBeanDefinition用于向spring添加aop的后置处理器的前期工作
-
编写代理proxyFactory工厂,用于jdk和cglib不同的代理方式
-
编写MethodInterceptor对切面进行封装和执行
-
编写Advice用于抽象各种通知
-
改造spring源码context模块注册后置处理部分,我们自己的aop后置处理器由自己去实例化,即此PostProcessorRegistrationDelegate类registerBeanPostProcessors方法的改造
-
思路有了我们来做具体实现吧
在spring-context模块中新建自己的包结构如图:
- 来看下我们自己的@EnableAopAspect注解:
-
bd注册类和spring实现相同,没什么可看的,我们重点看下自己的aop后置处理器的具体实现逻辑:
public class AopBeanPostProcessor implements BeanPostProcessor {
private DefaultListableBeanFactory beanFactory;
public AopBeanPostProcessor(ConfigurableListableBeanFactory beanFactory){
this.beanFactory=(DefaultListableBeanFactory)beanFactory;
}
//重写postProcessAfterInitialization 在原生对象填充完属性执行完初始化方法后执行
//对原生对象进行转换操作
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//从beanDefinitionMap中通获取加了Aspect注解的的beanName
String[] beanNamesForAnnotation = beanFactory.getBeanNamesForAnnotation(Aspect.class);
//如果存在切面类,就进行转换,否则直接返回原生对象
if(beanNamesForAnnotation.length>0) {
//获取切面类,此工厂是我们通过构造方法传入的,我们基于spring ioc实现的aop
//所以有些地方需要改造.将DefaultListableBeanFactory传入到自己的定义的后置处理器重用于获取我们的切面类对象
Object target = beanFactory.getBean(beanNamesForAnnotation[0]);
Method[] declaredMethods = target.getClass().getDeclaredMethods();
//通过反射,循环切面类中的所有方法
for(Method method:declaredMethods){
//如果该方法上存在Before注解,获取其注解中的切面表达式,并判断当前原生对象是否需要进行增强
//即是否符合我们切面表达式
if(method.isAnnotationPresent(Before.class)) {
Before annotation = method.getAnnotation(Before.class);
String name = bean.getClass().getName();
String substring = annotation.value().substring(0, annotation.value().indexOf("("));
Pattern pattern = Pattern.compile(substring);
//如果符合切面表达式,将其转换为代理对象后返回给spring
if (pattern.matcher(name).matches()) {
MethodInterceptor methodInterceptor = new MethodInterceptor(method,
target, method.getParameters());
//3 实例化Advice
Advice advice = new MethodBeforeAdvice(methodInterceptor);
//将原生对象转换为代理对象
return ProxyFactory.getProxy(bean, advice).getProxy();
}
}
}
return bean;
}else {
return bean;
}
}
}
- 此处有个地方和spring不大一样,就是增加了构造方法,用于传递DefaultListableBeanFactory 从而来获取我们的切面类对象.还需改造的地方就是spring 注册后置处理时的源码,由于我们需要通过构造方法传递DefaultListableBeanFactory ,所以不能交由spring原生代码去实例化,所以我们增加如下代码:
注:上面都加了注释大家可以仔细看下后置处理器中的逻辑.
- 代理工厂我们实现的比较简单,不解析许多了,看下代码吧
- 我们的前置处理器中逻辑如下:
几处的核心代码基本都编写完了,下面我们来测试一把
测试
1,在Configuration类中引入自己的@EnableAopAspect注解即开启aop代理增强功能.
2,创建aop切面类,编写前置通知,以及横切逻辑的表达式
3,在main函数中启动spring容器,获取twoService对象实例.执行其query方法.
4,在切面类的前置通知方法处,打debug断点,执行看结果
执行完后,再看控制台结果
哎呦卧槽嘞,完美!!!
总结
至此我们1.0v aop实现完成,1.0v版本粗略实现了aop增强功能,并且支持jdk的动态代理,由于没有手工实现ioc部分,所以有些地方和spring实现方式不同,而且环绕通知并未实现,但是核心原理是一样的,我们后续在对1.0v进行改造升级2.0v,敬请期待。
个人公众号: