/* * @(#)RestfullMappingHandlerMapping.java 2013-1-31 下午23:33:33 * * Copyright (c) 2011-2013 Makersoft.org all rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * */ package org.makersoft.mvc.method.annotation; import java.lang.reflect.Method; import org.apache.commons.lang3.StringUtils; import org.makersoft.log.Log; import org.makersoft.log.LoggerFactory; import org.makersoft.mvc.annotation.RESTfull; import org.makersoft.mvc.builder.PackageBasedUrlPathBuilder; import org.makersoft.mvc.mapping.Mapping; import org.makersoft.mvc.mapping.RESTfulHelper; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition; import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition; import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition; import org.springframework.web.servlet.mvc.condition.RequestCondition; import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; /** * 适用于带有{@link RESTfull }注解的Controller类,用于构造RESTful请求路径. * @see RESTfull * * @author Feng Kuok */ public class RESTfulMappingHandlerMapping extends RequestMappingInfoHandlerMapping{ private static final Log LOG = LoggerFactory.getLogger(RESTfulMappingHandlerMapping.class); private boolean useSuffixPatternMatch = true; private boolean useTrailingSlashMatch = true; private String[] packageLocators = new String[]{"web", "controllers", "actions", "action", "controller"}; private String[] controllerPackages = null; private String controllerSuffix = "Controller"; private String root = "application#index"; //default constructor public RESTfulMappingHandlerMapping(){ //set HandlerMapping order super.setOrder(0); LOG.debug("Init RESTfulMappingHandlerMapping..."); } /** * Whether to use suffix pattern match (".*") when matching patterns to * requests. If enabled a method mapped to "/users" also matches to "/users.*". * <p>The default value is {@code true}. */ public void setUseSuffixPatternMatch(boolean useSuffixPatternMatch) { this.useSuffixPatternMatch = useSuffixPatternMatch; } /** * Whether to match to URLs irrespective of the presence of a trailing slash. * If enabled a method mapped to "/users" also matches to "/users/". * <p>The default value is {@code true}. */ public void setUseTrailingSlashMatch(boolean useTrailingSlashMatch) { this.useTrailingSlashMatch = useTrailingSlashMatch; } /** * 用于指定Controller类的跟包名称 * 默认为"web", "controllers", "actions", "action", "controller" */ public void setPackageLocators(String packageLocators) { this.packageLocators = StringUtils.split(packageLocators, ","); } /** * 用于指定Controller所在包名称 */ public void setControllerPackages(String controllerPackages) { this.controllerPackages = StringUtils.split(controllerPackages, ","); } /** * 用于指定Controller类名称后缀,默认为Controller */ public void setControllerSuffix(String controllerSuffix) { this.controllerSuffix = controllerSuffix; } /** * 指定访问根路径,默认为application#index */ public void setRoot(String root) { this.root = root; } /** * Whether to use suffix pattern matching. */ public boolean useSuffixPatternMatch() { return this.useSuffixPatternMatch; } /** * Whether to match to URLs irrespective of the presence of a trailing slash. */ public boolean useTrailingSlashMatch() { return this.useTrailingSlashMatch; } @Override public void afterPropertiesSet() { super.afterPropertiesSet(); } /** * {@inheritDoc} * Expects a handler to have a type-level @{@link Controller} annotation. */ @Override protected boolean isHandler(Class<?> beanType) { return AnnotationUtils.findAnnotation(beanType, Controller.class) != null; } /** * Uses method and type-level @{@link RequestMapping} annotations to create * the RequestMappingInfo. * * @return the created RequestMappingInfo, or {@code null} if the method * does not have a {@code @RequestMapping} annotation. * * @see #getCustomMethodCondition(Method) * @see #getCustomTypeCondition(Class) */ @Override protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = null; RESTfull clazzAnnotation = AnnotationUtils.findAnnotation(handlerType, RESTfull.class); if (clazzAnnotation != null) { info = createRequestMappingInfo(method, clazzAnnotation, handlerType); // RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class); // if (typeAnnotation != null) { // RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType); // info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info); // } } return info; } /** * Provide a custom method-level request condition. * The custom {@link RequestCondition} can be of any type so long as the * same condition type is returned from all calls to this method in order * to ensure custom request conditions can be combined and compared. * @param method the handler method for which to create the condition * @return the condition, or {@code null} */ protected RequestCondition<?> getCustomMethodCondition(Method method) { return null; } /** * Provide a custom type-level request condition. * The custom {@link RequestCondition} can be of any type so long as the * same condition type is returned from all calls to this method in order * to ensure custom request conditions can be combined and compared. * @param method the handler method for which to create the condition * @return the condition, or {@code null} */ protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) { return null; } /** * Created a RequestMappingInfo from a RequestMapping annotation. */ private RequestMappingInfo createRequestMappingInfo(Method method, RESTfull annotation, Class<?> controllerClass) { Mapping mapping = RESTfulHelper.matchMapping(method.getName()); if(mapping != null){ PackageBasedUrlPathBuilder builder = new PackageBasedUrlPathBuilder(packageLocators, controllerPackages, controllerSuffix, root); String[] namespaceString = builder.buildUrlPath(controllerClass, mapping.getMethodName(), mapping.getValues()); if(namespaceString != null){ return new RequestMappingInfo( new PatternsRequestCondition(namespaceString, getUrlPathHelper(), getPathMatcher(), this.useSuffixPatternMatch, this.useTrailingSlashMatch), new RequestMethodsRequestCondition(mapping.getRequestMethods()), new ParamsRequestCondition(mapping.getParams()), new HeadersRequestCondition(mapping.getHeaders()), new ConsumesRequestCondition(mapping.getConsumes(), mapping.getHeaders()), new ProducesRequestCondition(mapping.getProduces(), mapping.getHeaders()), getCustomMethodCondition(method)); } } return null; } }