1
1
/*
2
- * Copyright 2002-2012 the original author or authors.
2
+ * Copyright 2002-2014 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
47
47
*/
48
48
public class InvocableHandlerMethod extends HandlerMethod {
49
49
50
- private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite ();
51
-
52
50
private WebDataBinderFactory dataBinderFactory ;
53
51
52
+ private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite ();
53
+
54
54
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer ();
55
55
56
56
57
57
/**
58
- * Creates an instance from the given handler and method.
58
+ * Create an instance from the given handler and method.
59
59
*/
60
60
public InvocableHandlerMethod (Object bean , Method method ) {
61
61
super (bean , method );
@@ -69,19 +69,19 @@ public InvocableHandlerMethod(HandlerMethod handlerMethod) {
69
69
}
70
70
71
71
/**
72
- * Constructs a new handler method with the given bean instance, method name and parameters.
72
+ * Construct a new handler method with the given bean instance, method name and parameters.
73
73
* @param bean the object bean
74
74
* @param methodName the method name
75
75
* @param parameterTypes the method parameter types
76
76
* @throws NoSuchMethodException when the method cannot be found
77
77
*/
78
- public InvocableHandlerMethod (
79
- Object bean , String methodName , Class <?>... parameterTypes ) throws NoSuchMethodException {
78
+ public InvocableHandlerMethod (Object bean , String methodName , Class <?>... parameterTypes ) throws NoSuchMethodException {
80
79
super (bean , methodName , parameterTypes );
81
80
}
82
81
82
+
83
83
/**
84
- * Sets the {@link WebDataBinderFactory} to be passed to argument resolvers allowing them to create
84
+ * Set the {@link WebDataBinderFactory} to be passed to argument resolvers allowing them to create
85
85
* a {@link WebDataBinder} for data binding and type conversion purposes.
86
86
* @param dataBinderFactory the data binder factory.
87
87
*/
@@ -97,78 +97,74 @@ public void setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverCompo
97
97
}
98
98
99
99
/**
100
- * Set the ParameterNameDiscoverer for resolving parameter names when needed (e.g. default request attribute name).
101
- * <p>Default is an {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer} instance.
100
+ * Set the ParameterNameDiscoverer for resolving parameter names when needed
101
+ * (e.g. default request attribute name).
102
+ * <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer} instance.
102
103
*/
103
104
public void setParameterNameDiscoverer (ParameterNameDiscoverer parameterNameDiscoverer ) {
104
105
this .parameterNameDiscoverer = parameterNameDiscoverer ;
105
106
}
106
107
108
+
107
109
/**
108
110
* Invoke the method after resolving its argument values in the context of the given request. <p>Argument
109
111
* values are commonly resolved through {@link HandlerMethodArgumentResolver}s. The {@code provideArgs}
110
112
* parameter however may supply argument values to be used directly, i.e. without argument resolution.
111
113
* Examples of provided argument values include a {@link WebDataBinder}, a {@link SessionStatus}, or
112
114
* a thrown exception instance. Provided argument values are checked before argument resolvers.
113
- *
114
115
* @param request the current request
115
116
* @param mavContainer the ModelAndViewContainer for this request
116
117
* @param providedArgs "given" arguments matched by type, not resolved
117
118
* @return the raw value returned by the invoked method
118
119
* @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception
119
120
*/
120
- public final Object invokeForRequest (NativeWebRequest request ,
121
- ModelAndViewContainer mavContainer ,
122
- Object ... providedArgs ) throws Exception {
123
- Object [] args = getMethodArgumentValues (request , mavContainer , providedArgs );
121
+ public final Object invokeForRequest (NativeWebRequest request , ModelAndViewContainer mavContainer ,
122
+ Object ... providedArgs ) throws Exception {
124
123
124
+ Object [] args = getMethodArgumentValues (request , mavContainer , providedArgs );
125
125
if (logger .isTraceEnabled ()) {
126
- StringBuilder builder = new StringBuilder ("Invoking [" );
127
- builder .append (this .getMethod ().getName ()).append ("] method with arguments " );
128
- builder .append (Arrays .asList (args ));
129
- logger .trace (builder .toString ());
126
+ StringBuilder sb = new StringBuilder ("Invoking [" );
127
+ sb .append (getBeanType ().getSimpleName ()).append ("." );
128
+ sb .append (getMethod ().getName ()).append ("] method with arguments " );
129
+ sb .append (Arrays .asList (args ));
130
+ logger .trace (sb .toString ());
130
131
}
131
-
132
132
Object returnValue = invoke (args );
133
-
134
133
if (logger .isTraceEnabled ()) {
135
- logger .trace ("Method [" + this . getMethod ().getName () + "] returned [" + returnValue + "]" );
134
+ logger .trace ("Method [" + getMethod ().getName () + "] returned [" + returnValue + "]" );
136
135
}
137
-
138
136
return returnValue ;
139
137
}
140
138
141
139
/**
142
140
* Get the method argument values for the current request.
143
141
*/
144
- private Object [] getMethodArgumentValues (
145
- NativeWebRequest request , ModelAndViewContainer mavContainer ,
142
+ private Object [] getMethodArgumentValues (NativeWebRequest request , ModelAndViewContainer mavContainer ,
146
143
Object ... providedArgs ) throws Exception {
147
144
148
145
MethodParameter [] parameters = getMethodParameters ();
149
146
Object [] args = new Object [parameters .length ];
150
147
for (int i = 0 ; i < parameters .length ; i ++) {
151
148
MethodParameter parameter = parameters [i ];
152
- parameter .initParameterNameDiscovery (parameterNameDiscoverer );
149
+ parameter .initParameterNameDiscovery (this . parameterNameDiscoverer );
153
150
GenericTypeResolver .resolveParameterType (parameter , getBean ().getClass ());
154
-
155
151
args [i ] = resolveProvidedArgument (parameter , providedArgs );
156
152
if (args [i ] != null ) {
157
153
continue ;
158
154
}
159
-
160
- if (argumentResolvers .supportsParameter (parameter )) {
155
+ if (this .argumentResolvers .supportsParameter (parameter )) {
161
156
try {
162
- args [i ] = argumentResolvers .resolveArgument (parameter , mavContainer , request , dataBinderFactory );
157
+ args [i ] = this .argumentResolvers .resolveArgument (
158
+ parameter , mavContainer , request , this .dataBinderFactory );
163
159
continue ;
164
- } catch (Exception ex ) {
160
+ }
161
+ catch (Exception ex ) {
165
162
if (logger .isTraceEnabled ()) {
166
163
logger .trace (getArgumentResolutionErrorMessage ("Error resolving argument" , i ), ex );
167
164
}
168
165
throw ex ;
169
166
}
170
167
}
171
-
172
168
if (args [i ] == null ) {
173
169
String msg = getArgumentResolutionErrorMessage ("No suitable resolver for argument" , i );
174
170
throw new IllegalStateException (msg );
@@ -214,17 +210,17 @@ private Object resolveProvidedArgument(MethodParameter parameter, Object... prov
214
210
* Invoke the handler method with the given argument values.
215
211
*/
216
212
private Object invoke (Object ... args ) throws Exception {
217
- ReflectionUtils .makeAccessible (this . getBridgedMethod ());
213
+ ReflectionUtils .makeAccessible (getBridgedMethod ());
218
214
try {
219
215
return getBridgedMethod ().invoke (getBean (), args );
220
216
}
221
- catch (IllegalArgumentException e ) {
222
- String msg = getInvocationErrorMessage ( e . getMessage (), args );
223
- throw new IllegalArgumentException ( msg , e );
217
+ catch (IllegalArgumentException ex ) {
218
+ assertTargetBean ( getBridgedMethod (), getBean (), args );
219
+ throw new IllegalStateException ( getInvocationErrorMessage ( ex . getMessage (), args ), ex );
224
220
}
225
- catch (InvocationTargetException e ) {
221
+ catch (InvocationTargetException ex ) {
226
222
// Unwrap for HandlerExceptionResolvers ...
227
- Throwable targetException = e .getTargetException ();
223
+ Throwable targetException = ex .getTargetException ();
228
224
if (targetException instanceof RuntimeException ) {
229
225
throw (RuntimeException ) targetException ;
230
226
}
@@ -241,6 +237,25 @@ else if (targetException instanceof Exception) {
241
237
}
242
238
}
243
239
240
+ /**
241
+ * Assert that the target bean class is an instance of the class where the given
242
+ * method is declared. In some cases the actual controller instance at request-
243
+ * processing time may be a JDK dynamic proxy (lazy initialization, prototype
244
+ * beans, and others). {@code @Controller}'s that require proxying should prefer
245
+ * class-based proxy mechanisms.
246
+ */
247
+ private void assertTargetBean (Method method , Object targetBean , Object [] args ) {
248
+ Class <?> methodDeclaringClass = method .getDeclaringClass ();
249
+ Class <?> targetBeanClass = targetBean .getClass ();
250
+ if (!methodDeclaringClass .isAssignableFrom (targetBeanClass )) {
251
+ String msg = "The mapped controller method class '" + methodDeclaringClass .getName () +
252
+ "' is not an instance of the actual controller bean instance '" +
253
+ targetBeanClass .getName () + "'. If the controller requires proxying " +
254
+ "(e.g. due to @Transactional), please use class-based proxying." ;
255
+ throw new IllegalStateException (getInvocationErrorMessage (msg , args ));
256
+ }
257
+ }
258
+
244
259
private String getInvocationErrorMessage (String message , Object [] resolvedArgs ) {
245
260
StringBuilder sb = new StringBuilder (getDetailedErrorMessage (message ));
246
261
sb .append ("Resolved arguments: \n " );
0 commit comments