手工实现AOP核心源码之aop1.0版

目录

  1. 环境准备

  2. 核心代码实现

  3. 测试

  4. 总结

环境准备

  1. spring源码版本5.0.2

  2. jdk 1.8

  3. ide  idea

  4. 在源码中创建子模块

     

友情提示: 

            我们1.0版本只需要实现前置,后置通知即可。由于ioc源码需要依赖spring 本身的,所以spring ioc源码有些地方需要改造下,实现方式上可能稍微有些不同,不过核心思想是一致的。后续手写ioc的时候将这块整合到一起去

核心代码实现

实现思路:

  1. 实现自己的注解  @Aspect   @Before  @After  @EnableAopAspect 

  2. 编写AopBeanPostProcessor 用于增强逻辑,将原生对象转换成代理对象返回给spring ioc

  3. 编写AopAnntationRegisterBeanDefinition用于向spring添加aop的后置处理器的前期工作

  4. 编写代理proxyFactory工厂,用于jdk和cglib不同的代理方式

  5. 编写MethodInterceptor对切面进行封装和执行

  6. 编写Advice用于抽象各种通知

  7. 改造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,敬请期待。

 

个人公众号:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值