001/* 002 * Copyright 2002-2012 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.mvc.support; 018 019import org.springframework.util.ClassUtils; 020import org.springframework.util.StringUtils; 021 022/** 023 * Implementation of {@link org.springframework.web.servlet.HandlerMapping} that 024 * follows a simple convention for generating URL path mappings from the <i>class names</i> 025 * of registered {@link org.springframework.web.servlet.mvc.Controller} beans 026 * as well as {@code @Controller} annotated beans. 027 * 028 * <p>For simple {@link org.springframework.web.servlet.mvc.Controller} implementations 029 * (those that handle a single request type), the convention is to take the 030 * {@link ClassUtils#getShortName short name} of the {@code Class}, 031 * remove the 'Controller' suffix if it exists and return the remaining text, lower-cased, 032 * as the mapping, with a leading {@code /}. For example: 033 * <ul> 034 * <li>{@code WelcomeController} -> {@code /welcome*}</li> 035 * <li>{@code HomeController} -> {@code /home*}</li> 036 * </ul> 037 * 038 * <p>For {@code MultiActionController MultiActionControllers} and {@code @Controller} 039 * beans, a similar mapping is registered, except that all sub-paths are registered 040 * using the trailing wildcard pattern {@code /*}. For example: 041 * <ul> 042 * <li>{@code WelcomeController} -> {@code /welcome}, {@code /welcome/*}</li> 043 * <li>{@code CatalogController} -> {@code /catalog}, {@code /catalog/*}</li> 044 * </ul> 045 * 046 * <p>For {@code MultiActionController} it is often useful to use 047 * this mapping strategy in conjunction with the 048 * {@link org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver}. 049 * 050 * <p>Thanks to Warren Oliver for suggesting the "caseSensitive", "pathPrefix" 051 * and "basePackage" properties which have been added in Spring 2.5. 052 * 053 * @author Rob Harrop 054 * @author Juergen Hoeller 055 * @since 2.0 056 * @see org.springframework.web.servlet.mvc.Controller 057 * @see org.springframework.web.servlet.mvc.multiaction.MultiActionController 058 * @deprecated as of 4.3, in favor of annotation-driven handler methods 059 */ 060@Deprecated 061public class ControllerClassNameHandlerMapping extends AbstractControllerUrlHandlerMapping { 062 063 /** 064 * Common suffix at the end of controller implementation classes. 065 * Removed when generating the URL path. 066 */ 067 private static final String CONTROLLER_SUFFIX = "Controller"; 068 069 070 private boolean caseSensitive = false; 071 072 private String pathPrefix; 073 074 private String basePackage; 075 076 077 /** 078 * Set whether to apply case sensitivity to the generated paths, 079 * e.g. turning the class name "BuyForm" into "buyForm". 080 * <p>Default is "false", using pure lower case paths, 081 * e.g. turning the class name "BuyForm" into "buyform". 082 */ 083 public void setCaseSensitive(boolean caseSensitive) { 084 this.caseSensitive = caseSensitive; 085 } 086 087 /** 088 * Specify a prefix to prepend to the path generated from the controller name. 089 * <p>Default is a plain slash ("/"). A path like "/mymodule" can be specified 090 * in order to have controller path mappings prefixed with that path, e.g. 091 * "/mymodule/buyform" instead of "/buyform" for the class name "BuyForm". 092 */ 093 public void setPathPrefix(String prefixPath) { 094 this.pathPrefix = prefixPath; 095 if (StringUtils.hasLength(this.pathPrefix)) { 096 if (!this.pathPrefix.startsWith("/")) { 097 this.pathPrefix = "/" + this.pathPrefix; 098 } 099 if (this.pathPrefix.endsWith("/")) { 100 this.pathPrefix = this.pathPrefix.substring(0, this.pathPrefix.length() - 1); 101 } 102 } 103 } 104 105 /** 106 * Set the base package to be used for generating path mappings, 107 * including all subpackages underneath this packages as path elements. 108 * <p>Default is {@code null}, using the short class name for the 109 * generated path, with the controller's package not represented in the path. 110 * Specify a base package like "com.mycompany.myapp" to include subpackages 111 * within that base package as path elements, e.g. generating the path 112 * "/mymodule/buyform" for the class name "com.mycompany.myapp.mymodule.BuyForm". 113 * Subpackage hierarchies are represented as individual path elements, 114 * e.g. "/mymodule/mysubmodule/buyform" for the class name 115 * "com.mycompany.myapp.mymodule.mysubmodule.BuyForm". 116 */ 117 public void setBasePackage(String basePackage) { 118 this.basePackage = basePackage; 119 if (StringUtils.hasLength(this.basePackage) && !this.basePackage.endsWith(".")) { 120 this.basePackage = this.basePackage + "."; 121 } 122 } 123 124 125 @Override 126 protected String[] buildUrlsForHandler(String beanName, Class<?> beanClass) { 127 return generatePathMappings(beanClass); 128 } 129 130 /** 131 * Generate the actual URL paths for the given controller class. 132 * <p>Subclasses may choose to customize the paths that are generated 133 * by overriding this method. 134 * @param beanClass the controller bean class to generate a mapping for 135 * @return the URL path mappings for the given controller 136 */ 137 protected String[] generatePathMappings(Class<?> beanClass) { 138 StringBuilder pathMapping = buildPathPrefix(beanClass); 139 String className = ClassUtils.getShortName(beanClass); 140 String path = (className.endsWith(CONTROLLER_SUFFIX) ? 141 className.substring(0, className.lastIndexOf(CONTROLLER_SUFFIX)) : className); 142 if (path.length() > 0) { 143 if (this.caseSensitive) { 144 pathMapping.append(path.substring(0, 1).toLowerCase()).append(path.substring(1)); 145 } 146 else { 147 pathMapping.append(path.toLowerCase()); 148 } 149 } 150 if (isMultiActionControllerType(beanClass)) { 151 return new String[] {pathMapping.toString(), pathMapping.toString() + "/*"}; 152 } 153 else { 154 return new String[] {pathMapping.toString() + "*"}; 155 } 156 } 157 158 /** 159 * Build a path prefix for the given controller bean class. 160 * @param beanClass the controller bean class to generate a mapping for 161 * @return the path prefix, potentially including subpackage names as path elements 162 */ 163 private StringBuilder buildPathPrefix(Class<?> beanClass) { 164 StringBuilder pathMapping = new StringBuilder(); 165 if (this.pathPrefix != null) { 166 pathMapping.append(this.pathPrefix); 167 pathMapping.append("/"); 168 } 169 else { 170 pathMapping.append("/"); 171 } 172 if (this.basePackage != null) { 173 String packageName = ClassUtils.getPackageName(beanClass); 174 if (packageName.startsWith(this.basePackage)) { 175 String subPackage = packageName.substring(this.basePackage.length()).replace('.', '/'); 176 pathMapping.append(this.caseSensitive ? subPackage : subPackage.toLowerCase()); 177 pathMapping.append("/"); 178 } 179 } 180 return pathMapping; 181 } 182 183}