文档章节

Spring Aop原理之切点表达式解析

爱宝贝丶
 爱宝贝丶
发布于 2018/08/19 14:02
字数 4564
阅读 1.3K
收藏 13

#程序员薪资揭榜#你做程序员几年了?月薪多少?发量还在么?>>>

       在前面的文章(Spring AOP切点表达式详解)中,我们总结了Spring Aop切点表达式的用法,而在上文(Spring Aop原理之Advisor过滤)中我们讲到,切点表达式的解析主要是在PatternParser.parsePointcut()方法中进行的。本文的主要目的是讲解Spring Aop是如何递归的对切点表达式进行解析,并且最终封装为一个Pointcut对象的。

       这里我们首先举一个典型的切点表达式的例子:

@Pointcut(value = "!execution(public protected void com.business.Dog.*(..)) && args(name)", argNames = "name")

       该切点将会匹配如下条件的反面的的所有方法:使用public修饰,返回值为void类型,类为com.business.Dog的方法,并且这些方法必须满足有一个名称为name的参数。

1. 解析入口

       如下是PatternParser.parsePointcut()方法的实现:

public Pointcut parsePointcut() {
    // 转换一个Pointcut单元,比如上述的execution和args都属于一个切点单元
    Pointcut p = parseAtomicPointcut();
    
    // 判断当前切点单元之后是否是一对&&符号,如果是,则递归的将&&后面的切点表达式解析为一个Pointcut
    // 对象,然后将两个Pointcut对象使用AndPointcut连接起来
    if (maybeEat("&&")) {
        p = new AndPointcut(p, parseNotOrPointcut());
    }

    // 判断当前切点单元之后是否是以对||符号,如果是,则递归的将||后面的切点表达式解析为一个Pointcut
    // 对象,然后将两个Pointcut对象使用OrPointcut连接起来
    if (maybeEat("||")) {
        p = new OrPointcut(p, parsePointcut());
    }

    return p;
}

       在parsePointcut()方法中,其对切点表达式的解析是一个一个进行的,解析完成一个之后就判断其后是&&还是||,然后使用AndPointcut或者OrPointcut将操作符两边的结果进行组装。这里需要说明的是,AndPointcut和OrPointcut内部实际上也只是保存了两个Pointcut对象,在后面进行切点匹配时,其实际上还是将匹配过程委托给这两个Pointcut对象进行,最后将两个Pointcut匹配的结果进行取交或者取并。这里我们继续阅读parseAtomicPointcut()的实现代码:

private Pointcut parseAtomicPointcut() {
    // 判断当前表达式单元是否以!开头,如果是,则使用一个NotPointcut对!后面的匹配结果进行封装
    if (maybeEat("!")) {
        int startPos = tokenSource.peek(-1).getStart();
        Pointcut p = new NotPointcut(parseAtomicPointcut(), startPos);
        return p;
    }
    
    // 如果表达式是以括号开头,则直接对括号后面的表达式进行解析,因为在进行顺序解析时,
    // 括号的作用与解析的顺序是一致的
    if (maybeEat("(")) {
        Pointcut p = parsePointcut();
        eat(")");
        return p;
    }
    
    // 判断表达式是否是以@符号开头,如果是,说明表达式是修饰注解类型的表达式单元,因而使用
    // parseAnnotationPointcut()方法对注解类型的表达式进行解析
    if (maybeEat("@")) {
        int startPos = tokenSource.peek().getStart();
        Pointcut p = parseAnnotationPointcut();
        int endPos = tokenSource.peek(-1).getEnd();
        p.setLocation(sourceContext, startPos, endPos);
        return p;
    }
    
    // 当上述条件都不满足时,说明当前表达式是一个简单类型的表达式,如前面示例中的execution表达式,
    // 此时使用parseSinglePointcut()方法对该类型表达式进行解析
    int startPos = tokenSource.peek().getStart();
    Pointcut p = parseSinglePointcut();
    int endPos = tokenSource.peek(-1).getEnd();
    p.setLocation(sourceContext, startPos, endPos);
    return p;
}

       从上面的代码中可以看出,Spring在进行切点单元的解析的时候主要分为两种情况进行解析:

  • 解析修饰注解类型的切点表达式,如@annotation,使用parseAnnotationPointcut()方法进行解析;
  • 解析一般的切点表达式,如execution等,使用parseSinglePointcut()方法进行解析。

2. 注解表达式解析

       对于注解类型的表达式的解析,使用的是parseAnnotationPointcut()方法进行,我们首先举例说明注解类型的表达式的用法:

@Around("@annotation(com.business.annotation.FruitAspect)")

       这里@Around将会环绕使用FruitAspect注解标注的方法。下面是parseAnnotationPointcut()的源码:

public Pointcut parseAnnotationPointcut() {
    int start = tokenSource.getIndex();
    IToken t = tokenSource.peek();
    // 获取当前修饰注解类型的表达式的标识符,如annotation,args,within等,
    // 上述示例中获取到的就是annotation
    String kind = parseIdentifier();
    IToken possibleTypeVariableToken = tokenSource.peek();
    // 这里是做的校验,在标识符之后不能是使用<>声明的注解类型列表,如果是,
    // 获取的typeVariables就不为空,下面if判断中就会抛出异常
    String[] typeVariables = maybeParseSimpleTypeVariableList();
    if (typeVariables != null) {
        String message = "(";
        assertNoTypeVariables(typeVariables, message, possibleTypeVariableToken);
    }
    
    // 充值当前要解析的表达式所在位置的索引
    tokenSource.setIndex(start);
    if (kind.equals("annotation")) {
        // 解析使用@annotation修饰的注解类型,只要目标方法上使用其后声明的注解就会被环绕
        return parseAtAnnotationPointcut();
    } else if (kind.equals("args")) {
        // 解析使用@args修饰的注解类型,其后可以使用多个注解参数类型,表示如果目标方法的参数
        // 使用其中任意一个注解参数类型进行标注,该方法就会被环绕
        return parseArgsAnnotationPointcut();
    } else if (kind.equals("this") || kind.equals("target")) {
        // this和target其后都是指定一个类或接口类型,分别表示匹配代理对象为指定类型和
        // 匹配目标对象为指定类型
        return parseThisOrTargetAnnotationPointcut();
    } else if (kind.equals("within")) {
        // 解析@within修饰的类型,其后接一个注解类型,表示目标类只要使用该注解进行标注,那么
        // 其所有方法将会被环绕
        return parseWithinAnnotationPointcut();
    } else if (kind.equals("withincode")) {
        // 解析@withincode修饰的类型,其后接一个注解类型,表示目标方法只要使用该注解进行标注就会被环绕
        return parseWithinCodeAnnotationPointcut();
    }
    throw new ParserException("pointcut name", t);
}

       可以看到,对于注解类型的参数解析都在parseAnnotationPointcut()方法进行声明了,并且使用多个if语句进行了分发。我们这里以@annotation修饰的注解类型进行讲解,其余的几个修饰的类型与其解析方式非常类似,读者可自行阅读。如下是parseAtAnnotationPointcut()的源码:

private Pointcut parseAtAnnotationPointcut() {
    // 将annotation进行转换,并将解析的index置于其后
    parseIdentifier();
    
    // 判断annotation后是否为左括号
    eat("(");
    // 如果左括号之后是右括号,而没有具体的注解类型,则抛出异常
    if (maybeEat(")")) {
        throw new ParserException("@AnnotationName or parameter", tokenSource.peek());
    }
    // 解析具体的注解或变量类型
    ExactAnnotationTypePattern type = parseAnnotationNameOrVarTypePattern();
    // 解析完注解类型之后其后应该是一个右括号,对其进行解析,并且将解析的index置于其后
    eat(")");
    
    // 将解析的结果封装为一个AnnotationPointcut
    return new AnnotationPointcut(type);
}

       这里parseAtAnnotationPointcut()主要是解析了annotation之后的左括号,注解类型和右括号,并将解析结果封装到了AnnotationPointcut对象中。对于注解类型的具体解析过程在parseAnnotationNameOrVarTypePattern()方法中,如下是该方法的源码:

protected ExactAnnotationTypePattern parseAnnotationNameOrVarTypePattern() {
    ExactAnnotationTypePattern p = null;
    int startPos = tokenSource.peek().getStart();
    // 如果注解类型前使用了@符号,则抛出异常
    if (maybeEat("@")) {
        throw new ParserException("@Foo form was deprecated in AspectJ 5 M2: " 
            + "annotation name or var ", tokenSource.peek(-1));
    }
    // 转换简单的注解类型名称,也即上述的com.business.annotation.FruitAspect
    p = parseSimpleAnnotationName();
    int endPos = tokenSource.peek(-1).getEnd();
    // 设置该注解类型的基本属性
    p.setLocation(sourceContext, startPos, endPos);
    // 如果注解类型之后是一个左括号,则对括号中的注解属性类型进行解析,并且将其和前面解析到的
    // 注解类型封装到ExactAnnotationFieldTypePattern中
    if (maybeEat("(")) {
        String formalName = parseIdentifier();
        p = new ExactAnnotationFieldTypePattern(p, formalName);
        eat(")");
    }
    return p;
}

       这里parseAnnotationNameOrVarTypePattern()方法主要解析了两部分数据:①注解类型的全路径名;②注解属性类型,如此一个注解类型才真正解析完成。

3. 一般表达式类型解析

       对于一般表达式类型的解析,主要是通过前面讲解的parseSinglePointcut()方法进行的,如下是该方法的实现源码:

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类型的表达式进行解析。这里execution主要修饰的是
        // 一整个方法,包括返回值和异常类型;call修饰的也是一个方法,只不过其目标是调用当前
        // 方法的对象;get和set都是修饰的属性设值的方法。
        p = parseKindedPointcut(kind);
    } else if (kind.equals("args")) {
        // 解析args类型的表达式,其后可以带多个全路径参数类型,目标方法如果参数类型与其匹配将会被环绕
        p = parseArgsPointcut();
    } else if (kind.equals("this")) {
        // 解析this类型的表达式,其后接一个类型,表示生成的代理对象必须是其后指定的类型
        p = parseThisOrTargetPointcut(kind);
    } else if (kind.equals("target")) {
        // 解析target类型的表达式,其后接一个类型,表示被代理的目标对象必须是其后指定的类型
        p = parseThisOrTargetPointcut(kind);
    } else if (kind.equals("within")) {
        // 解析within类型的表达式,其后也是接一个类型,表示目标类型必须是其后表达式的类型
        p = parseWithinPointcut();
    } else if (kind.equals("withincode")) {
        // 解析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")) {
        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);
            }
            p = new IfPointcut("");
        }
    } else {
        // 如果表达式的类型与上述所有类型都不符,那么可能当前类型是用户自定义的类型。对于用户
        // 自定义类型,其只需要实现PointcutDesignatorHandler接口,其getDesignatorName()用于
        // 返回当前表达式类型名,其parse()方法则用于将自定义的表达式转换为一个Pointcut对象
        boolean matchedByExtensionDesignator = false;
        for (PointcutDesignatorHandler pcd : pointcutDesignatorHandlers) {
            if (pcd.getDesignatorName().equals(kind)) {
                p = parseDesignatorPointcut(pcd);
                matchedByExtensionDesignator = true;
            }

        }
        if (!matchedByExtensionDesignator) {
            tokenSource.setIndex(start);
            p = parseReferencePointcut();
        }
    }
    return p;
}

       这里只列出了常用的几种表达式类型,并且对其含义进行了讲解,详细的用法请查看本人前面写的文章。这里parseSinglePointcut()方法与parseAnnotationPointcut()的结构非常类似,即这里只是起到一个分发的作用,具体的实现在具体的方法中进行。另外这里parseSinglePointcut()方法也提供了一个实现自定义的切点表达式的方法,具体的使用读者可以阅读PointcutDesignatorHandler接口的声明,对于Pointcut理解比较透彻的话实现自定义切点表达式还是比较简单的。对于切点表达式的解析,我们这里还是讲解最常用的一种,即execution类型表达式的解析,如下是parseKindedPointcut()的源码:

private KindedPointcut parseKindedPointcut(String kind) {
    // 解析的表达式必须是使用括号包围的
    eat("(");
    SignaturePattern sig;

    Shadow.Kind shadowKind = null;
    if (kind.equals("execution")) {
        // 对execution类型的表达式进行解析,解析时分为Method和Contrustor类型分别处理
        sig = parseMethodOrConstructorSignaturePattern();
        if (sig.getKind() == Member.METHOD) {
            shadowKind = Shadow.MethodExecution;
        } else if (sig.getKind() == Member.CONSTRUCTOR) {
            shadowKind = Shadow.ConstructorExecution;
        }
    } else if (kind.equals("call")) {
        // 对call类型的表达式进行解析,分为Method和Contrustor分别进行处理
        sig = parseMethodOrConstructorSignaturePattern();
        if (sig.getKind() == Member.METHOD) {
            shadowKind = Shadow.MethodCall;
        } else if (sig.getKind() == Member.CONSTRUCTOR) {
            shadowKind = Shadow.ConstructorCall;
        }
    } else if (kind.equals("get")) {
        // 对get表达式进行解析
        sig = parseFieldSignaturePattern();
        shadowKind = Shadow.FieldGet;
    } else if (kind.equals("set")) {
        // 对set表达式进行解析
        sig = parseFieldSignaturePattern();
        shadowKind = Shadow.FieldSet;
    } else {
        throw new ParserException("bad kind: " + kind, tokenSource.peek());
    }
    eat(")");
    // 将解析后的结果封装为一个KindedPointcut
    return new KindedPointcut(shadowKind, sig);
}

       这里parseKindedPointcut()将Kinded类型的表达式在这里分为execution,call,get和set分别进行解析,只不过最后都是使用KindedPointcut进行封装解析结果。我们这里还是继续阅读execution类型的解析代码:

public SignaturePattern parseMethodOrConstructorSignaturePattern() {
    int startPos = tokenSource.peek().getStart();
    // 判断当前方法是否使用指定注解进行了修饰,这里解析的方式与之前的@annotation不一样,
    // 这里是直接使用形如@chapter7.eg6.FruitAspect进行修饰即可
    AnnotationTypePattern annotationPattern = maybeParseAnnotationPattern();
    // 对访问权限修饰符进行解析,也即public,protected等,也可以使用多个进行组合,
    // 多个进行组合时只需要顺序列出即可;如果要过滤掉某些修饰符,比如过滤掉public修饰的
    // 方法,则在前面加一个!即可,如:!public
    ModifiersPattern modifiers = parseModifiersPattern();
    // 对方法返回值进行解析,这里也可以解析基本数据类型,如void,int等
    TypePattern returnType = parseTypePattern(false, false);

    TypePattern declaringType;
    NamePattern name = null;
    MemberKind kind;

    // 对返回值类型进行判断,如果返回值类型用点“.”分隔的最后一部分是new关键字,那么就会认为其
    // 是进行构造方法增强的切点表达式
    if (maybeEatNew(returnType)) {
        kind = Member.CONSTRUCTOR;
        // 对于构造方法,这里returnType始终是TypePattern.ANY,而构造的对象类型是
        // 通过declaringType来保存的
        if (returnType.toString().length() == 0) {
            declaringType = TypePattern.ANY;
        } else {
            declaringType = returnType;
        }
        returnType = TypePattern.ANY;
        name = NamePattern.ANY;
    } else {
        // 如果解析的不是构造方法,则按照一般的方法解析方式进行解析
        kind = Member.METHOD;
        IToken nameToken = tokenSource.peek();
        // 首先解析方法所在的class类型
        declaringType = parseTypePattern(false, false);
        if (maybeEat(".")) {
            // 如果类后面是一个“.”,则将其后的名称作为方法名进行解析
            nameToken = tokenSource.peek();
            name = parseNamePattern();
        } else {
            // 如果类后面的名称不是“.”,则将类按照“.”分隔,并将最后一个“.”之后的部分当做方法名,
            // 之前的部分则作为类名;如果类中没有“.”,则将整个类名都作为方法名进行解析,并且
            // 类名使用TypePattern.ANY表示,也即任意的类型名都行
            name = tryToExtractName(declaringType);
            if (declaringType.toString().equals("")) {
                declaringType = TypePattern.ANY;
            }
        }
        
        // 如果通过上面的解析后得到的方法名还是null,则抛出异常
        if (name == null) {
            throw new ParserException("name pattern", tokenSource.peek());
        }
        
        // 获取方法名,判断方法名是否为new关键字,如果是,则抛出异常
        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进行封装,并将其返回
    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;
}

       这里parseMethodOrConstructorSignaturePattern()方法就是对基本切点表达式单元进行解析的整体流程,其主要分为如下几个步骤:

  • 首先会解析当前方法是否需要使用某个注解进行修饰,比如如下的示例就要求目标方法必须使用FruitAspect注解进行修饰:
@Around("execution(@chapter7.eg6.FruitAspect public void chapter7.eg9.Dog.*(..))")
  • 然后解析当前方法所使用的修饰符,即public,protected等,可以取非,需要注意的是,肯定的不能同时有多个,比如不能同时存在public protected,此时虽然表达式不会报错,但是基本上匹配不到任何方法,因为没有方法同时是public又是protected的。如下就是要求目标方法必须是public的,并且不能是protected修饰的:
@Around("execution(public !protected void chapter7.eg9.Dog.*(..))")
  • 接着就是对返回值类型的解析,对返回值类型的解析又分为两种情况,一种是目标方法是构造方法,另一种则是目标方法是一般的方法。对于构造方法,只需要返回值的最后一部分是new即可,然后其后紧接着参数列表;而对于一般的方法,返回值之后则是当前方法所在的类,以及修饰的目标方法。如下是修饰构造方法的一个示例:
@Around("execution(public chapter7.eg9.Dog.new(..))")
  • 如果需要修饰的方法不是构造方法,那么其修饰的就是一般方法,对于一般方法,返回值之后就是要修饰的方法所在的类和要修饰的方法,这里的类必须包含全路径名。比如如下修饰的一般方法:
@Around("execution(public void chapter7.eg9.Dog.*(..))")
  • 在类和方法都解析完成之后,就是解析参数列表了,对于参数列表的解析,其会使用“,”对列表进行分割,并且会判断参数列表中是否包含有“..”和"*"之类的通配符;
  • 最后就是解析表达式中是否包含可选的异常抛出表达式,异常抛出表达式都是使用throws进行修饰的,而throws之后则是接的一系列的类型列表,这里很明显也是可以使用递归进行类型列表的解析的。如下使用throws表达式的一个示例:
@Around("execution(public void chapter7.eg9.Dog.*(..) throws java.lang.Exception)")

4. 小结

       本文主要讲解了Spring Aop是如何使用递归对切点表达式进行解析的,在解析过程中Spring Aop将其分为了两种过程,一种是对注解类型的切点表达式的解析,一种是对一般类型的切点表达式的解析。而在解析过程中也支持逻辑表达式&&,||和!的使用,对于这些表达式,则是通过AndPointcut,OrPointcut和NotPointcut等进行封装的。解析的最终结果就是整个切点表达式都会封装到一个Pointcut对象中,而标签中的每一部分都是该Pointcut对象的一个子节点,整体类似于一种树状结构。

5. 广告

       读者朋友如果觉得本文还不错,可以点击下面的广告链接,这可以为作者带来一定的收入,从而激励作者创作更好的文章,非常感谢!

在项目开发过程中,企业会有很多的任务、需求、缺陷等需要进行管理,CORNERSTONE 提供敏捷、任务、需求、缺陷、测试管理、WIKI、共享文件和日历等功能模块,帮助企业完成团队协作和敏捷开发中的项目管理需求;更有甘特图、看板、思维导图、燃尽图等多维度视图,帮助企业全面把控项目情况。

© 著作权归作者所有

爱宝贝丶

爱宝贝丶

粉丝 401
博文 160
码字总数 575060
作品 0
武汉
程序员
私信 提问
加载中

评论(4)

h
helloYouth

引用来自“helloYouth”的评论

看源码,有什么经验可以分享下吗?谢谢

引用来自“爱宝贝丶”的评论

一般都是先看书,然后再看源码,一般的如果对一个框架不熟悉,看源码是很难看懂的。这里说的熟悉指的是对框架的各个方面的用法的熟悉程度,因为源码其实就是实现这些功能点的,如果连如何用都不能比较全面的掌握的话,基本上很难看懂源码。我看源码一般的分为三步:①找一本讲解该框架是如何使用的书籍,把书看完,并且对着上面的示例自己敲一遍,这样基本上对这个框架的用法就有了一个整体上的认识;②找一本讲解源码的书籍,把书籍看一遍,这样的话你就对这个框架的源码实现结构有一个整体上的认识;③看源码,虽然第二步和第三步是有一些重叠的,但是需要明白的是,人的学习进程是一步一步的,如果跳过第二步直接看源码,基本上也是看不懂的,而且一般地即使是讲解源码的书籍,也只会讲解骨架结构,而不会非常细致的给你讲解每一个点,所以第三步就是一点一点的把这些“芝麻”都捡起来。基本上经过上面这三步,你对这个框架就有了非常全面的认识了。
谢谢大佬的提点
爱宝贝丶
爱宝贝丶 博主

引用来自“helloYouth”的评论

看源码,有什么经验可以分享下吗?谢谢
一般都是先看书,然后再看源码,一般的如果对一个框架不熟悉,看源码是很难看懂的。这里说的熟悉指的是对框架的各个方面的用法的熟悉程度,因为源码其实就是实现这些功能点的,如果连如何用都不能比较全面的掌握的话,基本上很难看懂源码。我看源码一般的分为三步:①找一本讲解该框架是如何使用的书籍,把书看完,并且对着上面的示例自己敲一遍,这样基本上对这个框架的用法就有了一个整体上的认识;②找一本讲解源码的书籍,把书籍看一遍,这样的话你就对这个框架的源码实现结构有一个整体上的认识;③看源码,虽然第二步和第三步是有一些重叠的,但是需要明白的是,人的学习进程是一步一步的,如果跳过第二步直接看源码,基本上也是看不懂的,而且一般地即使是讲解源码的书籍,也只会讲解骨架结构,而不会非常细致的给你讲解每一个点,所以第三步就是一点一点的把这些“芝麻”都捡起来。基本上经过上面这三步,你对这个框架就有了非常全面的认识了。
h
helloYouth
看源码,有什么经验可以分享下吗?谢谢
h
helloYouth
哥,你太牛逼了,何像秀。
Spring Aop原理之Advisor过滤

在上文(Spring Aop之Advisor解析)中我们讲到,Spring Aop对目标bean的代理主要分为三个步骤:获取所有的Advisor,过滤当前bean可应用的Advisor和使用Advisor为当前bean生成代理对象,并且上文...

爱宝贝丶
2018/08/18
340
0
《Spring5学习》04 - 面向切面编程

一、Spring面向切面编程的基本概念 面向切面编程(即AOP):把项目中需要再多处使用的功能比如日志、安全和事务等集中到一个类中处理,而不用在每个需要用到该功能的地方显式调用。 横切关注...

老韭菜
2018/08/19
165
0
Spring AOP 源码分析 - 筛选合适的通知器

1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析。本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出合适的通知器(Advisor...

java高级架构牛人
2018/06/21
25
0
曹工说Spring Boot源码(22)-- 你说我Spring Aop依赖AspectJ,我依赖它什么了

写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,...

三国梦回
03/05
0
0
Spring AOP 源码分析系列文章导读

简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解。在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅读了 AOP ...

java高级架构牛人
2018/06/21
32
1

没有更多内容

加载失败,请刷新页面

加载更多

Spring的扩展原理

MainConfigOfExt.class /** * 扩展原理: * 1. BeanPostProcessor:bean后置处理器;bean创建对象初始化前后进行拦截工作 * BeanFactoryPostProcessor:beanFactory的后置处理器 * ...

与你同行7
8分钟前
9
0
C# 基础知识系列- 16 开发工具篇

0. 前言 这是C# 基础知识系列的最后一个内容讲解篇,下一篇是基础知识-实战篇。这一篇主要讲解一下C#程序的结构和主要编程工具。 1. 工具 工欲善其事必先利其器,在实际动手之前我们先来看看...

月影南溪
13分钟前
13
0
阿里双11超级工程:PB级文件分发重器蜻蜓

有图有介绍见: http://tech.it168.com/a2017/1114/3179/000003179630.shtml 蜻蜓开源地址:https://github.com/alibaba/dragonfly 2017天猫双11, 交易峰值32.5万/秒,支付峰值25.6万/秒,数...

whoisliang
26分钟前
13
0
终于有人把最适合学习算法的书单找出来了,面试必备!

害,这年头算法真的不好学,但是笔试面试又非常爱考,那咋办呢?我来给你推荐几本算法学习好书吧,都是我当年秋招复习时用的,算法导论什么的都给我吃灰去吧!! 算法书单 算法图解 黄小斜的...

黄小斜
30分钟前
31
0
vue组件大小写说明

https://cn.vuejs.org/v2/style-guide/#%E6%A8%A1%E6%9D%BF%E4%B8%AD%E7%9A%84%E7%BB%84%E4%BB%B6%E5%90%8D%E5%A4%A7%E5%B0%8F%E5%86%99%E5%BC%BA%E7%83%88%E6%8E%A8%E8%8D%90......

李超明
43分钟前
29
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部