Skip to content

Commit

Permalink
Require type-level @controller annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
rstoyanchev committed Dec 14, 2021
1 parent 2db6795 commit 3600644
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,7 @@ public void afterPropertiesSet() {
*/
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,7 @@ public RequestMappingInfo.BuilderConfiguration getBuilderConfiguration() {
*/
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
Expand Down Expand Up @@ -78,7 +78,7 @@ static Object[][] handlerTypes() {
// { ParameterizedSubclassDoesNotOverrideConcreteImplementationsFromGenericAbstractSuperclass.class, true }, // CGLIB proxy
// { ParameterizedSubclassDoesNotOverrideConcreteImplementationsFromGenericAbstractSuperclass.class, false },

{ InterfaceController.class, true }, // JDK dynamic proxy
// { InterfaceController.class, true }, // JDK dynamic proxy (gh-22154: no longer supported))
{ InterfaceController.class, false },

{ ParameterizedInterfaceController.class, false }, // no AOP
Expand Down Expand Up @@ -250,6 +250,7 @@ interface MappingInterface {
* <p>JDK Dynamic proxy: All annotations must be on the interface.
* <p>Without AOP: Annotations can be on interface methods except parameter annotations.
*/
@Controller
static class InterfaceController implements MappingInterface {

@Override
Expand Down Expand Up @@ -443,6 +444,7 @@ interface MappingGenericInterface<A, B, C> {
* <p>All annotations can be on interface except parameter annotations.
* <p>Cannot be used as JDK dynamic proxy since parameterized interface does not contain type information.
*/
@Controller
static class ParameterizedInterfaceController implements MappingGenericInterface<String, Date, Date> {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
* @author Rossen Stoyanchev
* @author Sam Brannen
*/
@SuppressWarnings("unused")
public class MvcUriComponentsBuilderTests {

private final MockHttpServletRequest request = new MockHttpServletRequest();
Expand Down Expand Up @@ -455,7 +456,8 @@ public void fromControllerWithPrefix() {
this.request.setServerPort(9999);
this.request.setContextPath("/base");

assertThat(fromController(PersonsAddressesController.class).buildAndExpand("123").toString()).isEqualTo("https://example.org:9999/base/api/people/123/addresses");
assertThat(fromController(PersonsAddressesController.class).buildAndExpand("123").toString())
.isEqualTo("https://example.org:9999/base/api/people/123/addresses");
}

@Test
Expand All @@ -468,8 +470,12 @@ public void fromMethodWithPrefix() {
this.request.setServerPort(9999);
this.request.setContextPath("/base");

assertThat(fromMethodCall(on(PersonsAddressesController.class).getAddressesForCountry("DE"))
.buildAndExpand("123").toString()).isEqualTo("https://example.org:9999/base/api/people/123/addresses/DE");
String url = fromMethodCall(on(PersonsAddressesController.class)
.getAddressesForCountry("DE"))
.buildAndExpand("123")
.toString();

assertThat(url).isEqualTo("https://example.org:9999/base/api/people/123/addresses/DE");
}

private void initWebApplicationContext(Class<?> configClass) {
Expand Down Expand Up @@ -500,6 +506,7 @@ static class PersonControllerImpl implements PersonController {
}


@Controller
@RequestMapping("/people/{id}/addresses")
static class PersonsAddressesController {

Expand All @@ -509,6 +516,7 @@ HttpEntity<Void> getAddressesForCountry(@PathVariable String country) {
}
}

@Controller
@RequestMapping({"people"})
static class PathWithoutLeadingSlashController {

Expand Down
23 changes: 23 additions & 0 deletions src/docs/asciidoc/web/webflux.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1348,6 +1348,29 @@ directly to the response body versus view resolution and rendering with an HTML



[[webflux-ann-requestmapping-proxying]]
==== AOP Proxies
[.small]#<<web.adoc#mvc-ann-requestmapping-proxying, Web MVC>>#

In some cases, you may need to decorate a controller 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 automatically the case with such annotations
directly on the controller.

If the controller implements an interface, and needs AOP proxying, you may need to
explicitly configure class-based proxying. For example, with `@EnableTransactionManagement`
you can change to `@EnableTransactionManagement(proxyTargetClass = true)`, and with
`<tx:annotation-driven/>` you can change to `<tx:annotation-driven proxy-target-class="true"/>`.

NOTE: Keep in mind that as of 6.0, with interface proxying, Spring MVC no longer detects
controllers based solely on a type-level `@RequestMapping` annotation on the interface.
Please, enable class based proxying, or otherwise the interface must also have an
`@Controller` annotation.




[[webflux-ann-requestmapping]]
=== Request Mapping
[.small]#<<web.adoc#mvc-ann-requestmapping, Web MVC>>#
Expand Down
20 changes: 13 additions & 7 deletions src/docs/asciidoc/web/webmvc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1512,17 +1512,23 @@ directly to the response body versus view resolution and rendering with an HTML

[[mvc-ann-requestmapping-proxying]]
==== AOP Proxies
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-proxying, WebFlux>>#

In some cases, you may need to decorate a controller 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 (such as `InitializingBean`, `*Aware`, and others), you may need to explicitly
configure class-based proxying. For example, with `<tx:annotation-driven/>` you can
change to `<tx:annotation-driven proxy-target-class="true"/>`, and with
`@EnableTransactionManagement` you can change to
`@EnableTransactionManagement(proxyTargetClass = true)`.
using class-based proxying. This is automatically the case with such annotations
directly on the controller.

If the controller implements an interface, and needs AOP proxying, you may need to
explicitly configure class-based proxying. For example, with `@EnableTransactionManagement`
you can change to `@EnableTransactionManagement(proxyTargetClass = true)`, and with
`<tx:annotation-driven/>` you can change to `<tx:annotation-driven proxy-target-class="true"/>`.

NOTE: Keep in mind that as of 6.0, with interface proxying, Spring MVC no longer detects
controllers based solely on a type-level `@RequestMapping` annotation on the interface.
Please, enable class based proxying, or otherwise the interface must also have an
`@Controller` annotation.



Expand Down

0 comments on commit 3600644

Please sign in to comment.