diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java
index b0005f8ce1..78c4c3a7db 100644
--- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java
+++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -183,11 +183,11 @@ public class InvocableHandlerMethod extends HandlerMethod {
private Object invoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(this.getBridgedMethod());
try {
+ assertTargetBean(getBridgedMethod(), getBean(), args);
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException e) {
- String msg = getInvocationErrorMessage(e.getMessage(), args);
- throw new IllegalArgumentException(msg, e);
+ throw new IllegalArgumentException(getInvocationErrorMessage(e.getMessage(), args), e);
}
catch (InvocationTargetException e) {
// Unwrap for HandlerExceptionResolvers ...
@@ -208,6 +208,25 @@ public class InvocableHandlerMethod extends HandlerMethod {
}
}
+ /**
+ * Assert that the target bean class is an instance of the class where the given
+ * method is declared. In some cases the actual controller instance at request-
+ * processing time may be a JDK dynamic proxy (lazy initialization, prototype
+ * beans, and others). {@code @Controller}'s that require proxying should prefer
+ * class-based proxy mechanisms.
+ */
+ private void assertTargetBean(Method method, Object targetBean, Object[] args) {
+ Class> methodDeclaringClass = method.getDeclaringClass();
+ Class> targetBeanClass = targetBean.getClass();
+ if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) {
+ String message = "The mapped controller method class '" + methodDeclaringClass.getName() +
+ "' is not an instance of the actual controller bean instance '" +
+ targetBeanClass.getName() + "'. If the controller requires proxying " +
+ "(e.g. due to @Transactional), please use class-based proxying.";
+ throw new IllegalArgumentException(getInvocationErrorMessage(message, args));
+ }
+ }
+
private String getInvocationErrorMessage(String message, Object[] resolvedArgs) {
StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message));
sb.append("Resolved arguments: \n");
diff --git a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java
index 4942bf0922..a0b0cd6276 100644
--- a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java
+++ b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -211,11 +211,11 @@ public class InvocableHandlerMethod extends HandlerMethod {
private Object invoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(this.getBridgedMethod());
try {
+ assertTargetBean(getBridgedMethod(), getBean(), args);
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException e) {
- String msg = getInvocationErrorMessage(e.getMessage(), args);
- throw new IllegalArgumentException(msg, e);
+ throw new IllegalArgumentException(getInvocationErrorMessage(e.getMessage(), args), e);
}
catch (InvocationTargetException e) {
// Unwrap for HandlerExceptionResolvers ...
@@ -236,6 +236,25 @@ public class InvocableHandlerMethod extends HandlerMethod {
}
}
+ /**
+ * Assert that the target bean class is an instance of the class where the given
+ * method is declared. In some cases the actual controller instance at request-
+ * processing time may be a JDK dynamic proxy (lazy initialization, prototype
+ * beans, and others). {@code @Controller}'s that require proxying should prefer
+ * class-based proxy mechanisms.
+ */
+ private void assertTargetBean(Method method, Object targetBean, Object[] args) {
+ Class> methodDeclaringClass = method.getDeclaringClass();
+ Class> targetBeanClass = targetBean.getClass();
+ if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) {
+ String message = "The mapped controller method class '" + methodDeclaringClass.getName() +
+ "' is not an instance of the actual controller bean instance '" +
+ targetBeanClass.getName() + "'. If the controller requires proxying " +
+ "(e.g. due to @Transactional), please use class-based proxying.";
+ throw new IllegalArgumentException(getInvocationErrorMessage(message, args));
+ }
+ }
+
private String getInvocationErrorMessage(String message, Object[] resolvedArgs) {
StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message));
sb.append("Resolved arguments: \n");
diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc
index 8fee5fb45d..6fcb274ead 100644
--- a/src/asciidoc/index.adoc
+++ b/src/asciidoc/index.adoc
@@ -28381,7 +28381,7 @@ snippet:
[[mvc-ann-requestmapping]]
-==== Mapping Requests With Using @RequestMapping
+==== Mapping Requests With @RequestMapping
You use the `@RequestMapping` annotation to map URLs such as `/appointments` onto an
entire class or a particular handler method. Typically the class-level annotation maps a
@@ -28472,26 +28472,17 @@ application shows a multi-action controller using `@RequestMapping`:
}
----
-.@RequestMapping On Interface Methods
-[TIP]
-====
-
-A common pitfall when working with annotated controller classes happens when applying
-functionality that requires creating a proxy for the controller object (e.g.
-`@Transactional` methods). Usually you will introduce an interface for the controller in
-order to use JDK dynamic proxies. To make this work you must move the `@RequestMapping`
-annotations, as well as any other type and method-level annotations (e.g.
-`@ModelAttribute`, `@InitBinder`) to the interface as well as the mapping mechanism can
-only "see" the interface exposed by the proxy. Alternatively, you could activate
-`proxy-target-class="true"` in the configuration for the functionality applied to the
-controller (in our transaction scenario in ``). Doing so
-indicates that CGLIB-based subclass proxies should be used instead of interface-based
-JDK proxies. For more information on various proxying mechanisms see <>.
-
-Note however that method argument annotations, e.g. `@RequestParam`, must be present in
-the method signatures of the controller class.
-====
+[[mvc-ann-requestmapping-proxying]]
+===== `@Controller`'s and AOP Proxying
+In some cases a controller may need to be decorated with an AOP proxy at runtime.
+One example is if you choose to have `@Transactional` annotations directly on the
+controller. When this is the case, for controllers specifically, we recommend
+using class-based proxying. This is typically the default choice with controllers.
+However if a controller must implement an interface that is not a Spring Context
+callback (e.g. `InitializingBean`, `*Aware`, etc), you may need to explicitly
+configure class-based proxying. For example with ``,
+change to ``.
[[mvc-ann-requestmapping-31-vs-30]]
===== New Support Classes for @RequestMapping methods in Spring MVC 3.1
@@ -29465,14 +29456,6 @@ attribute name:
}
----
-[NOTE]
-====
-When using controller interfaces (e.g., for AOP proxying), make sure to consistently put
-__all__ your mapping annotations - such as `@RequestMapping` and `@SessionAttributes` -
-on the controller __interface__ rather than on the implementation class.
-====
-
-
[[mvc-ann-redirect-attributes]]
===== Specifying redirect and flash attributes
By default all model attributes are considered to be exposed as URI template variables