[关闭]
@joshsulin 2018-07-20T03:03:36.000000Z 字数 6211 阅读 657

mvc:interceptors 自定义标签运行原理

技术分享


回顾 mvc:interceptors 的功能及作用

参考网上文章: https://blog.csdn.net/tong_xinglong/article/details/52035131

回顾 SpringMVC http 请求处理流程

此处输入图片的描述

引出思考, 如何需要我们自己实现 mvc:interceptors 的功能, 应该如何开发?

??????

通过阅读Spring的源码, 学习Spring针对该功能的实现逻辑及思路

Spring如何对请求进行拦截?

Spring项目启动时如何将interceptors配置加载到Spring上下文里

开发流程
1、创建一个需要扩展的组件。
2、定义一个XSD文件描述组件内容。
3、创建一个文件, 实现BeanDefinitionParser接口, 用来解析XSD文件中的定义和组件定义。
4、创建一个Handler文件, 扩展自NamespaceHandlerSuport, 目的就是实现我们该自定义标签要完成的功能。
5、编写Spring.handlers和Spring.schemas文件。

归功于Spring在设计之初就一贯坚持的设计原则: 开闭原则: 对扩展开放(Open for extension), 对修改关闭(Closed for modification)。所以我们可以在不修改Spring源代码的情况下扩展Spring的功能。

下面我们就来看一下Spring对应XML标签的处理

  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  2. if (delegate.isDefaultNamespace(root)) {
  3. NodeList nl = root.getChildNodes();
  4. for (int i = 0; i < nl.getLength(); i++) {
  5. Node node = nl.item(i);
  6. if (node instanceof Element) {
  7. Element ele = (Element) node;
  8. if (delegate.isDefaultNamespace(ele)) {
  9. parseDefaultElement(ele, delegate);
  10. }
  11. else {
  12. delegate.parseCustomElement(ele);
  13. }
  14. }
  15. }
  16. }
  17. else {
  18. delegate.parseCustomElement(root);
  19. }
  20. }

上面的代码逻辑还是很清晰的, 一种处理默认标签, 如

  1. <bean id="test" class="com.Test"/>

另一类是自定义的。如下

  1. <mvc:interceptors>

下面我们来看一下自定义标签的解析原理

  1. public class BeanDefinitionParserDelegate {
  2. public BeanDefinition parseCustomElement(Element ele) {
  3. return parseCustomElement(ele, null);
  4. }
  5. public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
  6. String namespaceUri = getNamespaceURI(ele);
  7. NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
  8. if (handler == null) {
  9. error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
  10. return null;
  11. }
  12. return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
  13. }
  14. }

我们看到, 其实自定义标签的处理流程很简单。
1、获取到命名空间
2、根据命名空间找到对应的命名空间处理器
3、根据用户自定义的处理器进行解析

第一步很简单, 我们跳过。我们来看一下第二步的实现。

  1. public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
  2. /**
  3. * Locate the {@link NamespaceHandler} for the supplied namespace URI
  4. * from the configured mappings.
  5. * @param namespaceUri the relevant namespace URI
  6. * @return the located {@link NamespaceHandler}, or {@code null} if none found
  7. */
  8. @Override
  9. public NamespaceHandler resolve(String namespaceUri) {
  10. // 获取所有已经配置的handler映射
  11. Map<String, Object> handlerMappings = getHandlerMappings();
  12. // 根据命名空间获取对应的信息
  13. Object handlerOrClassName = handlerMappings.get(namespaceUri);
  14. if (handlerOrClassName == null) {
  15. return null;
  16. }
  17. else if (handlerOrClassName instanceof NamespaceHandler) {
  18. // 已经做过解析的直接从缓存中获取
  19. return (NamespaceHandler) handlerOrClassName;
  20. }
  21. else {
  22. // 没有做过解析的,则返回的是类路径
  23. String className = (String) handlerOrClassName;
  24. try {
  25. // 使用反射将类路径转化为类
  26. Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
  27. if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
  28. throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
  29. "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
  30. }
  31. // 初始化处理器
  32. NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
  33. // 调用自定义的NameSpaceHandler的init方法
  34. namespaceHandler.init();
  35. // 存入缓存
  36. handlerMappings.put(namespaceUri, namespaceHandler);
  37. return namespaceHandler;
  38. }
  39. catch (ClassNotFoundException ex) {
  40. throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
  41. namespaceUri + "] not found", ex);
  42. }
  43. catch (LinkageError err) {
  44. throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
  45. namespaceUri + "]: problem with handler class file or dependent class", err);
  46. }
  47. }
  48. }
  49. /**
  50. * Load the specified NamespaceHandler mappings lazily.
  51. */
  52. private Map<String, Object> getHandlerMappings() {
  53. // 如果没有缓存则开始进行缓存
  54. if (this.handlerMappings == null) {
  55. synchronized (this) {
  56. if (this.handlerMappings == null) {
  57. try {
  58. // this.handlerMappingsLocation在构造函数中初始化为META-INF/spring.handlers
  59. Properties mappings =
  60. PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
  61. if (logger.isDebugEnabled()) {
  62. logger.debug("Loaded NamespaceHandler mappings: " + mappings);
  63. }
  64. Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
  65. CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
  66. this.handlerMappings = handlerMappings;
  67. }
  68. catch (IOException ex) {
  69. throw new IllegalStateException(
  70. "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
  71. }
  72. }
  73. }
  74. }
  75. return this.handlerMappings;
  76. }
  77. }

现在我们知道了为什么需要在METE-INF/spring.handlers文件中那样配置了。因为在加载所有的处理器的配置的默认目录就是META-INF/spring.handlers

通过上面代码可以看到, 初始化时都调用了 init 方法

  1. // 调用自定义的NameSpaceHandler的init方法
  2. namespaceHandler.init();

进入init方法进行分析。

  1. public class MvcNamespaceHandler extends NamespaceHandlerSupport {
  2. @Override
  3. public void init() {
  4. registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
  5. registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
  6. registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
  7. registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
  8. registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
  9. registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
  10. registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
  11. registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
  12. registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
  13. registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
  14. registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
  15. registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
  16. registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
  17. registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
  18. }
  19. }

registerBeanDefinitionParser 方法由父抽象类NamespaceHandlerSupport默认实现。

  1. protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
  2. this.parsers.put(elementName, parser);
  3. }

第一个参数是elementName, 即元素名称, 即告诉Spring你要解析哪个标签, 第二个参数是BeanDefinitionParser的实现类, BeanDefinitionParser是Spring用来将xml元素转换成BeanDefinition对象的接口。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注