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.reactive; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.List; 022import java.util.Map; 023 024import reactor.core.publisher.Flux; 025import reactor.core.publisher.Mono; 026 027import org.springframework.beans.factory.BeanFactoryUtils; 028import org.springframework.context.ApplicationContext; 029import org.springframework.context.ApplicationContextAware; 030import org.springframework.core.annotation.AnnotationAwareOrderComparator; 031import org.springframework.http.HttpStatus; 032import org.springframework.lang.Nullable; 033import org.springframework.web.server.ResponseStatusException; 034import org.springframework.web.server.ServerWebExchange; 035import org.springframework.web.server.WebHandler; 036import org.springframework.web.server.adapter.WebHttpHandlerBuilder; 037 038/** 039 * Central dispatcher for HTTP request handlers/controllers. Dispatches to 040 * registered handlers for processing a request, providing convenient mapping 041 * facilities. 042 * 043 * <p>{@code DispatcherHandler} discovers the delegate components it needs from 044 * Spring configuration. It detects the following in the application context: 045 * <ul> 046 * <li>{@link HandlerMapping} -- map requests to handler objects 047 * <li>{@link HandlerAdapter} -- for using any handler interface 048 * <li>{@link HandlerResultHandler} -- process handler return values 049 * </ul> 050 * 051 * <p>{@code DispatcherHandler} is also designed to be a Spring bean itself and 052 * implements {@link ApplicationContextAware} for access to the context it runs 053 * in. If {@code DispatcherHandler} is declared with the bean name "webHandler" 054 * it is discovered by {@link WebHttpHandlerBuilder#applicationContext} which 055 * creates a processing chain together with {@code WebFilter}, 056 * {@code WebExceptionHandler} and others. 057 * 058 * <p>A {@code DispatcherHandler} bean declaration is included in 059 * {@link org.springframework.web.reactive.config.EnableWebFlux @EnableWebFlux} 060 * configuration. 061 * 062 * @author Rossen Stoyanchev 063 * @author Sebastien Deleuze 064 * @author Juergen Hoeller 065 * @since 5.0 066 * @see WebHttpHandlerBuilder#applicationContext(ApplicationContext) 067 */ 068public class DispatcherHandler implements WebHandler, ApplicationContextAware { 069 070 @Nullable 071 private List<HandlerMapping> handlerMappings; 072 073 @Nullable 074 private List<HandlerAdapter> handlerAdapters; 075 076 @Nullable 077 private List<HandlerResultHandler> resultHandlers; 078 079 080 /** 081 * Create a new {@code DispatcherHandler} which needs to be configured with 082 * an {@link ApplicationContext} through {@link #setApplicationContext}. 083 */ 084 public DispatcherHandler() { 085 } 086 087 /** 088 * Create a new {@code DispatcherHandler} for the given {@link ApplicationContext}. 089 * @param applicationContext the application context to find the handler beans in 090 */ 091 public DispatcherHandler(ApplicationContext applicationContext) { 092 initStrategies(applicationContext); 093 } 094 095 096 /** 097 * Return all {@link HandlerMapping} beans detected by type in the 098 * {@link #setApplicationContext injected context} and also 099 * {@link AnnotationAwareOrderComparator#sort(List) sorted}. 100 * <p><strong>Note:</strong> This method may return {@code null} if invoked 101 * prior to {@link #setApplicationContext(ApplicationContext)}. 102 * @return immutable list with the configured mappings or {@code null} 103 */ 104 @Nullable 105 public final List<HandlerMapping> getHandlerMappings() { 106 return this.handlerMappings; 107 } 108 109 @Override 110 public void setApplicationContext(ApplicationContext applicationContext) { 111 initStrategies(applicationContext); 112 } 113 114 115 protected void initStrategies(ApplicationContext context) { 116 Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 117 context, HandlerMapping.class, true, false); 118 119 ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values()); 120 AnnotationAwareOrderComparator.sort(mappings); 121 this.handlerMappings = Collections.unmodifiableList(mappings); 122 123 Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 124 context, HandlerAdapter.class, true, false); 125 126 this.handlerAdapters = new ArrayList<>(adapterBeans.values()); 127 AnnotationAwareOrderComparator.sort(this.handlerAdapters); 128 129 Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 130 context, HandlerResultHandler.class, true, false); 131 132 this.resultHandlers = new ArrayList<>(beans.values()); 133 AnnotationAwareOrderComparator.sort(this.resultHandlers); 134 } 135 136 137 @Override 138 public Mono<Void> handle(ServerWebExchange exchange) { 139 if (this.handlerMappings == null) { 140 return createNotFoundError(); 141 } 142 return Flux.fromIterable(this.handlerMappings) 143 .concatMap(mapping -> mapping.getHandler(exchange)) 144 .next() 145 .switchIfEmpty(createNotFoundError()) 146 .flatMap(handler -> invokeHandler(exchange, handler)) 147 .flatMap(result -> handleResult(exchange, result)); 148 } 149 150 private <R> Mono<R> createNotFoundError() { 151 return Mono.defer(() -> { 152 Exception ex = new ResponseStatusException(HttpStatus.NOT_FOUND, "No matching handler"); 153 return Mono.error(ex); 154 }); 155 } 156 157 private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) { 158 if (this.handlerAdapters != null) { 159 for (HandlerAdapter handlerAdapter : this.handlerAdapters) { 160 if (handlerAdapter.supports(handler)) { 161 return handlerAdapter.handle(exchange, handler); 162 } 163 } 164 } 165 return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler)); 166 } 167 168 private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) { 169 return getResultHandler(result).handleResult(exchange, result) 170 .checkpoint("Handler " + result.getHandler() + " [DispatcherHandler]") 171 .onErrorResume(ex -> 172 result.applyExceptionHandler(ex).flatMap(exResult -> { 173 String text = "Exception handler " + exResult.getHandler() + 174 ", error=\"" + ex.getMessage() + "\" [DispatcherHandler]"; 175 return getResultHandler(exResult).handleResult(exchange, exResult).checkpoint(text); 176 })); 177 } 178 179 private HandlerResultHandler getResultHandler(HandlerResult handlerResult) { 180 if (this.resultHandlers != null) { 181 for (HandlerResultHandler resultHandler : this.resultHandlers) { 182 if (resultHandler.supports(handlerResult)) { 183 return resultHandler; 184 } 185 } 186 } 187 throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue()); 188 } 189 190}