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