001/* 002 * Copyright 2002-2018 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.annotation; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.LinkedHashMap; 022import java.util.List; 023import java.util.Map; 024import javax.servlet.ServletContext; 025 026import org.springframework.beans.factory.BeanInitializationException; 027import org.springframework.context.ApplicationContext; 028import org.springframework.core.Ordered; 029import org.springframework.util.Assert; 030import org.springframework.web.HttpRequestHandler; 031import org.springframework.web.accept.ContentNegotiationManager; 032import org.springframework.web.servlet.HandlerMapping; 033import org.springframework.web.servlet.handler.AbstractHandlerMapping; 034import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; 035import org.springframework.web.servlet.resource.ResourceHttpRequestHandler; 036import org.springframework.web.util.UrlPathHelper; 037 038/** 039 * Stores registrations of resource handlers for serving static resources such as images, css files and others 040 * through Spring MVC including setting cache headers optimized for efficient loading in a web browser. 041 * Resources can be served out of locations under web application root, from the classpath, and others. 042 * 043 * <p>To create a resource handler, use {@link #addResourceHandler(String...)} providing the URL path patterns 044 * for which the handler should be invoked to serve static resources (e.g. {@code "/resources/**"}). 045 * 046 * <p>Then use additional methods on the returned {@link ResourceHandlerRegistration} to add one or more 047 * locations from which to serve static content from (e.g. {{@code "/"}, 048 * {@code "classpath:/META-INF/public-web-resources/"}}) or to specify a cache period for served resources. 049 * 050 * @author Rossen Stoyanchev 051 * @since 3.1 052 * @see DefaultServletHandlerConfigurer 053 */ 054public class ResourceHandlerRegistry { 055 056 private final ServletContext servletContext; 057 058 private final ApplicationContext applicationContext; 059 060 private final ContentNegotiationManager contentNegotiationManager; 061 062 private final UrlPathHelper pathHelper; 063 064 private final List<ResourceHandlerRegistration> registrations = new ArrayList<ResourceHandlerRegistration>(); 065 066 private int order = Ordered.LOWEST_PRECEDENCE - 1; 067 068 069 /** 070 * Create a new resource handler registry for the given application context. 071 * @param applicationContext the Spring application context 072 * @param servletContext the corresponding Servlet context 073 */ 074 public ResourceHandlerRegistry(ApplicationContext applicationContext, ServletContext servletContext) { 075 this(applicationContext, servletContext, null); 076 } 077 078 /** 079 * Create a new resource handler registry for the given application context. 080 * @param applicationContext the Spring application context 081 * @param servletContext the corresponding Servlet context 082 * @param contentNegotiationManager the content negotiation manager to use 083 * @since 4.3 084 */ 085 public ResourceHandlerRegistry(ApplicationContext applicationContext, ServletContext servletContext, 086 ContentNegotiationManager contentNegotiationManager) { 087 088 this(applicationContext, servletContext, contentNegotiationManager, null); 089 } 090 091 /** 092 * A variant of 093 * {@link #ResourceHandlerRegistry(ApplicationContext, ServletContext, ContentNegotiationManager)} 094 * that also accepts the {@link UrlPathHelper} used for mapping requests to static resources. 095 * @since 4.3.13 096 */ 097 public ResourceHandlerRegistry(ApplicationContext applicationContext, ServletContext servletContext, 098 ContentNegotiationManager contentNegotiationManager, UrlPathHelper pathHelper) { 099 100 Assert.notNull(applicationContext, "ApplicationContext is required"); 101 this.applicationContext = applicationContext; 102 this.servletContext = servletContext; 103 this.contentNegotiationManager = contentNegotiationManager; 104 this.pathHelper = pathHelper; 105 } 106 107 108 /** 109 * Add a resource handler for serving static resources based on the specified URL path patterns. 110 * The handler will be invoked for every incoming request that matches to one of the specified 111 * path patterns. 112 * <p>Patterns like {@code "/static/**"} or {@code "/css/{filename:\\w+\\.css}"} are allowed. 113 * See {@link org.springframework.util.AntPathMatcher} for more details on the syntax. 114 * @return a {@link ResourceHandlerRegistration} to use to further configure the 115 * registered resource handler 116 */ 117 public ResourceHandlerRegistration addResourceHandler(String... pathPatterns) { 118 ResourceHandlerRegistration registration = new ResourceHandlerRegistration(pathPatterns); 119 this.registrations.add(registration); 120 return registration; 121 } 122 123 /** 124 * Whether a resource handler has already been registered for the given path pattern. 125 */ 126 public boolean hasMappingForPattern(String pathPattern) { 127 for (ResourceHandlerRegistration registration : this.registrations) { 128 if (Arrays.asList(registration.getPathPatterns()).contains(pathPattern)) { 129 return true; 130 } 131 } 132 return false; 133 } 134 135 /** 136 * Specify the order to use for resource handling relative to other {@link HandlerMapping}s 137 * configured in the Spring MVC application context. 138 * <p>The default value used is {@code Integer.MAX_VALUE-1}. 139 */ 140 public ResourceHandlerRegistry setOrder(int order) { 141 this.order = order; 142 return this; 143 } 144 145 /** 146 * Return a handler mapping with the mapped resource handlers; or {@code null} in case 147 * of no registrations. 148 */ 149 protected AbstractHandlerMapping getHandlerMapping() { 150 if (this.registrations.isEmpty()) { 151 return null; 152 } 153 154 Map<String, HttpRequestHandler> urlMap = new LinkedHashMap<String, HttpRequestHandler>(); 155 for (ResourceHandlerRegistration registration : this.registrations) { 156 for (String pathPattern : registration.getPathPatterns()) { 157 ResourceHttpRequestHandler handler = registration.getRequestHandler(); 158 if (this.pathHelper != null) { 159 handler.setUrlPathHelper(this.pathHelper); 160 } 161 if (this.contentNegotiationManager != null) { 162 handler.setContentNegotiationManager(this.contentNegotiationManager); 163 } 164 handler.setServletContext(this.servletContext); 165 handler.setApplicationContext(this.applicationContext); 166 try { 167 handler.afterPropertiesSet(); 168 } 169 catch (Throwable ex) { 170 throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex); 171 } 172 urlMap.put(pathPattern, handler); 173 } 174 } 175 176 SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping(); 177 handlerMapping.setOrder(order); 178 handlerMapping.setUrlMap(urlMap); 179 return handlerMapping; 180 } 181 182}