因为字数超过了限制,所以分成了三篇,承接上篇:

https://www.jianshu.com/p/46e27afd7d96

代码过宽,可以shift + 鼠标滚轮 左右滑动查看

4.parseCustomElement

  1. <!-- 事务处理,事务只针对service层 -->
  2. <aop:config>
  3. <aop:pointcut id="pc" expression="execution(public * cn.mrdear.service.impl..*(..))" />
  4. <aop:advisor pointcut-ref="pc" advice-ref="txAdvice" />
  5. </aop:config>

非默认名称空间的标签,走的是这个方法

  1. //4.如果该标签属于其他的名称空间比如:context,aop等
  2. //xmlns:aop="http://www.springframework.org/schema/aop"
  3. //xmlns:context="http://www.springframework.org/schema/context"
  4. delegate.parseCustomElement(ele);

进入这个方法,在BeanDefinitionParserDelegate类中

  1. public BeanDefinition parseCustomElement(Element ele) {
  2. return parseCustomElement(ele, null);
  3. }
  4. public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
  5. //拿到标签对应的名称空间URI
  6. //比如:http://www.springframework.org/schema/aop
  7. String namespaceUri = getNamespaceURI(ele);
  8. //4.1拿到名称空间处理器解析器,去解析URI,获取名称空间处理器
  9. NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
  10. if (handler == null) {
  11. error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
  12. return null;
  13. }
  14. //4.2通过处理器的某个解析器解析对应标签
  15. return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
  16. }

4.1 resolve

DefaultNamespaceHandlerResolver是名称空间处理器解析器的默认实现

跟踪标记4.1的方法

  1. //4.1拿到名称空间处理器解析器,去解析URI,获取名称空间处理器
  2. NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
  3. /**
  4. * Locate the {@link NamespaceHandler} for the supplied namespace URI
  5. * from the configured mappings.
  6. *
  7. * 根据提供的 namespace URI 从配置映射中找到 NamespaceHandler
  8. */
  9. @Override
  10. public NamespaceHandler resolve(String namespaceUri) {
  11. //拿到处理器解析器中所有的处理器
  12. Map<String, Object> handlerMappings = getHandlerMappings();
  13. //拿到指定的处理器
  14. Object handlerOrClassName = handlerMappings.get(namespaceUri);
  15. //如果为null就返回null
  16. if (handlerOrClassName == null) {
  17. return null;
  18. }
  19. //如果是实例就返回实例
  20. else if (handlerOrClassName instanceof NamespaceHandler) {
  21. return (NamespaceHandler) handlerOrClassName;
  22. }
  23. //否则就一定是字符串
  24. else {
  25. String className = (String) handlerOrClassName;
  26. try {
  27. //通过加载器生成处理器的Class对象
  28. Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
  29. //必须要实现NamespaceHandler接口
  30. if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
  31. throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
  32. "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
  33. }
  34. //实例化一个
  35. NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
  36. //初始化,进入这个方法,里面初始化多个解析器
  37. namespaceHandler.init();
  38. //用实例覆盖原先handlerMappings中的字符串
  39. handlerMappings.put(namespaceUri, namespaceHandler);
  40. return namespaceHandler;
  41. }
  42. catch (ClassNotFoundException ex) {
  43. throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
  44. namespaceUri + "] not found", ex);
  45. }
  46. catch (LinkageError err) {
  47. throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
  48. namespaceUri + "]: problem with handler class file or dependent class", err);
  49. }
  50. }
  51. }
  52. /**
  53. * 这个方法在类AopNamespaceHandler中
  54. *
  55. * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
  56. * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
  57. * and '{@code scoped-proxy}' tags.
  58. *
  59. * 为config、spring-configured、aspectj-autoproxy以及scoped-proxy标签注册BeanDefinitionParsers
  60. */
  61. @Override
  62. public void init() {
  63. //这里面每一个解析器都对应着AOP名称空间下的一个标签
  64. //不同的处理器注册的解析器都不一样
  65. //这里随便找一个解析器,查看他的注册流程
  66. // In 2.0 XSD as well as in 2.1 XSD.
  67. registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
  68. registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
  69. registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
  70. // Only in 2.0 XSD: moved to context namespace as of 2.1
  71. registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
  72. }
  73. /**
  74. * 此方法在类AopNamespaceHandler的父类NamespaceHandlerSupport中
  75. *
  76. * Subclasses can call this to register the supplied {@link BeanDefinitionParser} to
  77. * handle the specified element. The element name is the local (non-namespace qualified)
  78. * name.
  79. *
  80. * 子类可以调用这个方法注册指定的BeanDefinitionParser去处理相关的标签
  81. * 这个elementName是个局部名称(不包含名称空间)
  82. */
  83. protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
  84. //将解析器注册到这个名称空间处理器的parsers属性中,
  85. //parsers是一个HashMap
  86. this.parsers.put(elementName, parser);
  87. }

4.2 parse

跟踪标记4.2的方法

进入处理器的抽象父类NamespaceHandlerSupport中

入参传递了一个ParserContext解析器上下文,ParserContext中拿到了阅读器上下文的引用,代理的引用,containingBd在这里为null

  1. //4.2通过处理器的某个解析器解析对应标签
  2. return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
  3. /**
  4. * Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
  5. * registered for that {@link Element}.
  6. *
  7. * 通过委派给根据对应标签所注册的解析器,来解析提供的标签
  8. */
  9. @Override
  10. public BeanDefinition parse(Element element, ParserContext parserContext) {
  11. //将标签名称作为key,去处理器内部的parsers属性中,获取对应的value值,也就是解析器
  12. //利用解析器来解析这个标签,进入这个方法。
  13. return findParserForElement(element, parserContext).parse(element, parserContext);
  14. }
  15. /**
  16. * 因为解析是的<aop:config>,所以该方法在ConfigBeanDefinitionParser类中
  17. *
  18. * Parse the specified {@link Element} and register the resulting
  19. * {@link BeanDefinition BeanDefinition(s)} with the
  20. * {@link org.springframework.beans.factory.xml.ParserContext#getRegistry() BeanDefinitionRegistry}
  21. * embedded in the supplied {@link ParserContext}.
  22. * <p>Implementations must return the primary {@link BeanDefinition} that results
  23. * from the parse if they will ever be used in a nested fashion (for example as
  24. * an inner tag in a {@code <property/>} tag). Implementations may return
  25. * {@code null} if they will <strong>not</strong> be used in a nested fashion.
  26. *
  27. * 解析指定的标签,并注册最终的结果bean Definition到内嵌在ParserContext中的bean工厂中
  28. */
  29. @Override
  30. public BeanDefinition parse(Element element, ParserContext parserContext) {
  31. //一个组件集合,里面包含了多个组件,以标签名(包含了名称空间)作为名称,比如aop:config
  32. //里面包含的多个组件就是aop:config的子标签,包含对标签属性的描述
  33. //比如xml中pointcut子标签就是一个组件,advisor子标签也是一个组件
  34. //这个组件记录了子标签的name属性或者expression属性,根据不同子标签,属性不一样
  35. CompositeComponentDefinition compositeDef =
  36. new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
  37. //将组件集合保存在parserContext解析器上下文的containingComponents属性中
  38. parserContext.pushContainingComponent(compositeDef);
  39. //4.2.1配置自动代理创建器
  40. configureAutoProxyCreator(parserContext, element);
  41. //配置完创建器后拿到所有子标签
  42. List<Element> childElts = DomUtils.getChildElements(element);
  43. for (Element elt: childElts) {
  44. //拿到所有子标签localName,也就是不包括名称空间的标签名字
  45. String localName = parserContext.getDelegate().getLocalName(elt);
  46. //如果子标签是pointcut
  47. if (POINTCUT.equals(localName)) {
  48. //4.2.2解析pointcut子标签
  49. parsePointcut(elt, parserContext);
  50. }
  51. //如果子标签是advisor
  52. else if (ADVISOR.equals(localName)) {
  53. //4.2.3解析advisor子标签
  54. parseAdvisor(elt, parserContext);
  55. }
  56. //如果子标签是aspect
  57. else if (ASPECT.equals(localName)) {
  58. //4.2.4解析aspect子标签
  59. parseAspect(elt, parserContext);
  60. }
  61. }
  62. //4.2.5弹出组件
  63. parserContext.popAndRegisterContainingComponent();
  64. return null;
  65. }

4.2.1 configureAutoProxyCreator

跟踪标记4.2.1的方法

此方法在类ConfigBeanDefinitionParser中实现

  1. //4.2.1配置自动代理创建器
  2. configureAutoProxyCreator(parserContext, element);
  3. /**
  4. * Configures the auto proxy creator needed to support the {@link BeanDefinition BeanDefinitions}
  5. * created by the '{@code <aop:config/>}' tag. Will force class proxying if the
  6. * '{@code proxy-target-class}' attribute is set to '{@code true}'.
  7. *
  8. * 配置自动代理创建器,支持由<aop:config/>标签创建的beanDefinition
  9. * 如果标签的proxy-target-class属性被设置为true,那么强制代理
  10. */
  11. private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
  12. AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
  13. }
  14. /**
  15. * 进入AopNamespaceUtils类中的静态方法
  16. */
  17. public static void registerAspectJAutoProxyCreatorIfNecessary(
  18. ParserContext parserContext, Element sourceElement) {
  19. //4.2.1.1如果必要的话注册AspectJ自动代理创建器
  20. BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
  21. parserContext.getRegistry(), parserContext.extractSource(sourceElement));
  22. //4.2.1.2如果必要的话使用CLass代理
  23. useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
  24. //4.2.1.3如果必要的话注册组件
  25. registerComponentIfNecessary(beanDefinition, parserContext);
  26. }
4.2.1.1 registerAspectJAutoProxyCreatorIfNecessary

先跟踪标记4.2.1.1的方法

该方法在AopConfigUtils类中实现

  1. //4.2.1.1如果必要的话注册AspectJ自动代理创建器
  2. BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
  3. parserContext.getRegistry(), parserContext.extractSource(sourceElement));
  4. public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
  5. //传递了一个AspectJAwareAdvisorAutoProxyCreator的Class,进入这个方法
  6. return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
  7. }
  8. private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
  9. Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
  10. //工厂中是否已经注册了org.springframework.aop.config.internalAutoProxyCreator
  11. if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
  12. BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
  13. //如果已经有注册了internalAutoProxyCreator,并且和入参传递的不是同一个Class,
  14. //那么就根据优先级进行选择
  15. if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
  16. //类AopConfigUtils中有个ArrayList属性APC_PRIORITY_LIST,在类静态构造中依次加入了
  17. //几个创建器,这个方法就是查找某个创建器在APC_PRIORITY_LIST中的索引,如果没有找到就报错
  18. int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
  19. int requiredPriority = findPriorityForClass(cls);
  20. //internalAutoProxyCreator的索引为0,入参的AspectJAwareAdvisorAutoProxyCreator
  21. //索引为1,后者要大,所以重新设置下apcDefinition的beanClass
  22. if (currentPriority < requiredPriority) {
  23. apcDefinition.setBeanClassName(cls.getName());
  24. }
  25. }
  26. //直接返回null
  27. return null;
  28. }
  29. RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
  30. beanDefinition.setSource(source);
  31. beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
  32. beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  33. registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
  34. return beanDefinition;
  35. }
4.2.1.2 useClassProxyingIfNecessary

跟踪标记4.2.1.2

此方法的实现在AopNamespaceUtils类中

  1. //4.2.1.2如果必要的话使用CLass代理
  2. useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
  3. private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
  4. if (sourceElement != null) {
  5. //查看是否有设置proxy-target-class属性
  6. boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
  7. if (proxyTargetClass) {
  8. AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
  9. }
  10. //查看是否有设置expose-proxy属性
  11. boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
  12. if (exposeProxy) {
  13. AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
  14. }
  15. }
  16. }
4.2.1.3 registerComponentIfNecessary

跟踪标记4.2.1.3

此方法的实现在AopNamespaceUtils类中

  1. //4.2.1.3如果必要的话注册组件
  2. registerComponentIfNecessary(beanDefinition, parserContext);
  3. private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ParserContext parserContext) {
  4. //因为前面创建器中的beanClass用了新的那个,所以返回的beanDefinition为null,
  5. //这里的beanDefinition也就为null了,跳过
  6. if (beanDefinition != null) {
  7. BeanComponentDefinition componentDefinition =
  8. new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
  9. parserContext.registerComponent(componentDefinition);
  10. }
  11. }

4.2.2 parsePointcut

跟踪标记4.2.2

此方法的实现在ConfigBeanDefinitionParser类中

  1. //4.2.2解析pointcut子标签
  2. parsePointcut(elt, parserContext);
  3. /**
  4. * Parses the supplied {@code <pointcut>} and registers the resulting
  5. * Pointcut with the BeanDefinitionRegistry.
  6. *
  7. * 解析<pointcut>标签,注册结果到bean工厂中
  8. */
  9. private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
  10. //拿到id和expression的属性
  11. String id = pointcutElement.getAttribute(ID);
  12. String expression = pointcutElement.getAttribute(EXPRESSION);
  13. AbstractBeanDefinition pointcutDefinition = null;
  14. try {
  15. //解析前放入,解析后弹出
  16. this.parseState.push(new PointcutEntry(id));
  17. //创建一个bean definition,提前指定AspectJExpressionPointcut.class为beanClass
  18. //默认多例,同步,将expression添加到bean definition的propertyValues中
  19. pointcutDefinition = createPointcutDefinition(expression);
  20. //这里走null
  21. pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));
  22. String pointcutBeanName = id;
  23. //有无id走不同方法
  24. if (StringUtils.hasText(pointcutBeanName)) {
  25. //走的工厂注册方法,上面跟踪过,可以回看3.3.1标记
  26. parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
  27. }
  28. else {
  29. pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
  30. }
  31. //注册组件,进入这个方法查看
  32. parserContext.registerComponent(
  33. new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
  34. }
  35. finally {
  36. //解析前放入,解析后弹出
  37. this.parseState.pop();
  38. }
  39. return pointcutDefinition;
  40. }
  41. /**
  42. * 此方法的实现在ParserContext类中
  43. */
  44. public void registerComponent(ComponentDefinition component) {
  45. //拿到parserContext中containingComponents属性里的最后一个元素
  46. //这个元素是在4.2标记中parse方法里被放入,组件名称叫做aop:config
  47. CompositeComponentDefinition containingComponent = getContainingComponent();
  48. if (containingComponent != null) {
  49. //增加嵌套的组件,进入这个方法
  50. containingComponent.addNestedComponent(component);
  51. }
  52. else {
  53. this.readerContext.fireComponentRegistered(component);
  54. }
  55. }
  56. /**
  57. * 这个方法在CompositeComponentDefinition类中
  58. *
  59. * Add the given component as nested element of this composite component.
  60. *
  61. * 增加指定的组件,作为这个组件的嵌套元素
  62. */
  63. public void addNestedComponent(ComponentDefinition component) {
  64. Assert.notNull(component, "ComponentDefinition must not be null");
  65. //也就是在CompositeComponentDefinition类中的nestedComponents属性中,
  66. //将入参的component添加进去
  67. //nestedComponents是一个LinkedList
  68. this.nestedComponents.add(component);
  69. }

4.2.3 parseAdvisor

跟踪标记4.2.3

此方法的实现在ConfigBeanDefinitionParser类中

  1. //4.2.3如果子标签是advisor
  2. parseAdvisor(elt, parserContext);
  3. /**
  4. * Parses the supplied {@code <advisor>} element and registers the resulting
  5. * {@link org.springframework.aop.Advisor} and any resulting {@link org.springframework.aop.Pointcut}
  6. * with the supplied {@link BeanDefinitionRegistry}.
  7. *
  8. * 解析 <advisor> 标签,并注册 Advisor 结果和其他 Pointcut 结果到工厂中
  9. */
  10. private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
  11. // 4.2.3.1 创建一个顾问的 BeanDefinition
  12. AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
  13. String id = advisorElement.getAttribute(ID);
  14. try {
  15. this.parseState.push(new AdvisorEntry(id));
  16. String advisorBeanName = id;
  17. if (StringUtils.hasText(advisorBeanName)) {
  18. // 注册到工厂
  19. parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
  20. }
  21. else {
  22. // 没有 id 属性,生成默认规则的名字再注册到工厂
  23. advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
  24. }
  25. // 4.2.3.2 解析切入点属性
  26. Object pointcut = parsePointcutProperty(advisorElement, parserContext);
  27. // advisorDef 添加 pointcut 等属性,然后将 advisorDef 注册到组件组合定义中
  28. if (pointcut instanceof BeanDefinition) {
  29. advisorDef.getPropertyValues().add(POINTCUT, pointcut);
  30. parserContext.registerComponent(
  31. new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
  32. }
  33. else if (pointcut instanceof String) {
  34. advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
  35. parserContext.registerComponent(
  36. new AdvisorComponentDefinition(advisorBeanName, advisorDef));
  37. }
  38. }
  39. finally {
  40. this.parseState.pop();
  41. }
  42. }
4.2.3.1 createAdvisorBeanDefinition

跟踪标记4.2.3.1

此方法的实现在ConfigBeanDefinitionParser类中

  1. // 4.2.3.1 创建一个顾问的 BeanDefinition
  2. AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
  3. /**
  4. * Create a {@link RootBeanDefinition} for the advisor described in the supplied. Does <strong>not</strong>
  5. * parse any associated '{@code pointcut}' or '{@code pointcut-ref}' attributes.
  6. *
  7. * 创建一个 advisor 的 RootBeanDefinition,不解析任何关联的 pointcut 或 pointcut-ref 属性
  8. */
  9. private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {
  10. RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);
  11. advisorDefinition.setSource(parserContext.extractSource(advisorElement));
  12. // 拿到 advisor 标签上的 advice-ref 属性
  13. String adviceRef = advisorElement.getAttribute(ADVICE_REF);
  14. if (!StringUtils.hasText(adviceRef)) {
  15. parserContext.getReaderContext().error(
  16. "'advice-ref' attribute contains empty value.", advisorElement, this.parseState.snapshot());
  17. }
  18. else {
  19. // 添加到属性键值对集合中
  20. advisorDefinition.getPropertyValues().add(
  21. ADVICE_BEAN_NAME, new RuntimeBeanNameReference(adviceRef));
  22. }
  23. // 拿到标签上的 order 属性
  24. if (advisorElement.hasAttribute(ORDER_PROPERTY)) {
  25. // 添加到属性键值对集合中
  26. advisorDefinition.getPropertyValues().add(
  27. ORDER_PROPERTY, advisorElement.getAttribute(ORDER_PROPERTY));
  28. }
  29. return advisorDefinition;
  30. }
4.2.3.2 parsePointcutProperty

跟踪标记4.2.3.2

此方法的实现在ConfigBeanDefinitionParser类中

  1. // 4.2.3.2 解析切入点属性
  2. Object pointcut = parsePointcutProperty(advisorElement, parserContext);
  3. /**
  4. * Parses the {@code pointcut} or {@code pointcut-ref} attributes of the supplied
  5. * {@link Element} and add a {@code pointcut} property as appropriate. Generates a
  6. * {@link org.springframework.beans.factory.config.BeanDefinition} for the pointcut if necessary
  7. * and returns its bean name, otherwise returns the bean name of the referred pointcut.
  8. *
  9. * 解析标签上的 pointcut 属性或者 pointcut-ref 属性,并适当的添加 pointcut 属性。
  10. * 如果必要的话生成一个 pointcut 的 BeanDefinition,并返回 bean name,否则返回引用的 pointcut 的 bean name
  11. */
  12. private Object parsePointcutProperty(Element element, ParserContext parserContext) {
  13. // pointcut 和 pointcut-ref 不能同时存在
  14. if (element.hasAttribute(POINTCUT) && element.hasAttribute(POINTCUT_REF)) {
  15. parserContext.getReaderContext().error(
  16. "Cannot define both 'pointcut' and 'pointcut-ref' on <advisor> tag.",
  17. element, this.parseState.snapshot());
  18. return null;
  19. }
  20. else if (element.hasAttribute(POINTCUT)) {
  21. // Create a pointcut for the anonymous pc and register it.
  22. // 为匿名电脑创建一个切入点并注册
  23. // 拿到标签上的 pointcut
  24. String expression = element.getAttribute(POINTCUT);
  25. // AspectJExpressionPointcut 作为 bean class 创建一个 beanDefinition
  26. // beanDefinition 设置同步、原型,表达式添加到属性键值对集合中
  27. AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
  28. pointcutDefinition.setSource(parserContext.extractSource(element));
  29. return pointcutDefinition;
  30. }
  31. else if (element.hasAttribute(POINTCUT_REF)) {
  32. // 拿到标签上的 pointcut-ref
  33. String pointcutRef = element.getAttribute(POINTCUT_REF);
  34. if (!StringUtils.hasText(pointcutRef)) {
  35. parserContext.getReaderContext().error(
  36. "'pointcut-ref' attribute contains empty value.", element, this.parseState.snapshot());
  37. return null;
  38. }
  39. // 直接返回字符串
  40. return pointcutRef;
  41. }
  42. else {
  43. parserContext.getReaderContext().error(
  44. "Must define one of 'pointcut' or 'pointcut-ref' on <advisor> tag.",
  45. element, this.parseState.snapshot());
  46. return null;
  47. }
  48. }

4.2.4 parseAspect

跟踪标记4.2.4

此方法的实现在ConfigBeanDefinitionParser类中

  1. //4.2.4如果子标签是aspect
  2. parseAspect(elt, parserContext);
  3. private void parseAspect(Element aspectElement, ParserContext parserContext) {
  4. // aspect 标签上的两属性
  5. String aspectId = aspectElement.getAttribute(ID);
  6. String aspectName = aspectElement.getAttribute(REF);
  7. try {
  8. this.parseState.push(new AspectEntry(aspectId, aspectName));
  9. // 存放 BeanDefinition
  10. List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
  11. // 存放字符串的引用对象
  12. List<BeanReference> beanReferences = new ArrayList<BeanReference>();
  13. // aspect 标签中有子标签 declare-parents
  14. List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
  15. for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
  16. Element declareParentsElement = declareParents.get(i);
  17. beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
  18. }
  19. // We have to parse "advice" and all the advice kinds in one loop, to get the
  20. // ordering semantics right.
  21. // 为了得到正确顺序,我们不得不在一个循环中解析"通知"和所有通知类型
  22. // 拿到标签下的子节点
  23. NodeList nodeList = aspectElement.getChildNodes();
  24. boolean adviceFoundAlready = false;
  25. for (int i = 0; i < nodeList.getLength(); i++) {
  26. Node node = nodeList.item(i);
  27. // 子节点是五种通知类型的情况下:前置、后置、最终、异常、环绕通知
  28. if (isAdviceNode(node, parserContext)) {
  29. // 在解析第一个通知时才进入
  30. if (!adviceFoundAlready) {
  31. adviceFoundAlready = true;
  32. if (!StringUtils.hasText(aspectName)) {
  33. parserContext.getReaderContext().error(
  34. "<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
  35. aspectElement, this.parseState.snapshot());
  36. return;
  37. }
  38. // 将 aspect 标签的 ref 属性值(字符串)包装后,添加到一个集合中
  39. beanReferences.add(new RuntimeBeanReference(aspectName));
  40. }
  41. // 4.2.4.1 解析通知
  42. AbstractBeanDefinition advisorDefinition = parseAdvice(
  43. aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
  44. // 返回的 Bean Definition 添加到另一个集合中
  45. beanDefinitions.add(advisorDefinition);
  46. }
  47. }
  48. // new 一个 AspectComponentDefinition
  49. AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
  50. aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
  51. parserContext.pushContainingComponent(aspectComponentDefinition);
  52. // 如果aspect标签下还有pointcut子标签,调用4.2.2方法进行解析
  53. List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
  54. for (Element pointcutElement : pointcuts) {
  55. parsePointcut(pointcutElement, parserContext);
  56. }
  57. // 将新创建的 AspectComponentDefinition 注册到组合组件定义器中
  58. parserContext.popAndRegisterContainingComponent();
  59. }
  60. finally {
  61. this.parseState.pop();
  62. }
  63. }
4.2.4.1 parseAdvice

跟踪标记4.2.4.1

此方法的实现在ConfigBeanDefinitionParser类中

  1. // 4.2.4.1 解析通知
  2. AbstractBeanDefinition advisorDefinition = parseAdvice(
  3. aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
  4. /**
  5. * Parses one of '{@code before}', '{@code after}', '{@code after-returning}',
  6. * '{@code after-throwing}' or '{@code around}' and registers the resulting
  7. * BeanDefinition with the supplied BeanDefinitionRegistry.
  8. *
  9. * 解析五个通知标签中的一个,并且注册最终的 BeanDefinition 到工厂中
  10. */
  11. private AbstractBeanDefinition parseAdvice(
  12. String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
  13. List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
  14. try {
  15. this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));
  16. // create the method factory bean
  17. // 创建一个 method factory bean 的 RootBeanDefinition
  18. RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
  19. methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
  20. methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
  21. methodDefinition.setSynthetic(true);
  22. // create instance factory definition
  23. // 创建一个 instance factory definition 的 RootBeanDefinition
  24. RootBeanDefinition aspectFactoryDef =
  25. new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
  26. aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
  27. aspectFactoryDef.setSynthetic(true);
  28. // register the pointcut
  29. // 注册 pointcut ,进入这个方法
  30. AbstractBeanDefinition adviceDef = createAdviceDefinition(
  31. adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
  32. beanDefinitions, beanReferences);
  33. // configure the advisor
  34. // 配置顾问
  35. // 创建一个顾问的 RootBeanDefinition
  36. RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
  37. advisorDefinition.setSource(parserContext.extractSource(adviceElement));
  38. // 将前面得到的通知的 BeanDefinition 添加到顾问的 constructorArgumentValues 中
  39. advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
  40. // 是否有优先级属性在 aspect 标签上,添加进属性键值对集合中
  41. if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
  42. advisorDefinition.getPropertyValues().add(
  43. ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
  44. }
  45. // register the final advisor
  46. // 注册最终顾问的 RootBeanDefinition
  47. parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
  48. return advisorDefinition;
  49. }
  50. finally {
  51. this.parseState.pop();
  52. }
  53. }
  54. /**
  55. * Creates the RootBeanDefinition for a POJO advice bean. Also causes pointcut
  56. * parsing to occur so that the pointcut may be associate with the advice bean.
  57. * This same pointcut is also configured as the pointcut for the enclosing
  58. * Advisor definition using the supplied MutablePropertyValues.
  59. *
  60. * 创建一个 POJO 通知 bean 的 RootBeanDefinition 。这使得切入点被解析,并和通知 bean 产生了关联。
  61. * 相同的切入点也会被封闭的顾问定义配置为切入点。顾问使用提供的 MutablePropertyValues
  62. */
  63. private AbstractBeanDefinition createAdviceDefinition(
  64. Element adviceElement, ParserContext parserContext, String aspectName, int order,
  65. RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
  66. List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
  67. // 根据不同的通知类型设置 beanClass,创建 RootBeanDefinition
  68. RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
  69. adviceDefinition.setSource(parserContext.extractSource(adviceElement));
  70. adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
  71. // 这个 order 是子标签(通知标签)被遍历时,所处集合的位置
  72. adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
  73. // returning 属性
  74. if (adviceElement.hasAttribute(RETURNING)) {
  75. adviceDefinition.getPropertyValues().add(
  76. RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
  77. }
  78. // throwing 属性
  79. if (adviceElement.hasAttribute(THROWING)) {
  80. adviceDefinition.getPropertyValues().add(
  81. THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
  82. }
  83. // arg-names 属性
  84. if (adviceElement.hasAttribute(ARG_NAMES)) {
  85. adviceDefinition.getPropertyValues().add(
  86. ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
  87. }
  88. ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
  89. // 添加上一层方法创建的 RootBeanDefinition
  90. cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
  91. // 如果是切入点的引用,直接返回字符串,否则创建一个 RootBeanDefinition ,设置表达式等属性并返回
  92. Object pointcut = parsePointcutProperty(adviceElement, parserContext
  93. // 添加解析了切入点属性后的返回值
  94. if (pointcut instanceof BeanDefinition) {
  95. cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
  96. // Bean Definition 的情况添加到一个集合
  97. beanDefinitions.add((BeanDefinition) pointcut);
  98. }
  99. else if (pointcut instanceof String) {
  100. RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
  101. cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
  102. // 字符串的情况添加到另外一个集合
  103. beanReferences.add(pointcutRef);
  104. }
  105. // 添加上一层方法创建的 RootBeanDefinition
  106. cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
  107. return adviceDefinition;
  108. }

4.2.5 popAndRegisterContainingComponent

跟踪标记4.2.5

此方法的实现在ParserContext类中

  1. //4.2.5弹出组件
  2. parserContext.popAndRegisterContainingComponent();
  3. public void popAndRegisterContainingComponent() {
  4. registerComponent(popContainingComponent());
  5. }
  6. public CompositeComponentDefinition popContainingComponent() {
  7. //先将ParserContext类containingComponents属性的组件弹出
  8. //比如aop:config,这个组件中包含着多个子标签对应的嵌套组件
  9. return (CompositeComponentDefinition) this.containingComponents.pop();
  10. }
  11. //拿到弹出的组件进行注册
  12. public void registerComponent(ComponentDefinition component) {
  13. //再拿到parserContext中的containingComponents属性中,最后一个组件
  14. //但是这里前面已经弹出,这里已经没有,所以返回null
  15. CompositeComponentDefinition containingComponent = getContainingComponent();
  16. if (containingComponent != null) {
  17. containingComponent.addNestedComponent(component);
  18. }
  19. else {
  20. //没做具体的实现
  21. this.readerContext.fireComponentRegistered(component);
  22. }
  23. }

到这里整个loadBeanDefinitions方法跟踪完成。

有兴趣的可以看下,spring配置文件中,组件扫描标签context:component-scan/的解析过程:

https://www.jianshu.com/p/e764a6959eeb

总结

4

  • 走解析自定义元素的方法。先拿到元素的名称空间URI,通过URI拿到对应的处理器,处理器中通常注册了许多解析器,每个解析器对应着该名称空间下的一个标签。
  • 4.2 利用解析器解析对应的元素标签

——————————————————————————————————

  • 4.2
  • 对于<aop:config>标签来说,其对应的解析器为 ConfigBeanDefinitionParser
  • 先创建一个组合组件定义器,标签名放入其中
  • 4.2.1 如果必要的话注册一个自动代理创建器
  • 4.2.2 然后拿到标签下的子标签,一共三种:pointcutadvisoraspect,根据不同标签走不同方法

——————————————————————————————————

  • 4.2.1
  • 工厂中已经注册了创建器,则根据其 BeanDefinition 中 beanClassName 的优先级和入参Class对象的优先级对比进行选择,入参Class对象优先级高则设置为 BeanDefinition 新的 beanClassName ;工厂中如果没有注册创建器,则创建 RootBeanDefinition,并在工厂中注册创建器。
  • 如果<aop:config>标签上有配置属性proxy-target-class或者expose-proxy等,则将其添加到 BeanDefinition 的属性键值对集合中。
  • 如果创建器是新创建的,则注册创建器到parserContext中,其实就是将创建器的 BeanDefinition 添加到组合组件定义器中

——————————————————————————————————

  • 4.2.2
  • 标签pointcut方法:创建 bean definition,指定 beanClass,默认多例,同步,将切入点表达式expression添加到bean definition的propertyValues中;将 bean definition 注册到工厂;包裹 bean definition 注册到组合组件定义器中。
  • 标签advisor方法:创建 bean definition,指定 beanClass,以adviceBeanName为key,将标签上属性advice-ref的值以 RuntimeBeanNameReference 类包装后,添加到 BeanDefinition 的属性键值对集合中;然后注册 bean definition; 解析pointcut-ref或者pointcut属性,添加到 BeanDefinition 的属性键值对集合中;包裹 bean definition 注册到组合组件定义器中。
  • 标签aspect方法

——————————————————————————————————

  • 标签aspect方法
  • 拿到aspect标签上属性 id、ref 的值,建两集合,一个放 BeanDefinition ,一个放字符串的引用对象。
  • 遍历子标签,在扫描到第一个子标签时用 RuntimeBeanReference 类包装 ref 字符串,也就是 aspectName,并将包装类放入到存放字符串引用的集合中。
  • 4.2.4.1 遍历时解析通知,将返回的 BeanDefinition 存放到另一个集合中
  • 利用 id、两集合新建一个 AspectComponentDefinition
  • 如果aspect标签下还有pointcut子标签,调用标签pointcut方法进行解析
  • 将新创建的 AspectComponentDefinition 注册到组合组件定义器中。

——————————————————————————————————

  • 4.2.4.1
  • 创建 RootBeanDefinition (methodDefinition),设置MethodLocatingFactoryBean.class为 beanClass、设置为同步、将标签aspect上的 ref 属性值和通知标签(例如:aop:before)上的 method 属性值添加到 methodDefinition 的属性键值对集合中
  • 创建 RootBeanDefinition (aspectFactoryDef),设置SimpleBeanFactoryAwareAspectInstanceFactory.class为 beanClass、设置为同步、将标签aspect上的 ref 属性值添加到 aspectFactoryDef 的属性键值对集合中
  • 创建 RootBeanDefinition (adviceDefinition),根据不同的通知类型设置 beanClass,将 ref 属性值、子标签遍历时所处的集合位置、子标签(通知标签)上的属性值添加到 adviceDefinition的属性键值对集合中;
  • 解析通知标签上 pointcut 或 pointcut-ref 属性,返回 BeanDefinition 或者字符串,根据返回值不同放入到不同的集合中;拿到 adviceDefinition 的 constructorArgumentValues 属性,依次添加 methodDefinition、BeanDefinition / 字符串、aspectFactoryDef 到 constructorArgumentValues 中;
  • 创建 RootBeanDefinition (advisorDefinition),设置AspectJPointcutAdvisor.class为 beanClass,拿到 advisorDefinition 的 constructorArgumentValues 属性,添加 adviceDefinition
  • 注册 advisorDefinition 到工厂中,并返回 advisorDefinition

loadBeanDefinitions方法源码跟踪(三)的更多相关文章

  1. loadBeanDefinitions方法源码跟踪(一)

    看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 AbstractBeanDe ...

  2. loadBeanDefinitions方法源码跟踪(二)

    因为字数超过了限制,所以分成了三篇,承接上篇: https://www.jianshu.com/p/a0cfaedf3fc5 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 3.parseDef ...

  3. prepareRefresh方法源码跟踪

    看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 AbstractApplic ...

  4. prepareBeanFactory方法源码跟踪

    看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 AbstractApplic ...

  5. obtainFreshBeanFactory方法源码跟踪

    看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 AbstractApplic ...

  6. postProcessBeanFactory方法源码跟踪

    看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 AbstractApplic ...

  7. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  8. spring security 认证源码跟踪

    spring security 认证源码跟踪 ​ 在跟踪认证源码之前,我们先根据官网说明一下security的内部原理,主要是依据一系列的filter来实现,大家可以根据https://docs.sp ...

  9. spring security 授权方式(自定义)及源码跟踪

    spring security 授权方式(自定义)及源码跟踪 ​ 这节我们来看看spring security的几种授权方式,及简要的源码跟踪.在初步接触spring security时,为了实现它的 ...

随机推荐

  1. if 条件的 true / false 及 select 的值

    if 条件的 true / false 任何不是 false, undefined, null, 0, NaN 的值,或一个空字符串('')在作为条件语句进行测试时实际返回true,因此您可以简单地使 ...

  2. UDP通讯代码

    UDP客户端代码: import socket # 创建套接字 socket.AF_INET:IPV4 socket.SOCK_DGRAM:UDP协议 udp_client=socket.socket ...

  3. js中怎么把类数组转化为数组

    说起伪数组,首先想到arguments, 这个我们函数参数的一个类数组,是类数组的代表. 1.拥有length属性,可以使用下标来访问元素,这两点和数组相同. 2.不能使用数组的方法,他们不能使用Ar ...

  4. Python数据类型-5 元组

    元组 我们知道,用方括号括起来的是列表,那么用圆括号括起来的是什么,是元组. 元组也是序列结构,但是是一种不可变序列,你可以简单的理解为内容不可变的列表.除了在内部元素不可修改的区别外,元组和列表的用 ...

  5. day22-Python运维开发基础(正则函数 / 异常处理)

    1. 正则函数 # ### 正则表达式 => 正则函数 import re # search 通过正则匹配出第一个对象返回,通过group取出对象中的值 strvar = "5*7 9 ...

  6. C/C++ - CallBack

    这是实验楼上一个callback debug例子,我没有提交结果,但在本地上运行没有任何问题,也无警告: #include <stdio.h> #define MAX 3 typedef ...

  7. windows网络编程-C语言实现简单的UDP协议聊天

    与TCP协议下编写服务端程序代码类似,但因为是无连接的形式,所以不需要监听. 这次,我用了一点不同的想法:我建立一个服务端,用了两个端口和两个套接字,把服务端作为一个数据转发的中转站,使得客户机之间进 ...

  8. 把链接生成二维码 二维码中间带有logo

    在工程中引入三个文件jquery.qrcode.js.qrcode.js.utf.js.其中utf.js文件是防止链接中的参数出现中文乱码现象 jquery.qrcode.js文件 function ...

  9. Codeforces1307B. Cow and Friend

    本题的难点是可以在y轴正轴动,但也是突破点,知道x轴都是整数,那么对于任意长度来说,能到达的最短是1,最长是本身长度,那么我们就选择最长的距离,跳到一个点,使这个点为再跳就超过终点,那么就可以用2次跳 ...

  10. Python 之并发编程之进程上(基本概念、并行并发、cpu调度、阻塞 )

    一: 进程的概念:(Process) 进程就是正在运行的程序,它是操作系统中,资源分配的最小单位. 资源分配:分配的是cpu和内存等物理资源 进程号是进程的唯一标识 同一个程序执行两次之后是两个进程 ...