ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

快速看懂 Bearcat 动态代理:AOP 核心实现原理全解析

快速看懂 Bearcat 动态代理:AOP 核心实现原理全解析 快速看懂 Bearcat 动态代理AOP 核心实现原理全解析【免费下载链接】bearcatpowered by enhanced javaScript objects项目地址: https://gitcode.com/gh_mirrors/be/bearcatBearcat 是一款面向 Node.js 的轻量级 IoC 容器它的 AOP面向切面编程能力完全由两个动态代理类驱动DynamicAopProxy切面代理与DynamicMetaProxy元数据懒加载代理。本文带你快速拆解这两个类的实现内幕理解 Bearcat 如何在不修改业务代码的前提下织入 before/around/after 通知并实现 Bean 的延迟实例化。一、先搞懂整体架构谁在生成代理Bearcat 的 AOP 模块位于 lib/aop/ 目录核心文件分工如下模块职责lib/aop/autoproxy/autoProxyCreator.js在 Bean 初始化后自动判断是否需要织入切面是代理的总开关lib/aop/framework/proxyFactory.js代理工厂负责组装并产出DynamicAopProxy实例lib/aop/framework/dynamicAopProxy.js切面代理执行 before/around/after 通知链lib/aop/framework/dynamicMetaProxy.js元数据代理实现 Bean 的懒加载lib/aop/pointcut.js切点用正则表达式匹配Bean名.方法名lib/aop/advisor.js增强器绑定切点、通知方法与执行顺序整个流程可以概括为一句话AutoProxyCreator 在 Bean 初始化后扫描所有切面Aspect把能匹配的 Advisor 收集起来交给 ProxyFactory 生成一个外壳对象替换原 Bean之后所有方法调用都被外壳拦截按顺序执行切面逻辑。二、DynamicAopProxy 内幕切面代理如何织入通知1. 动态生成接口方法DynamicAopProxy的构造器只做两件事保存advised即 ProxyFactory 实例它继承了 lib/aop/framework/advisedSupport.js然后调用dyInit()。dyInit()的精髓在于运行时动态挂载方法它遍历advised.getInterfaces()拿到目标 Bean 的全部方法名然后用闭包为每个方法在代理实例上挂一个同名函数self[interface] function() { arguments Array.prototype.slice.apply(arguments); return self.dyInvoke(interface, arguments); }也就是说代理对象长得和原 Bean 一模一样但每个方法调用实际都转交给dyInvoke()。这里还有一个细节checkFuncName会检查方法名是否与dyInit、dyInvoke等内部保留方法重名重名则报错跳过避免代理方法覆盖框架自身的调度逻辑——这是一个容易被忽视的坑。2. dyInvoke一次调用的完整生命周期dyInvoke(method, args)是所有代理调用的中枢它的执行步骤是补齐回调参数如果调用方最后一个参数不是函数说明目标方法不依赖 callback代理会临时补一个空函数保证后续通知链能统一用 callback 风格流转结果取出目标 Bean通过targetSource.getTarget()拿到真正被代理的对象TargetSource 定义在 lib/aop/targetSource.js同时持有 beanName 和 target 两个引用拉取三类通知按before、around、after三种类型分别调用advised.getInterceptionAdvice()得到三类 Advisor 列表依次执行通知链先跑 before 链doInvokeAdvisorsBefore若有 around 通知走doInvokeAdvisorsAround否则直接调用target[method]目标方法执行完毕后把结果传给 after 链doInvokeAdvisorsAfter。3. 通知链的递归推进与两种调用模式before 和 after 链的实现几乎一致都用一个next(err)递归函数推进var next function(err) { if (err || index advisors.length) { return cb(err); } var advisor advisors[index]; var advise advisor.getAdvice(); var aspectBean advisor.getBean(); // runtime 为 true 时透传业务参数否则只传 next if (advisor.isRuntime()) { args.push(_next); aspectBean[advise].apply(aspectBean, args); } else { aspectBeanadvise; } }这里有几个值得注意的设计runtime 标志Advisor 上的isRuntime()决定通知函数是否能看到业务参数。为true时把原始 args 透传给切面方法为false时切面方法只能拿到next回调实现更纯粹的流程控制型切面错误短路before 链中任何一个通知返回err整条链立即终止并直接把错误抛给调用方目标方法根本不会执行around 的洋葱契约doInvokeAdvisorsAround只取advisors[0]以(target, method, args, cb)的形式调用它切面必须自己决定何时回调cb——回调即放行到 after 链和原始调用结果。4. methodCache切点匹配的缓存优化每次dyInvoke都会查询三类通知列表但真正的筛选工作在 lib/aop/framework/advisedSupport.js 中做了缓存getInterceptionAdvice()以方法名_通知类型为 key 缓存结果只有首次调用才遍历全部 Advisor、用切点正则去匹配beanName.method并按order排序。这意味着热路径上的通知列表是一次计算、永久复用的避免了每次调用都重复做正则匹配。三、DynamicMetaProxy 内幕懒加载代理的极简实现如果说DynamicAopProxy负责切面织入那么DynamicMetaProxy负责的是延迟实例化。两者结构相似都是动态挂方法但目标完全不同。1. 使用场景当你通过bearcat.getBeanByMeta()或bearcat.getBeanByFunc()获取 Bean 时入口见 lib/context/applicationContext.js 中的getBeanByMeta容器并不会立即创建Bean 实例而是走到 lib/beans/beanFactory.js 的doGetBeanProxy()var dynamicMetaProxy new DynamicMetaProxy(); dynamicMetaProxy[beanDefinition] beanDefinition; dynamicMetaProxy[beanFactory] this; dynamicMetaProxy[args] arguments; dynamicMetaProxy._dyInit(); return dynamicMetaProxy;它先把 beanDefinition、beanFactory 和调用参数寄放在代理对象上然后返回这个空壳。2. _dyInit从构造函数反查方法列表DynamicMetaProxy没有现成的接口列表它从beanDefinition.getFunc()拿到 Bean 的构造函数遍历其prototype上所有函数同样用闭包挂成代理方法。这与DynamicAopProxy的思路一脉相承也保留了同样的方法名冲突检查_dyInit、_dyInvoke、_getBean为保留名。3. _getBean首次调用才真正创建 BeanDynamicMetaProxy.prototype._getBean function() { var args this.args; var beanFactory this.beanFactory; if (!this.target) { this.target beanFactory.getBean.apply(beanFactory, args); } return this.target; }每次代理方法被调用时_dyInvoke都会先经过_getBean()只有第一次调用才触发beanFactory.getBean()真正创建实例之后直接复用缓存的this.target。这样对于注册了但未必使用的 Bean容器可以完全推迟其构造、依赖注入和 init 方法执行的时机——这就是 Bearcat 懒加载机制的本质用一层空壳代理换取实例化时机的完全可控。四、两个代理的对比总结维度DynamicAopProxyDynamicMetaProxy职责切面织入before/around/afterBean 懒加载接口来源目标对象实例的方法列表构造函数的 prototype内部方法dyInit/dyInvoke/doInvokeAdvisorsXxx_dyInit/_dyInvoke/_getBean目标获取通过 TargetSource 直接取现成实例首次调用时才调getBean创建触发时机AutoProxyCreator 在 Bean 初始化后替换getBeanByMeta/getBeanByFunc时返回方法名冲突处理与 5 个调度方法重名则拒绝挂接与 3 个调度方法重名则拒绝挂接可以这样理解ProxyFactory DynamicAopProxy 是横切能力的执行器AutoProxyCreator 是自动装配器而 DynamicMetaProxy 是延迟加载的占位符。三者共同构成了 Bearcat 声明式 AOP 的完整闭环——你在切面 Bean 里声明切点表达式格式为通知类型:正则由 lib/aop/pointcut.js 解析容器就负责把日志、鉴权、事务之类的通用逻辑无侵入地套进目标方法。五、上手建议与注意事项方法名别撞车如果你的 Bean 恰好定义了dyInvoke、doInvokeAdvisorsBefore、_getBean这类名字代理会拒绝挂载并打印错误日志改名即可通知顺序由 order 决定Advisor 支持order排序切点匹配后统一按compareByOrder排序需要控制执行先后时记得显式设置切点表达式是正则匹配对象是beanName.methodName字符串写法如before:car\.drive$建议写精确些避免误伤同名方法around 通知务必回调around 切面拿到cb后如果忘记调用整个调用链会永远挂起这是使用 around 通知时最典型的错误。想进一步动手验证可以运行仓库中现成的 AOP 示例 examples/aop/ 与 examples/aop_annotation/配合 test/aop/ 下的测试用例如 test/aop/framework/dynamicAopProxy.js、test/aop/framework/dynamicMetaProxy.js观察每个阶段的执行日志比纯读源码更能建立直观感受。【免费下载链接】bearcatpowered by enhanced javaScript objects项目地址: https://gitcode.com/gh_mirrors/be/bearcat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表