详解使用抽象语法树AST实现一个AOP切面逻辑
作者:头疼脑胀的代码搬运工
这篇文章主要为大家介绍了使用抽象语法树AST实现一个AOP切面逻辑的简单方法,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
开篇
AST 功能很灵活,可以通过改变一些自定义结构便可以输入自定义的功能,下面简单的展示下如何利用抽象语法树AST实现一个AOP切面逻辑
一、实现目的
将 js 文件下的全部方法添加一个开始事件和一个结束事件并传入方法名,以便监听某个方法的调用。
1、work.js
function eat(){ let food = "apple" let time = '2小时' return '吃' + food + '用时' + time } function work(){ } console.log(eat())
控制台打印
可以看到,输入了执行 eat 方法的开始事件及结束事件。其实,这个过程就是 work.js 转成语法树后,对方法节点进行了处理,添加了两个切面方法:start 和 end 事件。
2、aop.js
这个文件用来声明 start 和 end 事件。
function start(funcName){ console.log('开始执行:' + funcName + '方法') } function end(funcName){ console.log('执行结束:' + funcName + '方法') }
二、利用语法树添加切面事件
//文件操作工具 const fs = require('fs'); //JS代码转语法树工具 const parser = require('./babel-core/node_modules/babylon'); //语法树遍历工具 const traverse = require('./babel-core/node_modules/babel-traverse'); //语法树转JS代码工具 const generator = require('./babel-core/node_modules/babel-generator'); //声明语法树类型工具 const t = require('./babel-core/node_modules/babel-types'); //获取aop代码 let aop = fs.readFileSync('./aop.js','utf-8') //获取需要添加切面的代码 let content = fs.readFileSync('./work.js','utf-8') //将需要添加切面的代码转换为语法树 let ast = parser.parse(content,{ sourceType:'script' }) //遍历指定语法树时操作项(这里遍历的指定节点是FunctionDeclaration方法) const visitor = { FunctionDeclaration:({ node }) => { //获取每个方法体里面的节点 let funcBody = node.body.body //创建一个名为start,参数为当前方法名的执行节点,后面的参数为创建一个名为start方法的参数 let start = t.callExpression(t.identifier('start'), [t.identifier(`"${node.id.name}"`)]) //添加到方法的最前面 funcBody.unshift(start) //判断最后一个节点是不是return,方式结束事件调用节点在最后无法调用。 let lastNode = funcBody.slice(-1) //创建一个名为end,参数为当前方法名的执行节点,后面的参数为end方法的参数 let end = t.callExpression(t.identifier('end'), [t.identifier(`"${node.id.name}"`)]) //设定end节点添加的位置 let insertEndEventPosition = (funcBody.length) if(lastNode[0].type == 'ReturnStatement'){ //放在return节点的前面 insertEndEventPosition -= 1 } //添加end节点 funcBody.splice(insertEndEventPosition,0,end) } } //开始遍历操作语法树 traverse.default(ast,visitor) //将处理完的语法树再次转换为JS代码 let codeResult = generator.default(ast) //这里需要添加aop里面的两个切面事件到最终的JS代码里。 let outFileCode = aop + '\n\n' + codeResult.code // 写入文件操作 fs.mkdir('cache',(err)=>{ if(!err){ fs.writeFile('cache/main.js',outFileCode,(err)=>{ if(!err){ console.log('文件创建完成') } }) } })
上面的代码执行完后,看下 main.js 生成的代码内容。
function start(funcName){ console.log('开始执行:' + funcName + '方法') } function end(funcName){ console.log('执行结束:' + funcName + '方法') } function eat() { start("eat") let food = "apple"; let time = '2小时'; end("eat") return '吃' + food + '用时' + time; } function work() { start("work") end("work") } console.log(eat());
相比较之前的 work.js 文件,添加了两个切面方法的同时,保证每个方法都添加了 start 及 end 事件。这样再调用的时候便可直接在执行前后操作其他任务了。
三、总结与思考
其实上面的操作人工也可以直接操作,但是,将一切重复单一的工作交给程序,这或许是代码存在的真正意义。一个简单的例子或许能打开一个新的世界,虽然,每一行代码都是独一无二的,但是如果追本溯源,最初并且真切的思想却是相通的
以上就是详解使用抽象语法树AST实现一个AOP切面逻辑的详细内容,更多关于AST抽象语法树实现AOP的资料请关注脚本之家其它相关文章!