本文主要是介绍Groovy简介——编译时方法注入,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一般只用于编写一些插件或者模板方法的时候使用,编译后的代码还是会合成为静态代码,会比运行时的处理方式效率更高。需要根据AST来添加对应的语法树。Groovy编译器允许我们进入其变异截断,一窥其所处理的AST(Abstract Syntax Tree 抽象语法树)。
AST:
这就是生成的语法树:
Groovy支持开发者在任何阶段介入:初始化、解析、转换、语义分析、规范化、指令选择、class生成、输出和结束
AST在语义分析阶段之后生成,如果想使用信息更多的AST,可以再之后的阶段介入。
CodeCheckpackage CodeAnalysisimport org.codehaus.groovy.ast.ASTNodeimport org.codehaus.groovy.ast.ClassNodeimport org.codehaus.groovy.ast.ConstructorNodeimport org.codehaus.groovy.ast.FieldNodeimport org.codehaus.groovy.ast.GroovyClassVisitorimport org.codehaus.groovy.ast.MethodNodeimport org.codehaus.groovy.ast.PropertyNodeimport org.codehaus.groovy.control.CompilePhaseimport org.codehaus.groovy.control.SourceUnitimport org.codehaus.groovy.syntax.SyntaxExceptionimport org.codehaus.groovy.transform.ASTTransformationimport org.codehaus.groovy.transform.GroovyASTTransformation@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)class CodeCheck implements ASTTransformation {@Overridevoid visit(ASTNode[] astNodes, SourceUnit sourceUnit) {sourceUnit.ast.classes.each { classNode ->classNode.visitContents(new OurClassVisitor(sourceUnit))}}}class OurClassVisitor implements GroovyClassVisitor{SourceUnit sourceUnitOurClassVisitor(theSourceUnit){sourceUnit = theSourceUnit}@Overridevoid visitClass(ClassNode classNode) {}@Overridevoid visitConstructor(ConstructorNode constructorNode) {}private void reportError(message, lineNumber, columnNumber){sourceUnit.addError(new SyntaxException(message, lineNumber, columnNumber))}@Overridevoid visitMethod(MethodNode methodNode) {if (methodNode.name.size() == 1){reportError "Make method name descriptive, avoid single letter names",methodNode.lineNumber, methodNode.columnNumber}methodNode.parameters.each {parameter ->if (parameter.name.size() == 1){reportError "Single letter parameters are morally wrong!",parameter.lineNumber, parameter.columnNumber}}}@Overridevoid visitField(FieldNode fieldNode) {}@Overridevoid visitProperty(PropertyNode propertyNode) {}}
OurClassVisitor
package CodeAnalysisimport org.codehaus.groovy.ast.ASTNodeimport org.codehaus.groovy.ast.ClassNodeimport org.codehaus.groovy.ast.ConstructorNodeimport org.codehaus.groovy.ast.FieldNodeimport org.codehaus.groovy.ast.GroovyClassVisitorimport org.codehaus.groovy.ast.MethodNodeimport org.codehaus.groovy.ast.PropertyNodeimport org.codehaus.groovy.control.CompilePhaseimport org.codehaus.groovy.control.SourceUnitimport org.codehaus.groovy.syntax.SyntaxExceptionimport org.codehaus.groovy.transform.ASTTransformationimport org.codehaus.groovy.transform.GroovyASTTransformation@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)class CodeCheck implements ASTTransformation {@Overridevoid visit(ASTNode[] astNodes, SourceUnit sourceUnit) {sourceUnit.ast.classes.each { classNode ->classNode.visitContents(new OurClassVisitor(sourceUnit))}}}class OurClassVisitor implements GroovyClassVisitor{SourceUnit sourceUnitOurClassVisitor(theSourceUnit){sourceUnit = theSourceUnit}@Overridevoid visitClass(ClassNode classNode) {}@Overridevoid visitConstructor(ConstructorNode constructorNode) {}private void reportError(message, lineNumber, columnNumber){sourceUnit.addError(new SyntaxException(message, lineNumber, columnNumber))}@Overridevoid visitMethod(MethodNode methodNode) {if (methodNode.name.size() == 1){reportError "Make method name descriptive, avoid single letter names",methodNode.lineNumber, methodNode.columnNumber}methodNode.parameters.each {parameter ->if (parameter.name.size() == 1){reportError "Single letter parameters are morally wrong!",parameter.lineNumber, parameter.columnNumber}}}@Overridevoid visitField(FieldNode fieldNode) {}@Overridevoid visitProperty(PropertyNode propertyNode) {}}
配置文件
manifest/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
groovyc -classpath checkcode.jar smelly.groovy
groovyc -d classes CodeAnalysis/CodeCheck.groovy
jar -cf checkcode.jar -C classes CodeAnalysis -C manifest .
groovyc -classpath checkcode.jar smelly.groovy
新建InjectAudit.groovy
package AST.InterceptingCalls.com.aglledeveloperimport org.codehaus.groovy.ast.ASTNodeimport org.codehaus.groovy.ast.expr.ArgumentListExpressionimport org.codehaus.groovy.ast.expr.MethodCallExpressionimport org.codehaus.groovy.ast.expr.VariableExpressionimport org.codehaus.groovy.ast.stmt.ExpressionStatementimport org.codehaus.groovy.control.CompilePhaseimport org.codehaus.groovy.control.SourceUnitimport org.codehaus.groovy.transform.ASTTransformationimport org.codehaus.groovy.transform.GroovyASTTransformation@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)class InjectAudit implements ASTTransformation {@Overridevoid visit(ASTNode[] astNodes, SourceUnit sourceUnit) {def checkingAccountClassNode =astNodes[0].classes.find {it.name == 'AST.InterceptingCalls.CheckingAccount'}injectAuditMethod(checkingAccountClassNode)}static void injectAuditMethod(checkingAccountClassNode){def nonAuditMethods =checkingAccountClassNode?.methods.findAll{ it.name != 'audit'}nonAuditMethods?.each { injectMethodWithAudit(it) }}static void injectMethodWithAudit(methodNode){def callToAudit = new ExpressionStatement(new MethodCallExpression(new VariableExpression('this'),'audit',new ArgumentListExpression(methodNode.parameters)))methodNode.code.statements.add(0, callToAudit)}}
groovyc -d classes AST/InterceptingCalls/com/aglledeveloper/InjectAudit.groovy
jar -cf injectAudit.jar -C classes AST -C manifest .
生成对应的jar包
UsingCheckingAccount.groovypackage AST.InterceptingCallsclass CheckingAccount {def audit(amount) {if (amount > 10000){print "auditing ..."}}def deposit(amount){println "depositing ${amount} ..."}def withdraw(amount){println "withdrawing ${amount} ..."}}def account = new CheckingAccount()account.deposit(1000)account.deposit(12000)account.withdraw(11000)groovy src/groovy/AST/InterceptingCalls/UsingCheckingAccount.groovydepositing 1000 ...depositing 12000 ...withdrawing 11000 ...depositing 1000 ...auditing ...depositing 12000 ...auditing ...withdrawing 11000 ...
执行:
groovyc -d classes src/groovy/AST/InterceptingCalls/com/aglledeveloper/InjectAudit.groovy
jar -cf injectAudit.jar -C classes AST -C manifest .
groovy -classpath injectAudit.jar src/groovy/AST/InterceptingCalls/UsingCheckingAccount.groovy
结果:
depositing 1000 ...
auditing ...depositing 12000 ...
auditing ...withdrawing 11000 ...
*audit方法被应用在最前方,当对于10000的时候会自动审计
这篇关于Groovy简介——编译时方法注入的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!