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.lang.Nullable; 028import org.springframework.util.AntPathMatcher; 029import org.springframework.util.PathMatcher; 030import org.springframework.web.cors.CorsConfiguration; 031import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping; 032import org.springframework.web.servlet.handler.HandlerMappingIntrospector; 033import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter; 034import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter; 035import org.springframework.web.util.UrlPathHelper; 036 037/** 038 * Convenience methods for use in MVC namespace BeanDefinitionParsers. 039 * 040 * @author Rossen Stoyanchev 041 * @author Brian Clozel 042 * @since 3.1 043 */ 044public abstract class MvcNamespaceUtils { 045 046 private static final String BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME = 047 BeanNameUrlHandlerMapping.class.getName(); 048 049 private static final String SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME = 050 SimpleControllerHandlerAdapter.class.getName(); 051 052 private static final String HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME = 053 HttpRequestHandlerAdapter.class.getName(); 054 055 private static final String URL_PATH_HELPER_BEAN_NAME = "mvcUrlPathHelper"; 056 057 private static final String PATH_MATCHER_BEAN_NAME = "mvcPathMatcher"; 058 059 private static final String CORS_CONFIGURATION_BEAN_NAME = "mvcCorsConfigurations"; 060 061 private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"; 062 063 064 public static void registerDefaultComponents(ParserContext parserContext, @Nullable Object source) { 065 registerBeanNameUrlHandlerMapping(parserContext, source); 066 registerHttpRequestHandlerAdapter(parserContext, source); 067 registerSimpleControllerHandlerAdapter(parserContext, source); 068 registerHandlerMappingIntrospector(parserContext, source); 069 } 070 071 /** 072 * Adds an alias to an existing well-known name or registers a new instance of a {@link UrlPathHelper} 073 * under that well-known name, unless already registered. 074 * @return a RuntimeBeanReference to this {@link UrlPathHelper} instance 075 */ 076 public static RuntimeBeanReference registerUrlPathHelper( 077 @Nullable RuntimeBeanReference urlPathHelperRef, ParserContext parserContext, @Nullable Object source) { 078 079 if (urlPathHelperRef != null) { 080 if (parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME)) { 081 parserContext.getRegistry().removeAlias(URL_PATH_HELPER_BEAN_NAME); 082 } 083 parserContext.getRegistry().registerAlias(urlPathHelperRef.getBeanName(), URL_PATH_HELPER_BEAN_NAME); 084 } 085 else if (!parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME) && 086 !parserContext.getRegistry().containsBeanDefinition(URL_PATH_HELPER_BEAN_NAME)) { 087 RootBeanDefinition urlPathHelperDef = new RootBeanDefinition(UrlPathHelper.class); 088 urlPathHelperDef.setSource(source); 089 urlPathHelperDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 090 parserContext.getRegistry().registerBeanDefinition(URL_PATH_HELPER_BEAN_NAME, urlPathHelperDef); 091 parserContext.registerComponent(new BeanComponentDefinition(urlPathHelperDef, URL_PATH_HELPER_BEAN_NAME)); 092 } 093 return new RuntimeBeanReference(URL_PATH_HELPER_BEAN_NAME); 094 } 095 096 /** 097 * Adds an alias to an existing well-known name or registers a new instance of a {@link PathMatcher} 098 * under that well-known name, unless already registered. 099 * @return a RuntimeBeanReference to this {@link PathMatcher} instance 100 */ 101 public static RuntimeBeanReference registerPathMatcher(@Nullable RuntimeBeanReference pathMatcherRef, 102 ParserContext parserContext, @Nullable Object source) { 103 104 if (pathMatcherRef != null) { 105 if (parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME)) { 106 parserContext.getRegistry().removeAlias(PATH_MATCHER_BEAN_NAME); 107 } 108 parserContext.getRegistry().registerAlias(pathMatcherRef.getBeanName(), PATH_MATCHER_BEAN_NAME); 109 } 110 else if (!parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME) && 111 !parserContext.getRegistry().containsBeanDefinition(PATH_MATCHER_BEAN_NAME)) { 112 RootBeanDefinition pathMatcherDef = new RootBeanDefinition(AntPathMatcher.class); 113 pathMatcherDef.setSource(source); 114 pathMatcherDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 115 parserContext.getRegistry().registerBeanDefinition(PATH_MATCHER_BEAN_NAME, pathMatcherDef); 116 parserContext.registerComponent(new BeanComponentDefinition(pathMatcherDef, PATH_MATCHER_BEAN_NAME)); 117 } 118 return new RuntimeBeanReference(PATH_MATCHER_BEAN_NAME); 119 } 120 121 /** 122 * Registers an {@link HttpRequestHandlerAdapter} under a well-known 123 * name unless already registered. 124 */ 125 private static void registerBeanNameUrlHandlerMapping(ParserContext context, @Nullable Object source) { 126 if (!context.getRegistry().containsBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME)) { 127 RootBeanDefinition mappingDef = new RootBeanDefinition(BeanNameUrlHandlerMapping.class); 128 mappingDef.setSource(source); 129 mappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 130 mappingDef.getPropertyValues().add("order", 2); // consistent with WebMvcConfigurationSupport 131 RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source); 132 mappingDef.getPropertyValues().add("corsConfigurations", corsRef); 133 context.getRegistry().registerBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME, mappingDef); 134 context.registerComponent(new BeanComponentDefinition(mappingDef, BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME)); 135 } 136 } 137 138 /** 139 * Registers an {@link HttpRequestHandlerAdapter} under a well-known 140 * name unless already registered. 141 */ 142 private static void registerHttpRequestHandlerAdapter(ParserContext context, @Nullable Object source) { 143 if (!context.getRegistry().containsBeanDefinition(HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME)) { 144 RootBeanDefinition adapterDef = new RootBeanDefinition(HttpRequestHandlerAdapter.class); 145 adapterDef.setSource(source); 146 adapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 147 context.getRegistry().registerBeanDefinition(HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME, adapterDef); 148 context.registerComponent(new BeanComponentDefinition(adapterDef, HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME)); 149 } 150 } 151 152 /** 153 * Registers a {@link SimpleControllerHandlerAdapter} under a well-known 154 * name unless already registered. 155 */ 156 private static void registerSimpleControllerHandlerAdapter(ParserContext context, @Nullable Object source) { 157 if (!context.getRegistry().containsBeanDefinition(SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME)) { 158 RootBeanDefinition beanDef = new RootBeanDefinition(SimpleControllerHandlerAdapter.class); 159 beanDef.setSource(source); 160 beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 161 context.getRegistry().registerBeanDefinition(SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME, beanDef); 162 context.registerComponent(new BeanComponentDefinition(beanDef, SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME)); 163 } 164 } 165 166 /** 167 * Registers a {@code Map<String, CorsConfiguration>} (mapped {@code CorsConfiguration}s) 168 * under a well-known name unless already registered. The bean definition may be updated 169 * if a non-null CORS configuration is provided. 170 * @return a RuntimeBeanReference to this {@code Map<String, CorsConfiguration>} instance 171 */ 172 public static RuntimeBeanReference registerCorsConfigurations( 173 @Nullable Map<String, CorsConfiguration> corsConfigurations, 174 ParserContext context, @Nullable Object source) { 175 176 if (!context.getRegistry().containsBeanDefinition(CORS_CONFIGURATION_BEAN_NAME)) { 177 RootBeanDefinition corsDef = new RootBeanDefinition(LinkedHashMap.class); 178 corsDef.setSource(source); 179 corsDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 180 if (corsConfigurations != null) { 181 corsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations); 182 } 183 context.getReaderContext().getRegistry().registerBeanDefinition(CORS_CONFIGURATION_BEAN_NAME, corsDef); 184 context.registerComponent(new BeanComponentDefinition(corsDef, CORS_CONFIGURATION_BEAN_NAME)); 185 } 186 else if (corsConfigurations != null) { 187 BeanDefinition corsDef = context.getRegistry().getBeanDefinition(CORS_CONFIGURATION_BEAN_NAME); 188 corsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations); 189 } 190 return new RuntimeBeanReference(CORS_CONFIGURATION_BEAN_NAME); 191 } 192 193 /** 194 * Registers an {@link HandlerMappingIntrospector} under a well-known name 195 * unless already registered. 196 */ 197 private static void registerHandlerMappingIntrospector(ParserContext parserContext, @Nullable Object source) { 198 if (!parserContext.getRegistry().containsBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) { 199 RootBeanDefinition beanDef = new RootBeanDefinition(HandlerMappingIntrospector.class); 200 beanDef.setSource(source); 201 beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 202 beanDef.setLazyInit(true); 203 parserContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, beanDef); 204 parserContext.registerComponent(new BeanComponentDefinition(beanDef, HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)); 205 } 206 } 207 208 /** 209 * Find the {@code ContentNegotiationManager} bean created by or registered 210 * with the {@code annotation-driven} element. 211 * @return a bean definition, bean reference, or {@code null} if none defined 212 */ 213 @Nullable 214 public static Object getContentNegotiationManager(ParserContext context) { 215 String name = AnnotationDrivenBeanDefinitionParser.HANDLER_MAPPING_BEAN_NAME; 216 if (context.getRegistry().containsBeanDefinition(name)) { 217 BeanDefinition handlerMappingBeanDef = context.getRegistry().getBeanDefinition(name); 218 return handlerMappingBeanDef.getPropertyValues().get("contentNegotiationManager"); 219 } 220 name = AnnotationDrivenBeanDefinitionParser.CONTENT_NEGOTIATION_MANAGER_BEAN_NAME; 221 if (context.getRegistry().containsBeanDefinition(name)) { 222 return new RuntimeBeanReference(name); 223 } 224 return null; 225 } 226 227}