Skip to content

Commit 5f95ff6

Browse files
committed
Optimized access to resolved bean type (avoiding BeanFactory locks)
Revised HandlerMethod.getBeanType() impl for both web and messaging. In addition, HandlerMethods get created with the internal BeanFactory now. Issue: SPR-12832 (cherry picked from commit 898c24f)
1 parent 4446b5f commit 5f95ff6

File tree

4 files changed

+100
-93
lines changed

4 files changed

+100
-93
lines changed

spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,26 +30,30 @@
3030
import org.springframework.util.ClassUtils;
3131

3232
/**
33-
* Encapsulates information about a bean method consisting of a {@link #getMethod() method}
34-
* and a {@link #getBean() bean}. Provides convenient access to method parameters,
35-
* method return value, method annotations.
33+
* Encapsulates information about a handler method consisting of a
34+
* {@linkplain #getMethod() method} and a {@linkplain #getBean() bean}.
35+
* Provides convenient access to method parameters, method return value, method annotations.
3636
*
37-
* <p>The class may be created with a bean instance or with a bean name (e.g. lazy bean,
38-
* prototype bean). Use {@link #createWithResolvedBean()} to obtain an {@link HandlerMethod}
39-
* instance with a bean instance initialized through the bean factory.
37+
* <p>The class may be created with a bean instance or with a bean name (e.g. lazy-init bean,
38+
* prototype bean). Use {@link #createWithResolvedBean()} to obtain a {@link HandlerMethod}
39+
* instance with a bean instance resolved through the associated {@link BeanFactory}.
4040
*
4141
* @author Arjen Poutsma
4242
* @author Rossen Stoyanchev
43+
* @author Juergen Hoeller
4344
* @since 4.0
4445
*/
4546
public class HandlerMethod {
4647

47-
protected final Log logger = LogFactory.getLog(HandlerMethod.class);
48+
/** Logger that is available to subclasses */
49+
protected final Log logger = LogFactory.getLog(getClass());
4850

4951
private final Object bean;
5052

5153
private final BeanFactory beanFactory;
5254

55+
private final Class<?> beanType;
56+
5357
private final Method method;
5458

5559
private final Method bridgedMethod;
@@ -65,6 +69,7 @@ public HandlerMethod(Object bean, Method method) {
6569
Assert.notNull(method, "Method is required");
6670
this.bean = bean;
6771
this.beanFactory = null;
72+
this.beanType = ClassUtils.getUserClass(bean);
6873
this.method = method;
6974
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
7075
this.parameters = initMethodParameters();
@@ -79,6 +84,7 @@ public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
7984
Assert.notNull(methodName, "Method name is required");
8085
this.bean = bean;
8186
this.beanFactory = null;
87+
this.beanType = ClassUtils.getUserClass(bean);
8288
this.method = bean.getClass().getMethod(methodName, parameterTypes);
8389
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method);
8490
this.parameters = initMethodParameters();
@@ -93,22 +99,22 @@ public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
9399
Assert.hasText(beanName, "Bean name is required");
94100
Assert.notNull(beanFactory, "BeanFactory is required");
95101
Assert.notNull(method, "Method is required");
96-
Assert.isTrue(beanFactory.containsBean(beanName),
97-
"BeanFactory [" + beanFactory + "] does not contain bean [" + beanName + "]");
98102
this.bean = beanName;
99103
this.beanFactory = beanFactory;
104+
this.beanType = ClassUtils.getUserClass(beanFactory.getType(beanName));
100105
this.method = method;
101106
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
102107
this.parameters = initMethodParameters();
103108
}
104109

105110
/**
106-
* Copy constructor for use in sub-classes.
111+
* Copy constructor for use in subclasses.
107112
*/
108113
protected HandlerMethod(HandlerMethod handlerMethod) {
109114
Assert.notNull(handlerMethod, "HandlerMethod is required");
110115
this.bean = handlerMethod.bean;
111116
this.beanFactory = handlerMethod.beanFactory;
117+
this.beanType = handlerMethod.beanType;
112118
this.method = handlerMethod.method;
113119
this.bridgedMethod = handlerMethod.bridgedMethod;
114120
this.parameters = handlerMethod.parameters;
@@ -122,6 +128,7 @@ private HandlerMethod(HandlerMethod handlerMethod, Object handler) {
122128
Assert.notNull(handler, "Handler object is required");
123129
this.bean = handler;
124130
this.beanFactory = handlerMethod.beanFactory;
131+
this.beanType = handlerMethod.beanType;
125132
this.method = handlerMethod.method;
126133
this.bridgedMethod = handlerMethod.bridgedMethod;
127134
this.parameters = handlerMethod.parameters;
@@ -152,18 +159,17 @@ public Method getMethod() {
152159
}
153160

154161
/**
155-
* Returns the type of the handler for this handler method.
156-
* Note that if the bean type is a CGLIB-generated class, the original, user-defined class is returned.
162+
* This method returns the type of the handler for this handler method.
163+
* <p>Note that if the bean type is a CGLIB-generated class, the original
164+
* user-defined class is returned.
157165
*/
158166
public Class<?> getBeanType() {
159-
Class<?> clazz = (this.bean instanceof String ?
160-
this.beanFactory.getType((String) this.bean) : this.bean.getClass());
161-
return ClassUtils.getUserClass(clazz);
167+
return this.beanType;
162168
}
163169

164170
/**
165-
* If the bean method is a bridge method, this method returns the bridged (user-defined) method.
166-
* Otherwise it returns the same method as {@link #getMethod()}.
171+
* If the bean method is a bridge method, this method returns the bridged
172+
* (user-defined) method. Otherwise it returns the same method as {@link #getMethod()}.
167173
*/
168174
protected Method getBridgedMethod() {
169175
return this.bridgedMethod;
@@ -198,8 +204,8 @@ public boolean isVoid() {
198204
}
199205

200206
/**
201-
* Returns a single annotation on the underlying method traversing its super methods if no
202-
* annotation can be found on the given method itself.
207+
* Returns a single annotation on the underlying method traversing its super methods
208+
* if no annotation can be found on the given method itself.
203209
* @param annotationType the type of annotation to introspect the method for.
204210
* @return the annotation, or {@code null} if none found
205211
*/
@@ -208,8 +214,8 @@ public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) {
208214
}
209215

210216
/**
211-
* If the provided instance contains a bean name rather than an object instance, the bean name is resolved
212-
* before a {@link HandlerMethod} is created and returned.
217+
* If the provided instance contains a bean name rather than an object instance,
218+
* the bean name is resolved before a {@link HandlerMethod} is created and returned.
213219
*/
214220
public HandlerMethod createWithResolvedBean() {
215221
Object handler = this.bean;
@@ -220,26 +226,27 @@ public HandlerMethod createWithResolvedBean() {
220226
return new HandlerMethod(this, handler);
221227
}
222228

229+
public String getShortLogMessage() {
230+
int args = this.method.getParameterTypes().length;
231+
return getBeanType().getName() + "#" + this.method.getName() + "[" + args + " args]";
232+
}
233+
234+
223235
@Override
224-
public boolean equals(Object obj) {
225-
if (this == obj) {
236+
public boolean equals(Object other) {
237+
if (this == other) {
226238
return true;
227239
}
228-
if (obj != null && obj instanceof HandlerMethod) {
229-
HandlerMethod other = (HandlerMethod) obj;
230-
return this.bean.equals(other.bean) && this.method.equals(other.method);
240+
if (!(other instanceof HandlerMethod)) {
241+
return false;
231242
}
232-
return false;
243+
HandlerMethod otherMethod = (HandlerMethod) other;
244+
return (this.bean.equals(otherMethod.bean) && this.method.equals(otherMethod.method));
233245
}
234246

235247
@Override
236248
public int hashCode() {
237-
return this.bean.hashCode() * 31 + this.method.hashCode();
238-
}
239-
240-
public String getShortLogMessage() {
241-
int args = method.getParameterTypes().length;
242-
return getBeanType().getName() + "#" + this.method.getName() + "[" + args + " args]";
249+
return (this.bean.hashCode() * 31 + this.method.hashCode());
243250
}
244251

245252
@Override

spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,7 +30,6 @@
3030
import org.apache.commons.logging.Log;
3131
import org.apache.commons.logging.LogFactory;
3232

33-
import org.springframework.beans.BeansException;
3433
import org.springframework.beans.factory.InitializingBean;
3534
import org.springframework.context.ApplicationContext;
3635
import org.springframework.context.ApplicationContextAware;
@@ -154,7 +153,7 @@ public List<HandlerMethodReturnValueHandler> getCustomReturnValueHandlers() {
154153
/**
155154
* Configure the complete list of supported argument types effectively overriding
156155
* the ones configured by default. This is an advanced option. For most use cases
157-
* it should be sufficient to use {@link #setCustomArgumentResolvers(java.util.List)}.
156+
* it should be sufficient to use {@link #setCustomArgumentResolvers}.
158157
*/
159158
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
160159
if (argumentResolvers == null) {
@@ -171,7 +170,7 @@ public List<HandlerMethodArgumentResolver> getArgumentResolvers() {
171170
/**
172171
* Configure the complete list of supported return value types effectively overriding
173172
* the ones configured by default. This is an advanced option. For most use cases
174-
* it should be sufficient to use {@link #setCustomReturnValueHandlers(java.util.List)}
173+
* it should be sufficient to use {@link #setCustomReturnValueHandlers}.
175174
*/
176175
public void setReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
177176
if (returnValueHandlers == null) {
@@ -193,7 +192,7 @@ public Map<T, HandlerMethod> getHandlerMethods() {
193192
}
194193

195194
@Override
196-
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
195+
public void setApplicationContext(ApplicationContext applicationContext) {
197196
this.applicationContext = applicationContext;
198197
}
199198

@@ -204,7 +203,6 @@ public ApplicationContext getApplicationContext() {
204203

205204
@Override
206205
public void afterPropertiesSet() {
207-
208206
if (this.argumentResolvers.getResolvers().isEmpty()) {
209207
this.argumentResolvers.addResolvers(initArgumentResolvers());
210208
}
@@ -222,17 +220,17 @@ public void afterPropertiesSet() {
222220

223221
/**
224222
* Return the list of argument resolvers to use. Invoked only if the resolvers
225-
* have not already been set via {@link #setArgumentResolvers(java.util.List)}.
226-
* <p>Sub-classes should also take into account custom argument types configured via
227-
* {@link #setCustomArgumentResolvers(java.util.List)}.
223+
* have not already been set via {@link #setArgumentResolvers}.
224+
* <p>Subclasses should also take into account custom argument types configured via
225+
* {@link #setCustomArgumentResolvers}.
228226
*/
229227
protected abstract List<? extends HandlerMethodArgumentResolver> initArgumentResolvers();
230228

231229
/**
232230
* Return the list of return value handlers to use. Invoked only if the return
233-
* value handlers have not already been set via {@link #setReturnValueHandlers(java.util.List)}.
234-
* <p>Sub-classes should also take into account custom return value types configured
235-
* via {@link #setCustomReturnValueHandlers(java.util.List)}.
231+
* value handlers have not already been set via {@link #setReturnValueHandlers}.
232+
* <p>Subclasses should also take into account custom return value types configured
233+
* via {@link #setCustomReturnValueHandlers}.
236234
*/
237235
protected abstract List<? extends HandlerMethodReturnValueHandler> initReturnValueHandlers();
238236

@@ -248,9 +246,8 @@ public void afterPropertiesSet() {
248246
* @param handler the handler to check, either an instance of a Spring bean name
249247
*/
250248
protected final void detectHandlerMethods(Object handler) {
251-
252-
Class<?> handlerType = (handler instanceof String) ?
253-
this.applicationContext.getType((String) handler) : handler.getClass();
249+
Class<?> handlerType = (handler instanceof String ?
250+
this.applicationContext.getType((String) handler) : handler.getClass());
254251

255252
final Class<?> userType = ClassUtils.getUserClass(handlerType);
256253

@@ -285,14 +282,13 @@ public boolean matches(Method method) {
285282
* under the same mapping
286283
*/
287284
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
288-
289285
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
290-
HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
286+
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
291287

292288
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
293-
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean()
294-
+ "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '"
295-
+ oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
289+
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
290+
"' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" +
291+
oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
296292
}
297293

298294
this.handlerMethods.put(mapping, newHandlerMethod);
@@ -313,7 +309,8 @@ protected HandlerMethod createHandlerMethod(Object handler, Method method) {
313309
HandlerMethod handlerMethod;
314310
if (handler instanceof String) {
315311
String beanName = (String) handler;
316-
handlerMethod = new HandlerMethod(beanName, this.applicationContext, method);
312+
handlerMethod = new HandlerMethod(beanName,
313+
this.applicationContext.getAutowireCapableBeanFactory(), method);
317314
}
318315
else {
319316
handlerMethod = new HandlerMethod(handler, method);
@@ -491,9 +488,8 @@ protected void processHandlerMethodException(HandlerMethod handlerMethod, Except
491488
}
492489
this.returnValueHandlers.handleReturnValue(returnValue, returnType, message);
493490
}
494-
catch (Throwable t) {
495-
logger.error("Error while handling exception", t);
496-
return;
491+
catch (Throwable ex2) {
492+
logger.error("Error while processing handler method exception", ex2);
497493
}
498494
}
499495

0 commit comments

Comments
 (0)