EaseAgent provides a Matcher DSL (Domain Specific Language) to define a Points implementation which is used to define classes and their methods to be enhanced.
The design of this DSL is borrowed from ByteBuddy's class and method matching DSL design. Why not use ByteBuddy's DSL interface directly?
- Decouple from the ByteBuddy. Although ByteBuddy is an excellent byte code framework, we provide dedicated DSL to developers via abstracting it, can easily migrate to a more excellent framework in the future.
- Provide a cleaner and easier to use API for Agent scenarios. We have defined two matchers, there are
ClassMatcherandMethodMatcher.- The
ClassMatcheris dedicated to identify classes in which methods need to be enhanced are located. - The
MethodMatcheris dedicated to select methods located in the matched classes that need to be enhanced.
- The
As defined by the JVM Specification for the class structure and the status of the annotations commonly used in Java, the optional elements used for class matching contain:
- Class name.
- Implementing Interface name.
- Super class name.
- Modifier, access flags, public/private/abstract/final/synthetic, and so on.
- Annotation.
ClassMatcher definition is provided by com.megaease.easeagent.plugin.matcher.ClassMatcher.
Each condition in a matcher is a combination of logical AND.
private String name;
// setup the match type of name: className, super className, Interface Name or Annotation name.
private ClassMatch matchType;
private int modifier = Modifier.ACC_NONE;
private int notModifier = Modifier.ACC_NONE;There is a MethodMatcherBuilder class in com.megaease.easeagent.plugin.matcher.MethodMatcher, providing interface to build a ClassMatcher.
The following methods are common condition settings in class matching,
ClassMatcherBuilder hasSuperClass(String className)
ClassMatcherBuilder hasClassName(String className)
ClassMatcherBuilder hasAnnotation(String className)
ClassMatcherBuilder hasInterface(String className)
ClassMatcherBuilder isPublic();
ClassMatcherBuilder isPrivate();
ClassMatcherBuilder isAbstract();
ClassMatcherBuilder isInterface();
ClassMatcherBuilder notPrivate();
ClassMatcherBuilder notAbstract();
ClassMatcherBuilder notInterface();-
Logical AND
As mention above, each condition in a matcher is a combination oflogical AND.For example, the following ClassMatcher will match non-interface classes which inherit the
java.lang.Stringand implements thejava.lang.Comparableinterface.
ClassMatcher.builder()
.hasSuperClass("java.lang.String")
.hasInterface("java.lang.Comparable")
.notInterface()
.build()In addition, ClassMatcherBuilder provides and() method which will finish current builder and start a new ClassMatcherBuilder.
The previous builder will generate a Left ClassMatcher and the new builder will generate a 'Right ClassMatcher'.
The Left matcher and Right matcher will be combined into one AndClassMatcher, and a method will match as long as the Left or Right matcher is a match.
For example, the following matcher will match classes that inherit the easeagent.test.TestBaseClass class and exclude classes implementing "easeagent.test.InterfaceB" interface.
matcher = ClassMatcher.builder()
.hasSuperClass("easeagent.test.TestBaseClass")
.and()
.hasInterface("easeagent.test.InterfaceB")
.negate()
.build();-
Logical OR
ClassMatcherBuilderprovidesor()method which will finish current builder and start a newClassMatcherBuilder. The previous builder will generate aLeft ClassMatcherand the new builder will generate a 'Right ClassMatcher'. TheLeft matcherandRight matcherwill be combined into oneOrClassMatcher, and a class will match as long as theLeftorRightmatcher is a match.For example, the following ClassMatcher will match classes that implement the
javax.servlet.Filterinterface or inherit the "javax.servlet.http.HttpServlet" class.
public IClassMatcher getClassMatcher() {
return ClassMatcher.builder()
.hasInterface("javax.servlet.Filter")
.or()
.hasSuperClass("javax.servlet.http.HttpServlet")
.build();
}- Logical NOT
Thenegate()method ofClassMatcherBuilderusesNegateClassMatcherto wrap the original matcher generated by the builder when no such method is applied. When a class matches the original matcher, it is unmatched by the wrap matcher; conversely, it is matched.
For example, the following ClassMatcher will match classes that inherit theeaseagent.test.TestBaseClassclass, but exclude classes implementing "easeagent.test.InterfaceB" class.
matcher = ClassMatcher.builder()
.hasSuperClass("easeagent.test.TestBaseClass")
.and()
.hasInterface("easeagent.test.InterfaceB")
.negate()
.build();As defined by the JVM Specification for the method structure , the optional elements used for method matching contain:
- Method name.
- Modifier, public/private, and so on.
- Argument type, the full qualified class name of argument.
- ReturnType, the return type of method.
MethodMatcher definition is provided by com.megaease.easeagent.plugin.matcher.MethodMatcher.
Each condition in a matcher is a combination of logical AND.
// method name
private String name;
// the match type of method name: equals, startWith...
private StringMatch nameMatchType;
// ignored when with default value
private String returnType = null;
// types of method arguments
private String[] args;
private int argsLength = -1;
private int modifier = Modifier.ACC_NONE;
private int notModifier = Modifier.ACC_NONE;
// only match methods overridden form the ClassMatcher
private IClassMatcher overriddenFrom = null;
// assign a name for this method matcher
private String qualifier;There is a MethodMatcherBuilder class in com.megaease.easeagent.plugin.matcher.MethodMatcher, providing interface to build a MethodMatcher.
The following methods are common condition settings in method matching,
// method name condition
MethodMatcherBuilder named(String methodName);
MethodMatcherBuilder isConstruct();
MethodMatcherBuilder nameStartWith(String methodName);
MethodMatcherBuilder nameEndWith(String methodName);
MethodMatcherBuilder nameContains(String methodName);
// modifier condition
MethodMatcherBuilder isPublic();
MethodMatcherBuilder isPrivate();
MethodMatcherBuilder isAbstract();
MethodMatcherBuilder isStatic();
MethodMatcherBuilder notPublic();
MethodMatcherBuilder notPrivate();
MethodMatcherBuilder notAbstract();
MethodMatcherBuilder notStatic();
MethodMatcherBuilder returnType(String returnType);
// setup method arguments condition
MethodMatcherBuilder arg(int idx, String argType);
MethodMatcherBuilder argsLength(int length);
// setup method matcher name
MethodMatcherBuilder qualifier(String qualifier);
// only match methods overridden from matched classed through cMatcher
MethodMatcherBuilder isOverriddenFrom(IClassMatcher cMatcher);- Logical AND
As mention above, each condition in a matcher is a combination oflogical AND.
For example, the following MethodMatcher will match the method which namedgetConnectionand with a return type ofjava.sql.Connection.
MethodMatcher.builder()
.named("getConnection")
.returnType("java.sql.Connection")
.build())In addition, MethodMatcherBuilder provides and() method which will finish current builder and start a new MethodMatcherBuilder.
The previous builder will generate a Left MethodMatcher and the new builder will generate a 'Right MethodMatcher'.
The Left matcher and Right matcher will be combined into one AndMethodMatcher, and a method will match as long as the Left or Right matcher is a match.
For example, the following matcher will match public methods that named write and exclude that with void return type.
MethodMatcher.builder()
.named("write")
.argsLength(1)
.isPublic()
.and()
.returnType("void")
.negate()
.build()-
Logical OR
MethodMatcherBuilderprovidesor()method which will finish current builder and start a newMethodMatcherBuilder.
The previous builder will generate aLeft MethodMatcherand the new builder will generate a 'Right MethodMatcher'. TheLeft matcherandRight matcherwill be combined into oneOrMethodMatcher, and a method will match as long as theLeftorRightmatcher is a match.For example, the following MethodMatcher will match method that named
addBatchor named "clearBatch" and then give thisOrClassMatchermatcher a namebatch.
MethodMatcher.builder()
.named("addBatch")
.or()
.named("clearBatch")
.qualifier("batch")
.build()-
Logical NOT
Thenegate()method ofMethodMatcherBuilderusesNegateMethodMatcherto wrap the original matcher generated by builder when no such method is applied.
When a method matches the original matcher, it is unmatched by the wrap matcher; conversely, it is matched.For example, the following matcher will match public methods that named
writeand exclude that withvoidreturn type.
MethodMatcher.builder()
.named("write")
.argsLength(1)
.isPublic()
.and()
.returnType("void")
.negate()
.build()Ends.