001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.config; 018 019import java.util.LinkedHashMap; 020import java.util.Map; 021 022import org.springframework.beans.factory.config.BeanDefinition; 023import org.springframework.beans.factory.config.RuntimeBeanReference; 024import org.springframework.beans.factory.parsing.BeanComponentDefinition; 025import org.springframework.beans.factory.support.RootBeanDefinition; 026import org.springframework.beans.factory.xml.ParserContext; 027import org.springframework.util.AntPathMatcher; 028import org.springframework.util.PathMatcher; 029import org.springframework.web.cors.CorsConfiguration; 030import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping; 031import org.springframework.web.servlet.handler.HandlerMappingIntrospector; 032import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter; 033import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter; 034import org.springframework.web.util.UrlPathHelper; 035 036/** 037 * Convenience methods for use in MVC namespace BeanDefinitionParsers. 038 * 039 * @author Rossen Stoyanchev 040 * @author Brian Clozel 041 * @since 3.1 042 */ 043public abstract class MvcNamespaceUtils { 044 045 private static final String BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME = 046 BeanNameUrlHandlerMapping.class.getName(); 047 048 private static final String SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME = 049 SimpleControllerHandlerAdapter.class.getName(); 050 051 private static final String HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME = 052 HttpRequestHandlerAdapter.class.getName(); 053 054 private static final String URL_PATH_HELPER_BEAN_NAME = "mvcUrlPathHelper"; 055 056 private static final String PATH_MATCHER_BEAN_NAME = "mvcPathMatcher"; 057 058 private static final String CORS_CONFIGURATION_BEAN_NAME = "mvcCorsConfigurations"; 059 060 private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"; 061 062 063 public static void registerDefaultComponents(ParserContext parserContext, Object source) { 064 registerBeanNameUrlHandlerMapping(parserContext, source); 065 registerHttpRequestHandlerAdapter(parserContext, source); 066 registerSimpleControllerHandlerAdapter(parserContext, source); 067 registerHandlerMappingIntrospector(parserContext, source); 068 } 069 070 /** 071 * Adds an alias to an existing well-known name or registers a new instance of a {@link UrlPathHelper} 072 * under that well-known name, unless already registered. 073 * @return a RuntimeBeanReference to this {@link UrlPathHelper} instance 074 */ 075 public static RuntimeBeanReference registerUrlPathHelper( 076 RuntimeBeanReference urlPathHelperRef, ParserContext parserContext, Object source) { 077 078 if (urlPathHelperRef != null) { 079 if (parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME)) { 080 parserContext.getRegistry().removeAlias(URL_PATH_HELPER_BEAN_NAME); 081 } 082 parserContext.getRegistry().registerAlias(urlPathHelperRef.getBeanName(), URL_PATH_HELPER_BEAN_NAME); 083 } 084 else if (!parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME) && 085 !parserContext.getRegistry().containsBeanDefinition(URL_PATH_HELPER_BEAN_NAME)) { 086 RootBeanDefinition urlPathHelperDef = new RootBeanDefinition(UrlPathHelper.class); 087 urlPathHelperDef.setSource(source); 088 urlPathHelperDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 089 parserContext.getRegistry().registerBeanDefinition(URL_PATH_HELPER_BEAN_NAME, urlPathHelperDef); 090 parserContext.registerComponent(new BeanComponentDefinition(urlPathHelperDef, URL_PATH_HELPER_BEAN_NAME)); 091 } 092 return new RuntimeBeanReference(URL_PATH_HELPER_BEAN_NAME); 093 } 094 095 /** 096 * Adds an alias to an existing well-known name or registers a new instance of a {@link PathMatcher} 097 * under that well-known name, unless already registered. 098 * @return a RuntimeBeanReference to this {@link PathMatcher} instance 099 */ 100 public static RuntimeBeanReference registerPathMatcher( 101 RuntimeBeanReference pathMatcherRef, ParserContext parserContext, Object source) { 102 103 if (pathMatcherRef != null) { 104 if (parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME)) { 105 parserContext.getRegistry().removeAlias(PATH_MATCHER_BEAN_NAME); 106 } 107 parserContext.getRegistry().registerAlias(pathMatcherRef.getBeanName(), PATH_MATCHER_BEAN_NAME); 108 } 109 else if (!parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME) && 110 !parserContext.getRegistry().containsBeanDefinition(PATH_MATCHER_BEAN_NAME)) { 111 RootBeanDefinition pathMatcherDef = new RootBeanDefinition(AntPathMatcher.class); 112 pathMatcherDef.setSource(source); 113 pathMatcherDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 114 parserContext.getRegistry().registerBeanDefinition(PATH_MATCHER_BEAN_NAME, pathMatcherDef); 115 parserContext.registerComponent(new BeanComponentDefinition(pathMatcherDef, PATH_MATCHER_BEAN_NAME)); 116 } 117 return new RuntimeBeanReference(PATH_MATCHER_BEAN_NAME); 118 } 119 120 /** 121 * Registers an {@link HttpRequestHandlerAdapter} under a well-known 122 * name unless already registered. 123 */ 124 private static void registerBeanNameUrlHandlerMapping(ParserContext context, Object source) { 125 if (!context.getRegistry().containsBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME)) { 126 RootBeanDefinition mappingDef = new RootBeanDefinition(BeanNameUrlHandlerMapping.class); 127 mappingDef.setSource(source); 128 mappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 129 mappingDef.getPropertyValues().add("order", 2); // consistent with WebMvcConfigurationSupport 130 RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source); 131 mappingDef.getPropertyValues().add("corsConfigurations", corsRef); 132 context.getRegistry().registerBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME, mappingDef); 133 context.registerComponent(new BeanComponentDefinition(mappingDef, BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME)); 134 } 135 } 136 137 /** 138 * Registers an {@link HttpRequestHandlerAdapter} under a well-known 139 * name unless already registered. 140 */ 141 private static void registerHttpRequestHandlerAdapter(ParserContext context, Object source) { 142 if (!context.getRegistry().containsBeanDefinition(HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME)) { 143 RootBeanDefinition adapterDef = new RootBeanDefinition(HttpRequestHandlerAdapter.class); 144 adapterDef.setSource(source); 145 adapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 146 context.getRegistry().registerBeanDefinition(HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME, adapterDef); 147 context.registerComponent(new BeanComponentDefinition(adapterDef, HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME)); 148 } 149 } 150 151 /** 152 * Registers a {@link SimpleControllerHandlerAdapter} under a well-known 153 * name unless already registered. 154 */ 155 private static void registerSimpleControllerHandlerAdapter(ParserContext context, Object source) { 156 if (!context.getRegistry().containsBeanDefinition(SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME)) { 157 RootBeanDefinition beanDef = new RootBeanDefinition(SimpleControllerHandlerAdapter.class); 158 beanDef.setSource(source); 159 beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 160 context.getRegistry().registerBeanDefinition(SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME, beanDef); 161 context.registerComponent(new BeanComponentDefinition(beanDef, SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME)); 162 } 163 } 164 165 /** 166 * Registers a {@code Map<String, CorsConfiguration>} (mapped {@code CorsConfiguration}s) 167 * under a well-known name unless already registered. The bean definition may be updated 168 * if a non-null CORS configuration is provided. 169 * @return a RuntimeBeanReference to this {@code Map<String, CorsConfiguration>} instance 170 */ 171 public static RuntimeBeanReference registerCorsConfigurations( 172 Map<String, CorsConfiguration> corsConfigurations, ParserContext context, Object source) { 173 174 if (!context.getRegistry().containsBeanDefinition(CORS_CONFIGURATION_BEAN_NAME)) { 175 RootBeanDefinition corsDef = new RootBeanDefinition(LinkedHashMap.class); 176 corsDef.setSource(source); 177 corsDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 178 if (corsConfigurations != null) { 179 corsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations); 180 } 181 context.getReaderContext().getRegistry().registerBeanDefinition(CORS_CONFIGURATION_BEAN_NAME, corsDef); 182 context.registerComponent(new BeanComponentDefinition(corsDef, CORS_CONFIGURATION_BEAN_NAME)); 183 } 184 else if (corsConfigurations != null) { 185 BeanDefinition corsDef = context.getRegistry().getBeanDefinition(CORS_CONFIGURATION_BEAN_NAME); 186 corsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations); 187 } 188 return new RuntimeBeanReference(CORS_CONFIGURATION_BEAN_NAME); 189 } 190 191 /** 192 * Registers an {@link HandlerMappingIntrospector} under a well-known name 193 * unless already registered. 194 */ 195 private static void registerHandlerMappingIntrospector(ParserContext parserContext, Object source) { 196 if (!parserContext.getRegistry().containsBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) { 197 RootBeanDefinition beanDef = new RootBeanDefinition(HandlerMappingIntrospector.class); 198 beanDef.setSource(source); 199 beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 200 beanDef.setLazyInit(true); 201 parserContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, beanDef); 202 parserContext.registerComponent(new BeanComponentDefinition(beanDef, HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)); 203 } 204 } 205 206 /** 207 * Find the {@code ContentNegotiationManager} bean created by or registered 208 * with the {@code annotation-driven} element. 209 * @return a bean definition, bean reference, or {@code null} if none defined 210 */ 211 public static Object getContentNegotiationManager(ParserContext context) { 212 String name = AnnotationDrivenBeanDefinitionParser.HANDLER_MAPPING_BEAN_NAME; 213 if (context.getRegistry().containsBeanDefinition(name)) { 214 BeanDefinition handlerMappingBeanDef = context.getRegistry().getBeanDefinition(name); 215 return handlerMappingBeanDef.getPropertyValues().get("contentNegotiationManager"); 216 } 217 name = AnnotationDrivenBeanDefinitionParser.CONTENT_NEGOTIATION_MANAGER_BEAN_NAME; 218 if (context.getRegistry().containsBeanDefinition(name)) { 219 return new RuntimeBeanReference(name); 220 } 221 return null; 222 } 223 224}