我们在使用Spring AOP时,在切面类上配置一个注解,再配置一个表达式,就实现了类或者方法的切面,Spring 又是如何实现的呢?根据我对源码的理解,Spring 先通过ASM包访问类字节码表,得到类注解及方法注解,在每个bean的实例化过程中,获取所有配置了@Aspect注解的类,获取类中所有方法,如果方法中配置了如@Before, @Around, @After, @AfterReturning, @AfterThrowing, @Pointcut,等注解,解析注解上的表达式,根据表达式和当前要初始化类方法进行匹配,匹配正确,生成相应的代理方法或对象。这是切面实现的大概流程,但是整个过程太复杂,今天,我们只针对其中一个点进行分析,也就是如何解析切点表达式。如何从ASM中获取类和方法的注解,在前面的博客中己经分析过了,对于如何匹配,如何生成代理 ,以及生成代理之后,如何调用。在后面的博客中,再来详述。先看示例如下:
AspectJTest
@Aspect @Configuration public class AspectJTest { @Before("execution(* com.spring_101_200.test_111_120.test_117_excution.excution1.*.*(..))") public void beforeTest() { System.out.println("beforeTest"); } }
今天我们主要分析Spring 是如何解析 execution(* com.spring_101_200.test_111_120.test_117_excution.excution1..(…)) 表达式。
MyService.java
@Service public class MyService { public void service() { System.out.println("serivce"); } }
spring_117_excution1.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <context:component-scan base-package="com.spring_101_200.test_111_120.test_117_excution.excution1"></context:component-scan> </beans>
测试:
public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring_101_200/config_111_120/spring117_excution/spring_117_excution1.xml"); MyService myService = ac.getBean(MyService.class); myService.service(); }
结果输出:
PointcutParser.java
protected Pointcut resolvePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters) { try { //创建PatternParser解析类,并在构造函数中分词 PatternParser parser = new PatternParser(expression); parser.setPointcutDesignatorHandlers(pointcutDesignators, world); Pointcut pc = parser.parsePointcut(); validateAgainstSupportedPrimitives(pc, expression); IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope), formalParameters); pc = pc.resolve(resolutionScope); return pc; } catch (ParserException pEx) { throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx)); } }
Spring 所有解析切点表达式方法都在PatternParser类中,在PatternParser的构造函数中,实现对表达式的分词。下面,我们来看看,Spring是如何分词的。
PatternParser.java
public PatternParser(String data) { //在makeTokenSource方法中实现分词,分词得到tokenSource数组 this(BasicTokenSource.makeTokenSource(data, null)); }
BasicTokenSource.java
public static ITokenSource makeTokenSource(String input, ISourceContext context) { //将表达式转化为字符数组 char[] chars = input.toCharArray(); int i = 0; //创建存储分词的集合 List tokens = new ArrayList(); while (i < chars.length) { char ch = chars[i++]; switch(ch) { case ' ': case '\t': case '\n': case '\r': continue;//如果是空格, '\t','\n','\r' ,直接跳过 case '*': case '(': case ')': case '+': case '[': case ']': case ',': case '!': case ':': case '@': case '<': case '>': case '=': case '?': //如果是 *...,创建BasicToken对象,将字符转化成字符串,保存当前字符所在表达式的开始位置和结束位置 tokens.add(BasicToken.makeOperator(makeString(ch), i-1, i-1)); continue; case '.': //如果读取到 . ,那么再向后读取两个字符 if ((i+2)<=chars.length) { char nextChar1 = chars[i]; char nextChar2 = chars[i+1]; //如果是'...' ,那么 ch==chars[i+1]==chars[i+2] == '.',保存字符串值为"..." 的BasicToken对象,并且 // isIdentifier属性为true,同样保存字符串所在表达式的位置,并且字符数组下标向后移到两位 if (ch==nextChar1 && ch==nextChar2) { tokens.add(BasicToken.makeIdentifier("...",i-1,i+1)); i=i+2; } else { //保存字符串值为. 的BasicToken对象 tokens.add(BasicToken.makeOperator(makeString(ch), i-1, i-1)); } } else { //如果字符串的后缀是 .a ,i+2 > chars.length,因此进入下面的执行 tokens.add(BasicToken.makeOperator(makeString(ch), i-1, i-1)); } continue; case '&': if ((i+1) <= chars.length && chars[i] != '&') { //如果读取读取到字符是&,但后面的第一个字符不是&,直接保存为字符串值为&的BasicToken对象 tokens.add(BasicToken.makeOperator(makeString(ch),i-1,i-1)); continue; } case '|': if (i == chars.length) { // 如果 & 或 |不是成对出现,将抛出异常 throw new BCException("bad " + ch); } char nextChar = chars[i++]; if (nextChar == ch) { // 如果 & 或 | 成对出现,&& 或 || ,将保存字符串为 && 的BasicToken对象,否则,抛出异常 tokens.add(BasicToken.makeOperator(makeString(ch, 2), i-2, i-1)); } else { throw new RuntimeException("bad " + ch); } continue; case '\"'://如果读取到" ,则截取""之间的值,保存为BasicToken对象 int start0 = i-1; while (i < chars.length && !(chars[i]=='\"')) i++; i += 1; tokens.add(BasicToken.makeLiteral(new String(chars, start0+1, i-start0-2), "string", start0, i-1)); continue; default: int start = i-1; //否则,只要是java字符,将一直读取 while (i < chars.length && Character.isJavaIdentifierPart(chars[i])) { i++; } tokens.add(BasicToken.makeIdentifier(new String(chars, start, i-start), start, i-1)); } } return new BasicTokenSource((IToken[])tokens.toArray(new IToken[tokens.size()]), context); }
其实,上面的这段分词的主要操作,去掉字符串之间的空格,Tab,回车,将字符串以 . [ ) { && || 分隔。
Token中有如下字段 :
String value;
boolean isIdentifier; //表示是一个字符串
int start;
int end;
例如:execution(* com.“spring_101_200”..(…) && xxx) ,分词得到
得到tokens :
[
execution true 0:8,
‘(’ false 9:9,
‘’ false 10:10,
com true 12:14,
‘.’ false 15:15,
‘spring_101_200’ false 16:31,
‘.’ false 32:32,
'’ false 33:33,
‘.’ false 34:34,
‘*’ false 35:35,
‘(’ false 36:36,
‘.’ false 37:37,
‘.’ false 38:38,
‘)’ false 39:39,
‘&&’ false 41:42,
xxx true 44:46,
‘)’ false 47:47
]
接下来,我们来具体分析一下,如何得到Pointcut对象
PatternParser.java
public Pointcut parsePointcut() { Pointcut p = parseAtomicPointcut(); if (maybeEat("&&")) { //如果 tokenSource 中还有未吃掉完的字符,且是&&,继续进行! 或者非&解析 p = new AndPointcut(p, parseNotOrPointcut()); } if (maybeEat("||")) { //如果 tokenSource 中还有未吃掉完的字符,且是|| ,继续 pointCut 解析 p = new OrPointcut(p, parsePointcut()); } return p; } private Pointcut parseAtomicPointcut() { if (maybeEat("!")) { //如果分词数组中当前解析元素是!,则进行 not PointCut 相关解析 int startPos = tokenSource.peek(-1).getStart(); Pointcut p = new NotPointcut(parseAtomicPointcut(), startPos); return p; } if (maybeEat("(")) { //如果当前解析到的是括号,去掉括号 Pointcut p = parsePointcut(); eat(")"); return p; } //如果当前解析到@,则进行注解相关解析 if (maybeEat("@")) { int startPos = tokenSource.peek().getStart(); Pointcut p = parseAnnotationPointcut(); int endPos = tokenSource.peek(-1).getEnd(); p.setLocation(sourceContext, startPos, endPos); return p; } int startPos = tokenSource.peek().getStart(); //否则,进行单个 PointCut 解析 Pointcut p = parseSinglePointcut(); int endPos = tokenSource.peek(-1).getEnd(); p.setLocation(sourceContext, startPos, endPos); return p; }
public Pointcut parseSinglePointcut() { int start = tokenSource.getIndex(); IToken t = tokenSource.peek(); Pointcut p = t.maybeGetParsedPointcut(); if (p != null) { tokenSource.next(); return p; } //取出分词数组当前所在的字符串 String kind = parseIdentifier(); if (kind.equals("execution") || kind.equals("call") || kind.equals("get") || kind.equals("set")) { //分词数组当前所在位置的字符串是,execution,call,get,set p = parseKindedPointcut(kind); } else if (kind.equals("args")) { //如:@Pointcut("args(java.io.Serializable,..)") p = parseArgsPointcut(); } else if (kind.equals("this")) { //如:@Pointcut("this(com.spring_1_100.test_61_70.test64_2.UserService)") p = parseThisOrTargetPointcut(kind); } else if (kind.equals("target")) { //如:@Pointcut("target(com.spring_1_100.test_61_70.test64_2.UserService)") p = parseThisOrTargetPointcut(kind); } else if (kind.equals("within")) { //如:@Pointcut("within(com..test64_2..UserService+)") p = parseWithinPointcut(); } else if (kind.equals("withincode")) { p = parseWithinCodePointcut(); } else if (kind.equals("cflow")) { p = parseCflowPointcut(false); } else if (kind.equals("cflowbelow")) { p = parseCflowPointcut(true); } else if (kind.equals("adviceexecution")) { eat("("); eat(")"); p = new KindedPointcut(Shadow.AdviceExecution, new SignaturePattern(Member.ADVICE, ModifiersPattern.ANY, TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY, AnnotationTypePattern.ANY)); } else if (kind.equals("handler")) { eat("("); TypePattern typePat = parseTypePattern(false, false); eat(")"); p = new HandlerPointcut(typePat); } else if (kind.equals("lock") || kind.equals("unlock")) { p = parseMonitorPointcut(kind); } else if (kind.equals("initialization")) { eat("("); SignaturePattern sig = parseConstructorSignaturePattern(); eat(")"); p = new KindedPointcut(Shadow.Initialization, sig); } else if (kind.equals("staticinitialization")) { eat("("); TypePattern typePat = parseTypePattern(false, false); eat(")"); p = new KindedPointcut(Shadow.StaticInitialization, new SignaturePattern(Member.STATIC_INITIALIZATION, ModifiersPattern.ANY, TypePattern.ANY, typePat, NamePattern.ANY, TypePatternList.EMPTY, ThrowsPattern.ANY, AnnotationTypePattern.ANY)); } else if (kind.equals("preinitialization")) { eat("("); SignaturePattern sig = parseConstructorSignaturePattern(); eat(")"); p = new KindedPointcut(Shadow.PreInitialization, sig); } else if (kind.equals("if")) { // - annotation style only allows if(), if(true) or if(false) // - if() means the body of the annotated method represents the if expression // - anything else is an error because code cannot be put into the if() // - code style will already have been processed and the call to maybeGetParsedPointcut() // at the top of this method will have succeeded. eat("("); if (maybeEatIdentifier("true")) { eat(")"); p = new IfPointcut.IfTruePointcut(); } else if (maybeEatIdentifier("false")) { eat(")"); p = new IfPointcut.IfFalsePointcut(); } else { if (!maybeEat(")")) { throw new ParserException( "in annotation style, if(...) pointcuts cannot contain code. Use if() and put the code in the annotated method", t); } // TODO - Alex has some token stuff going on here to get a readable name in place of ""... p = new IfPointcut(""); } } else { boolean matchedByExtensionDesignator = false; // see if a registered handler wants to parse it, otherwise // treat as a reference pointcut for (PointcutDesignatorHandler pcd : pointcutDesignatorHandlers) { if (pcd.getDesignatorName().equals(kind)) { p = parseDesignatorPointcut(pcd); matchedByExtensionDesignator = true; } } if (!matchedByExtensionDesignator) { tokenSource.setIndex(start); p = parseReferencePointcut(); } } return p; }
private KindedPointcut parseKindedPointcut(String kind) { //越过当前位置后的( eat("("); SignaturePattern sig; Shadow.Kind shadowKind = null; //如果分词数组所在位置的字符串是execution if (kind.equals("execution")) { //解析方法或者构造方法 sig = parseMethodOrConstructorSignaturePattern(); if (sig.getKind() == Member.METHOD) { //如果解析到的 PointCut 是方法,则设置shadowKind为Method shadowKind = Shadow.MethodExecution; } else if (sig.getKind() == Member.CONSTRUCTOR) { //如果解析到的 PointCut 是方法,则设置shadowKind为constructor Method shadowKind = Shadow.ConstructorExecution; } } else if (kind.equals("call")) { sig = parseMethodOrConstructorSignaturePattern(); if (sig.getKind() == Member.METHOD) { shadowKind = Shadow.MethodCall; } else if (sig.getKind() == Member.CONSTRUCTOR) { shadowKind = Shadow.ConstructorCall; } } else if (kind.equals("get")) { sig = parseFieldSignaturePattern(); shadowKind = Shadow.FieldGet; } else if (kind.equals("set")) { sig = parseFieldSignaturePattern(); shadowKind = Shadow.FieldSet; } else { throw new ParserException("bad kind: " + kind, tokenSource.peek()); } eat(")"); return new KindedPointcut(shadowKind, sig); }
public SignaturePattern parseMethodOrConstructorSignaturePattern() { int startPos = tokenSource.peek().getStart(); //也许解析注解表达式,如果解析不到,返回@ANY AnnotationTypePattern annotationPattern = maybeParseAnnotationPattern(); //解析访问修饰符,比如 public ,private 等,比如 @Before("execution(public * *(..))") 中的 public ModifiersPattern modifiers = parseModifiersPattern(); //解析方法的返回值类型 TypePattern returnType = parseTypePattern(false, false); //定义包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。 TypePattern declaringType; NamePattern name = null; MemberKind kind; //如果方法返回值参数之后是 new 修饰 if (maybeEatNew(returnType)) { //当前kind是构造方法 kind = Member.CONSTRUCTOR; if (returnType.toString().length() == 0) { declaringType = TypePattern.ANY; } else { declaringType = returnType; } returnType = TypePattern.ANY; name = NamePattern.ANY; } else { //当前kind是普通方法 kind = Member.METHOD; IToken nameToken = tokenSource.peek(); declaringType = parseTypePattern(false, false); if (maybeEat(".")) { nameToken = tokenSource.peek(); name = parseNamePattern(); } else { //获取具体的匹配的方法名称 name = tryToExtractName(declaringType); //如果此时包名+类名为"",则匹配任意包 if (declaringType.toString().equals("")) { declaringType = TypePattern.ANY; } } //如果匹配方法的具体名称为空,抛出异常 if (name == null) { throw new ParserException("name pattern", tokenSource.peek()); } //获取简单方法名 String simpleName = name.maybeGetSimpleName(); if (simpleName != null && simpleName.equals("new")) { throw new ParserException("method name (not constructor)", nameToken); } } //匹配方法参数 TypePatternList parameterTypes = parseArgumentsPattern(true); //匹配方法抛出的异常类型 ThrowsPattern throwsPattern = parseOptionalThrowsPattern(); SignaturePattern ret = new SignaturePattern(kind, modifiers, returnType, declaringType, name, parameterTypes, throwsPattern, annotationPattern); int endPos = tokenSource.peek(-1).getEnd(); ret.setLocation(sourceContext, startPos, endPos); return ret; }
public ModifiersPattern parseModifiersPattern() { int requiredFlags = 0; int forbiddenFlags = 0; int start; while (true) { start = tokenSource.getIndex(); boolean isForbidden = false; isForbidden = maybeEat("!"); IToken t = tokenSource.next(); //得到访问标识位的数值类型 如 : public 0x00000001 , private 0x00000002 , protected 0x00000004, static 0x00000008, final 0x00000010, 这是 java字节码表就是这样规定的 int flag = ModifiersPattern.getModifierFlag(t.getString()); if (flag == -1) { break; } if (isForbidden) { forbiddenFlags |= flag; } else { //根据上面,我们知道如果一个类由 public final 修饰,那么得到的结果是0x00000011,十进制值为18 requiredFlags |= flag; } } tokenSource.setIndex(start); if (requiredFlags == 0 && forbiddenFlags == 0) { return ModifiersPattern.ANY; } else { return new ModifiersPattern(requiredFlags, forbiddenFlags); } }
public TypePattern parseTypePattern(boolean insideTypeParameters, boolean parameterAnnotationsPossible) { TypePattern p = parseAtomicTypePattern(insideTypeParameters, parameterAnnotationsPossible); if (maybeEat("&&")) { p = new AndTypePattern(p, parseNotOrTypePattern(insideTypeParameters, parameterAnnotationsPossible)); } if (maybeEat("||")) { p = new OrTypePattern(p, parseTypePattern(insideTypeParameters, parameterAnnotationsPossible)); } return p; }
private TypePattern parseAtomicTypePattern(boolean insideTypeParameters, boolean parameterAnnotationsPossible) { //也许解析到注解类型 AnnotationTypePattern ap = maybeParseAnnotationPattern(); // 解析注解之后如果是! if (maybeEat("!")) { TypePattern p = null; TypePattern tp = parseAtomicTypePattern(insideTypeParameters, parameterAnnotationsPossible); if (!(ap instanceof AnyAnnotationTypePattern)) { //如果配置了注解,且又配置了tp类型,表示匹配的方法必需包含注解类型同时又不是 tp 类型 p = new NotTypePattern(tp); p = new AndTypePattern(setAnnotationPatternForTypePattern(TypePattern.ANY, ap, false), p); } else { //匹配 ,非tp 类型 p = new NotTypePattern(tp); } return p; } if (maybeEat("(")) { int openParenPos = tokenSource.peek(-1).getStart(); TypePattern p = parseTypePattern(insideTypeParameters, false); if ((p instanceof NotTypePattern) && !(ap instanceof AnyAnnotationTypePattern)) { // @xxx(!com.test.AAA),表示不是 com.test.AAA类型,并配置了注解@xxx TypePattern tp = setAnnotationPatternForTypePattern(TypePattern.ANY, ap, parameterAnnotationsPossible); p = new AndTypePattern(tp, p); } else { // @xxx(com.test.AAA),表示是 com.test.AAA类型,并配置了注解@xxx p = setAnnotationPatternForTypePattern(p, ap, parameterAnnotationsPossible); } eat(")"); int closeParenPos = tokenSource.peek(-1).getStart(); //是否包含可变参数 boolean isVarArgs = maybeEat("..."); if (isVarArgs) { p.setIsVarArgs(isVarArgs); } boolean isIncludeSubtypes = maybeEat("+"); if (isIncludeSubtypes) { p.includeSubtypes = true; // need the test because (A+) should not set subtypes to false! } p.start = openParenPos; p.end = closeParenPos; return p; } int startPos = tokenSource.peek().getStart(); if (ap.start != -1) { startPos = ap.start; } TypePattern p = parseSingleTypePattern(insideTypeParameters); int endPos = tokenSource.peek(-1).getEnd(); p = setAnnotationPatternForTypePattern(p, ap, false); p.setLocation(sourceContext, startPos, endPos); return p; }
public TypePattern parseSingleTypePattern(boolean insideTypeParameters) { if (insideTypeParameters && maybeEat("?")) { return parseGenericsWildcardTypePattern(); } if (allowHasTypePatterns) { if (maybeEatIdentifier("hasmethod")) { return parseHasMethodTypePattern(); } if (maybeEatIdentifier("hasfield")) { return parseHasFieldTypePattern(); } } // 如果参数中有 is if (maybeEatIdentifier("is")) { int pos = tokenSource.getIndex() - 1; TypePattern typeIsPattern = parseIsTypePattern(); if (typeIsPattern != null) { return typeIsPattern; } // rewind as if we never tried to parse it as a typeIs tokenSource.setIndex(pos); } // @Before("execution(* *(java.util.Map [][]<com..Model, com..Model>+[][]..., ..))") 如果这样配置,下面的将全部被解析到 List<NamePattern> names = parseDottedNamePattern(); int dim = 0; while (maybeEat("[")) { eat("]"); dim++; } TypePatternList typeParameters = maybeParseTypeParameterList(); int endPos = tokenSource.peek(-1).getEnd(); boolean includeSubtypes = maybeEat("+"); // 主要是对数组级别的解析 while (maybeEat("[")) { eat("]"); dim++; } boolean isVarArgs = maybeEat("..."); // 如果名字是* ,并且没有数组,没有可变参数,方法的参数还是空,则匹配任意类型 if (names.size() == 1 && names.get(0).isAny() && dim == 0 && !isVarArgs && typeParameters == null) { return TypePattern.ANY; } // Notice we increase the dimensions if varargs is set. this is to allow type matching to // succeed later: The actual signature at runtime of a method declared varargs is an array type of // the original declared type (so Integer... becomes Integer[] in the bytecode). So, here for the // pattern 'Integer...' we create a WildTypePattern 'Integer[]' with varargs set. If this matches // during shadow matching, we confirm that the varargs flags match up before calling it a successful // match. return new WildTypePattern(names, includeSubtypes, dim + (isVarArgs ? 1 : 0), endPos, isVarArgs, typeParameters); }
public AnnotationTypePattern maybeParseAnnotationPattern() { //默认,无论配置和不配置注解的方法都匹配 AnnotationTypePattern ret = AnnotationTypePattern.ANY; AnnotationTypePattern nextPattern = null; //也许吃掉单个注解表达式 while ((nextPattern = maybeParseSingleAnnotationPattern()) != null) { //如果只配置了单个注解,则直接返回单个注解 if (ret == AnnotationTypePattern.ANY) { ret = nextPattern; } else { //如果配置了多个注解,则使用AndAnnotationTypePattern封装一下 ret = new AndAnnotationTypePattern(ret, nextPattern); } } return ret; } public AnnotationTypePattern maybeParseSingleAnnotationPattern() { AnnotationTypePattern ret = null; Map<String, String> values = null; int startIndex = tokenSource.getIndex(); if (maybeEat("!")) { //@Pointcut("execution(!@(java.lang.Deprecated || com.spring_1_100..MyAnnotation) * *(..))") //如果是以上配置先解析!,再解析@ if (maybeEat("@")) { if (maybeEat("(")) { TypePattern p = parseTypePattern(); ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p)); eat(")"); return ret; } else { TypePattern p = parseSingleTypePattern(); if (maybeEatAdjacent("(")) { values = parseAnnotationValues(); eat(")"); ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p, values)); } else { ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p)); } return ret; } } else { tokenSource.setIndex(startIndex); // not for us! return ret; } } if (maybeEat("@")) { if (maybeEat("(")) { //如果是这样配置:@Pointcut("execution(@(java.lang.Deprecated || com.spring_1_100..MyAnnotation) * *(..))") TypePattern p = parseTypePattern(); ret = new WildAnnotationTypePattern(p); eat(")"); return ret; } else { //如果是这样配置 @Pointcut("execution(@java.lang.Deprecated @com.spring_1_100..MyAnnotation * *(..))") int atPos = tokenSource.peek(-1).getStart(); TypePattern p = parseSingleTypePattern(); if (maybeEatAdjacent("(")) { //如果注解中配置了属性,则解析出属性 map values = parseAnnotationValues(); eat(")"); ret = new WildAnnotationTypePattern(p, values); } else { ret = new WildAnnotationTypePattern(p); } ret.start = atPos; return ret; } } else { tokenSource.setIndex(startIndex); // not for us! return ret; } } public TypePatternList parseArgumentsPattern(boolean parameterAnnotationsPossible) { List<TypePattern> patterns = new ArrayList<TypePattern>(); //先吃掉方法的左括号 eat("("); //再尝试吃掉方法的右括号,如果方法中没有配置参数,此时返回 if (maybeEat(")")) { return new TypePatternList(); } do { if (maybeEat(".")) { //如果方法里第一个元素是.,那么必然第二个元素也是. eat("."); //当前参数类型是省略类型,也就是可以是任意类型 patterns.add(TypePattern.ELLIPSIS); } else { //再次递归查找方法参数类型 patterns.add(parseTypePattern(false, parameterAnnotationsPossible)); } } while (maybeEat(",")); //如果方法第一个参数解析完后,接, 则继续解析,否则跳出循环,方法参数解析结束 eat(")"); return new TypePatternList(patterns); } public ThrowsPattern parseOptionalThrowsPattern() { IToken t = tokenSource.peek(); // 如果方法右括号之后的第一个元素是 throws 元素 if (t.isIdentifier() && t.getString().equals("throws")) { tokenSource.next(); //这里可以是排除方法抛出某类异常,也可是方法必需抛出某类异常,才做 PointCut 拦截 List<TypePattern> required = new ArrayList<TypePattern>(); List<TypePattern> forbidden = new ArrayList<TypePattern>(); do { //如果throws 后面是!,只要抛出该类异常的方法,将不做拦截 boolean isForbidden = maybeEat("!"); TypePattern p = parseTypePattern(); if (isForbidden) { forbidden.add(p); } else { //否则,方法必需抛出throws 后所配置的异常,才进行拦截,如果配置多个异常,则需要方法同时抛出多个异常时,才进行方法的切面拦截。 required.add(p); } } while (maybeEat(",")); return new ThrowsPattern(new TypePatternList(required), new TypePatternList(forbidden)); } //如果方法后没有配置 throws ,表示方法无论是抛出异常还是不抛出异常,都进行拦截 return ThrowsPattern.ANY; }
//表达式解析到这里,己经解析完成了,最终返回一个SignaturePattern对象。用于对 Spring 中所有 bean方法的过滤匹配 public SignaturePattern(MemberKind kind, ModifiersPattern modifiers, TypePattern returnType, TypePattern declaringType, NamePattern name, TypePatternList parameterTypes, ThrowsPattern throwsPattern, AnnotationTypePattern annotationPattern) { this.kind = kind;//方法是普通方法还是构造方法 this.modifiers = modifiers;//方法修饰符限制 this.returnType = returnType;//方法返回值类型限制 this.name = name;//所匹配的方法名称限制 this.declaringType = declaringType;//方法所在包+类名限制 this.parameterTypes = parameterTypes;//方法参数类型限制 this.throwsPattern = throwsPattern;//方法异常类型限制 this.annotationPattern = annotationPattern;//方法注解类型限制 this.isExactDeclaringTypePattern = (declaringType instanceof ExactTypePattern); }
最终,我们根据表达式得到 PointCut 如下图所示
经过上面的分析,我相信,如果没有阅读过这一部分源码的小伙伴肯定很晕,不知道在说些什么,下面我们来总结一下
- Spring 先对表达式进行分词,去除空格,tab,回车,等。
- 解析execution()表达式主体。
- 解析是否配置了方法修饰符,如public ,private ,protected 等
- 解析返回类型,*号表示所有的类型。
- 解析需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
- 从包名中提取类名,如果是*号表示所有的类。
- 解析方法参数,*(…):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
- 解析方法后是否配置了异常类,如果配置多个,则表示方法同时要抛出多个异常,才符合条件。
解析流程:
1) PatternParser(expression) :传入切面表达式进行分词; 1)makeOperator(): 创建符号BasicToken; 2)makeIdentifier(): 创建字符串BasicToken; 3)makeLiteral(): 创建string类型的BasicToken; 2)parsePointcut() : 解析 PointCut 1)parseAtomicPointcut() :解析原子 PointCut 1)new NotPointcut(parseAtomicPointcut(), startPos) :如果当前解析到的分词元素是!,则创建NotPointcut 2)parsePointcut() : 如果当前是( ,则再次递归解析 PointCut 3) parseAnnotationPointcut() :如果当前是@ ,则解析注解 PointCut 4) parseSinglePointcut() :解析单个 PointCut 1) parseKindedPointcut () : 如果当前是execution,call,get,set 1)parseMethodOrConstructorSignaturePattern(): 如果是execution,则调用该方法 1)maybeParseAnnotationPattern():解析注解匹配 2)parseModifiersPattern():解析方法修饰符匹配 3)parseTypePattern():解析返回值匹配 4)declaringTypePattern():解析得到包名和类名匹配 5)parseArgumentsPattern():解析得到方法参数匹配 6)parseOptionalThrowsPattern():解析方法后的异常匹配 7)new SignaturePattern(kind, modifiers, returnType, declaringType, name, parameterTypes, throwsPattern, annotationPattern):封装对象,返回方法各项目参数匹配。 2)parseMethodOrConstructorSignaturePattern(): 如果是 call,则调用该方法 ... 3)parseFieldSignaturePattern():如果是 get,set 则调用该方法 1)maybeParseAnnotationPattern():解析注解匹配 2)parseModifiersPattern():解析方法修饰符匹配 3)parseTypePattern():解析返回值匹配 4)declaringTypePattern():解析得到包名和类名匹配, 5)parseNamePattern() : 如果解析到.,则进行名称匹配。到这里直接返回方法参数各项匹配,无参数,和异常类型匹配 2) parseArgsPointcut() : 1)parseArgumentsPattern() :直接进行参数类型匹配,返回 new ArgsPointcut(arguments); 3)parseThisOrTargetPointcut(): this 或者 target 匹配,返回new ThisOrTargetPointcut(kind.equals("this"), type); ... 4) parseWithinPointcut(): 对于 within 类型表达式的匹配,解析出()内类型,直接返回new WithinPointcut(type); ... ... 2)AndPointcut() : 如果原子 PointCut 后还有&& ,则创建AndPointcut 1)left : PointCut 值 2)right : PointCut 匹配结果取 left 和 right 的并集。 3)OrPointcut() : 如果原子 PointCut 后还在 || ,则创建OrPointcut 1)left : PointCut 值 2)right : PointCut 匹配结果,只要任意一个符合要求,则匹配成功。
大体的思路是这样子的,但是实际的解析考虑的情况比上面要复杂得多,在里面很多递归调用,最后才得到我们的PointCut表达式,而,Spring 就是根据表达式的匹配,轮询 bean 中所有的方法,看是否能够匹配,如果能够匹配,才生成相应的动态代理类,和代理方法,从而实现切面。这里只是对表达式的解析进行分析,而表达式的匹配更加复杂,那在下一篇博客中再来做说明了吧。