001/* 002 * Copyright 2002-2017 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.messaging.handler; 018 019import java.lang.annotation.Annotation; 020import java.lang.reflect.Method; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import org.springframework.beans.factory.BeanFactory; 026import org.springframework.core.BridgeMethodResolver; 027import org.springframework.core.GenericTypeResolver; 028import org.springframework.core.MethodParameter; 029import org.springframework.core.annotation.AnnotatedElementUtils; 030import org.springframework.core.annotation.SynthesizingMethodParameter; 031import org.springframework.util.Assert; 032import org.springframework.util.ClassUtils; 033 034/** 035 * Encapsulates information about a handler method consisting of a 036 * {@linkplain #getMethod() method} and a {@linkplain #getBean() bean}. 037 * Provides convenient access to method parameters, the method return value, 038 * method annotations, etc. 039 * 040 * <p>The class may be created with a bean instance or with a bean name 041 * (e.g. lazy-init bean, prototype bean). Use {@link #createWithResolvedBean()} 042 * to obtain a {@code HandlerMethod} instance with a bean instance resolved 043 * through the associated {@link BeanFactory}. 044 * 045 * @author Arjen Poutsma 046 * @author Rossen Stoyanchev 047 * @author Juergen Hoeller 048 * @since 4.0 049 */ 050public class HandlerMethod { 051 052 /** Logger that is available to subclasses */ 053 protected final Log logger = LogFactory.getLog(getClass()); 054 055 private final Object bean; 056 057 private final BeanFactory beanFactory; 058 059 private final Class<?> beanType; 060 061 private final Method method; 062 063 private final Method bridgedMethod; 064 065 private final MethodParameter[] parameters; 066 067 private HandlerMethod resolvedFromHandlerMethod; 068 069 070 /** 071 * Create an instance from a bean instance and a method. 072 */ 073 public HandlerMethod(Object bean, Method method) { 074 Assert.notNull(bean, "Bean is required"); 075 Assert.notNull(method, "Method is required"); 076 this.bean = bean; 077 this.beanFactory = null; 078 this.beanType = ClassUtils.getUserClass(bean); 079 this.method = method; 080 this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); 081 this.parameters = initMethodParameters(); 082 } 083 084 /** 085 * Create an instance from a bean instance, method name, and parameter types. 086 * @throws NoSuchMethodException when the method cannot be found 087 */ 088 public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException { 089 Assert.notNull(bean, "Bean is required"); 090 Assert.notNull(methodName, "Method name is required"); 091 this.bean = bean; 092 this.beanFactory = null; 093 this.beanType = ClassUtils.getUserClass(bean); 094 this.method = bean.getClass().getMethod(methodName, parameterTypes); 095 this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method); 096 this.parameters = initMethodParameters(); 097 } 098 099 /** 100 * Create an instance from a bean name, a method, and a {@code BeanFactory}. 101 * The method {@link #createWithResolvedBean()} may be used later to 102 * re-create the {@code HandlerMethod} with an initialized bean. 103 */ 104 public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) { 105 Assert.hasText(beanName, "Bean name is required"); 106 Assert.notNull(beanFactory, "BeanFactory is required"); 107 Assert.notNull(method, "Method is required"); 108 this.bean = beanName; 109 this.beanFactory = beanFactory; 110 this.beanType = ClassUtils.getUserClass(beanFactory.getType(beanName)); 111 this.method = method; 112 this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); 113 this.parameters = initMethodParameters(); 114 } 115 116 /** 117 * Copy constructor for use in subclasses. 118 */ 119 protected HandlerMethod(HandlerMethod handlerMethod) { 120 Assert.notNull(handlerMethod, "HandlerMethod is required"); 121 this.bean = handlerMethod.bean; 122 this.beanFactory = handlerMethod.beanFactory; 123 this.beanType = handlerMethod.beanType; 124 this.method = handlerMethod.method; 125 this.bridgedMethod = handlerMethod.bridgedMethod; 126 this.parameters = handlerMethod.parameters; 127 this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod; 128 } 129 130 /** 131 * Re-create HandlerMethod with the resolved handler. 132 */ 133 private HandlerMethod(HandlerMethod handlerMethod, Object handler) { 134 Assert.notNull(handlerMethod, "HandlerMethod is required"); 135 Assert.notNull(handler, "Handler object is required"); 136 this.bean = handler; 137 this.beanFactory = handlerMethod.beanFactory; 138 this.beanType = handlerMethod.beanType; 139 this.method = handlerMethod.method; 140 this.bridgedMethod = handlerMethod.bridgedMethod; 141 this.parameters = handlerMethod.parameters; 142 this.resolvedFromHandlerMethod = handlerMethod; 143 } 144 145 146 private MethodParameter[] initMethodParameters() { 147 int count = this.bridgedMethod.getParameterTypes().length; 148 MethodParameter[] result = new MethodParameter[count]; 149 for (int i = 0; i < count; i++) { 150 HandlerMethodParameter parameter = new HandlerMethodParameter(i); 151 GenericTypeResolver.resolveParameterType(parameter, this.beanType); 152 result[i] = parameter; 153 } 154 return result; 155 } 156 157 /** 158 * Return the bean for this handler method. 159 */ 160 public Object getBean() { 161 return this.bean; 162 } 163 164 /** 165 * Return the method for this handler method. 166 */ 167 public Method getMethod() { 168 return this.method; 169 } 170 171 /** 172 * This method returns the type of the handler for this handler method. 173 * <p>Note that if the bean type is a CGLIB-generated class, the original 174 * user-defined class is returned. 175 */ 176 public Class<?> getBeanType() { 177 return this.beanType; 178 } 179 180 /** 181 * If the bean method is a bridge method, this method returns the bridged 182 * (user-defined) method. Otherwise it returns the same method as {@link #getMethod()}. 183 */ 184 protected Method getBridgedMethod() { 185 return this.bridgedMethod; 186 } 187 188 /** 189 * Return the method parameters for this handler method. 190 */ 191 public MethodParameter[] getMethodParameters() { 192 return this.parameters; 193 } 194 195 /** 196 * Return the HandlerMethod return type. 197 */ 198 public MethodParameter getReturnType() { 199 return new HandlerMethodParameter(-1); 200 } 201 202 /** 203 * Return the actual return value type. 204 */ 205 public MethodParameter getReturnValueType(Object returnValue) { 206 return new ReturnValueMethodParameter(returnValue); 207 } 208 209 /** 210 * Return {@code true} if the method return type is void, {@code false} otherwise. 211 */ 212 public boolean isVoid() { 213 return Void.TYPE.equals(getReturnType().getParameterType()); 214 } 215 216 /** 217 * Return a single annotation on the underlying method traversing its super methods 218 * if no annotation can be found on the given method itself. 219 * <p>Also supports <em>merged</em> composed annotations with attribute 220 * overrides as of Spring Framework 4.3. 221 * @param annotationType the type of annotation to introspect the method for 222 * @return the annotation, or {@code null} if none found 223 * @see AnnotatedElementUtils#findMergedAnnotation 224 */ 225 public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) { 226 return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType); 227 } 228 229 /** 230 * Return whether the parameter is declared with the given annotation type. 231 * @param annotationType the annotation type to look for 232 * @since 4.3 233 * @see AnnotatedElementUtils#hasAnnotation 234 */ 235 public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationType) { 236 return AnnotatedElementUtils.hasAnnotation(this.method, annotationType); 237 } 238 239 /** 240 * Return the HandlerMethod from which this HandlerMethod instance was 241 * resolved via {@link #createWithResolvedBean()}. 242 * @since 4.3 243 */ 244 public HandlerMethod getResolvedFromHandlerMethod() { 245 return this.resolvedFromHandlerMethod; 246 } 247 248 /** 249 * If the provided instance contains a bean name rather than an object instance, 250 * the bean name is resolved before a {@link HandlerMethod} is created and returned. 251 */ 252 public HandlerMethod createWithResolvedBean() { 253 Object handler = this.bean; 254 if (this.bean instanceof String) { 255 String beanName = (String) this.bean; 256 handler = this.beanFactory.getBean(beanName); 257 } 258 return new HandlerMethod(this, handler); 259 } 260 261 /** 262 * Return a short representation of this handler method for log message purposes. 263 */ 264 public String getShortLogMessage() { 265 int args = this.method.getParameterTypes().length; 266 return getBeanType().getName() + "#" + this.method.getName() + "[" + args + " args]"; 267 } 268 269 270 @Override 271 public boolean equals(Object other) { 272 if (this == other) { 273 return true; 274 } 275 if (!(other instanceof HandlerMethod)) { 276 return false; 277 } 278 HandlerMethod otherMethod = (HandlerMethod) other; 279 return (this.bean.equals(otherMethod.bean) && this.method.equals(otherMethod.method)); 280 } 281 282 @Override 283 public int hashCode() { 284 return (this.bean.hashCode() * 31 + this.method.hashCode()); 285 } 286 287 @Override 288 public String toString() { 289 return this.method.toGenericString(); 290 } 291 292 293 /** 294 * A MethodParameter with HandlerMethod-specific behavior. 295 */ 296 protected class HandlerMethodParameter extends SynthesizingMethodParameter { 297 298 public HandlerMethodParameter(int index) { 299 super(HandlerMethod.this.bridgedMethod, index); 300 } 301 302 protected HandlerMethodParameter(HandlerMethodParameter original) { 303 super(original); 304 } 305 306 @Override 307 public Class<?> getContainingClass() { 308 return HandlerMethod.this.getBeanType(); 309 } 310 311 @Override 312 public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) { 313 return HandlerMethod.this.getMethodAnnotation(annotationType); 314 } 315 316 @Override 317 public <T extends Annotation> boolean hasMethodAnnotation(Class<T> annotationType) { 318 return HandlerMethod.this.hasMethodAnnotation(annotationType); 319 } 320 321 @Override 322 public HandlerMethodParameter clone() { 323 return new HandlerMethodParameter(this); 324 } 325 } 326 327 328 /** 329 * A MethodParameter for a HandlerMethod return type based on an actual return value. 330 */ 331 private class ReturnValueMethodParameter extends HandlerMethodParameter { 332 333 private final Object returnValue; 334 335 public ReturnValueMethodParameter(Object returnValue) { 336 super(-1); 337 this.returnValue = returnValue; 338 } 339 340 protected ReturnValueMethodParameter(ReturnValueMethodParameter original) { 341 super(original); 342 this.returnValue = original.returnValue; 343 } 344 345 @Override 346 public Class<?> getParameterType() { 347 return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType()); 348 } 349 350 @Override 351 public ReturnValueMethodParameter clone() { 352 return new ReturnValueMethodParameter(this); 353 } 354 } 355 356}