转载

为seed容器添加自动注入和AOP功能

大概在半年前写了一个简陋的ioc容器,并设想实现很多功能,不过之后因为各种原因(主要是懒( ̄▽ ̄)")闲置了。最近又重新将这个项目拾起,通过半个多月上班摸鱼时间更新了两个版本0.0.2(实现自动注入)和0.0.3(实现AOP),这篇博客就简单讲讲实现的一些过程,分享给大家。 源码地址

自动注入

大致流程图:

为seed容器添加自动注入和AOP功能
自动注入实现思路比较简单,在bean创建完成后查看有哪些需要注入的属性,利用beanPostProcessor来对bean进行处理,注入需要的属性。目前seed容器支持 @Autowired 注入当前bean中的类,以及 @Value 注入默认值和 seed.yml / seed.properties

(当前seed容器环境变量environment)中的属性(当前还不能够和spring一样支持复杂的方法等表达式)。主要功能其实就只有两块:

  1. 解析yml文件。
  2. 判断某个类是否符合注入需要(需要考虑到泛型)。

解析yml比较简单,由于yml文件很有特点。实现了 YmlProperties 用于读取yml文件(参考了 Properties 类)。实现HashTable来存储读取的属性值键值对。通过逐行读取文件,利用yml文件明显的特点:空格来表示所属分级,来判断出当前所属节点以及上级节点,将所有属性读取成 spring.aop (每一级用·分隔)格式的key,再利用 的分隔符来获取value。在seed容器初始化的时候将属性加入到容器中。

判断某个类的是否符合注入需求做起来还是走了一些弯路。其实实现起来也不难,首先需要注入的类一定同级类或者父类。只要找到容器中每个bean的实现或继承类所对应的泛型关系就可以了,通过两者对应的泛型关系来比较是否为相同的类。

AOP

AOP功能的实现也是依赖的BeanPostProcessor。主要功能点在创建jdk以及cglib代理类和切入点的读取处理。实现还是参考的spring,在创建代理类的时候利用拦截器链来在方法执行前后进行处理。不管是创建jdk的代理类还是创建cglib的代理类都会给处理器设置一个拦截器链,里面放置着匹配该类的拦截器,之后执行代理类方法的时候会匹配拦截器并逐一执行拦截器。

AOP抽象层的建模:

为seed容器添加自动注入和AOP功能

首先讲下创建代理类遇到的一些问题吧,JDK的代理类创建比较简单直接调用java的api就行。cglib就有些问题了,由于是直接创建的新类,所以如果原先的类如果没有无参的构造函数就需要调用有参构造函数,那么就要在创建代理对象时注入构造函数参数。我这边的处理方式是获取第一个构造函数传入的null参数。(其实和spring类似,由于spring自己实现了cglib,所以它在创建代理对象的时候不需要调用构造函数。)这边使用空参数并会有带来什么影响,因为最终调用原始方法的还是容器创建的对象,而不是代理对象。(这是在实现cglib的 MethodInterceptor 时处理的)。但是如果创建对象的构造函数对参数进行了判断并且抛异常,那创建代理对象就会出错,暂时还没有什么好的方案修复这一点,这也许就是spring使用自己的方式来实现cglib代理对象创建的原因吧。

在完成创建一个带有拦截器链的代理类之后遇到的问题就是如何匹配切点表达式。原先是想自己实现的,然而自己光是写匹配包名的表达式就花了好几个小时,于是果断放弃了(lll¬ω¬)。然后看了下spring是如何处理的,结果发现spring是使用了 aspectjweaver 这个jar包来处理的。之后便在aop模块导入了该jar包,参考了spring的使用方式,完成了对切面表达式的匹配。(所以@Aspect,@Before等注解都是使用的该jar包的)。

在完成编码之后赶紧测试了一下AOP的功能,发现完全OK,说实话还是有点小激动的,就目前使用来看和Spring的几乎没有什么差别,可以说完全契合麻雀虽小,五脏俱全这句话了。不过仔细想想在实现AOP的时候也没有太多难点,主要还是在思路上。我虽然实现方式有差别,但是大致思路还是模仿Spring,还是得感叹下Spring设计师超前的思路。

总结

完成了自动注入和AOP之后可以说seed作为一个bean容器来说已经比较完善了。之后应该会为该项目添加更多功能,例如数据库交互,web交互等等。该项目还是能够帮助我更好的理解框架内部的一些原理的。最后我希望自己的项目可以帮助到大家。(如果有志同道合的小伙伴我们可以一起写一些比较好玩的项目O(∩_∩)O)

原文  https://juejin.im/post/5d9ed5e1f265da5b707e95a0
正文到此结束
Loading...