Skip to content

Introduce aliases for 'value' annotation attributes [SPR-11393] #16020

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 of 3 tasks
spring-projects-issues opened this issue Feb 5, 2014 · 4 comments
Closed
1 of 3 tasks
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) in: data Issues in data modules (jdbc, orm, oxm, tx) in: messaging Issues in messaging modules (jms, messaging) in: test Issues in the test module in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Feb 5, 2014

Sam Brannen opened SPR-11393 and commented

Background

Java supports a special annotation attribute named value that can be used without being named if an annotation is declared with a single attribute. Due to the convenience it provides, the value attribute is used frequently in annotations across the Spring Framework. However, there are some downsides to making an attribute available only via the value attribute name:

  1. Readability: when the value attribute is used without being named (i.e., as a single attribute), the readability is enhanced due to the reduction of clutter, but if multiple attributes are declared, the readability and usability of the code base suffers. Seeing "value=..." repeated across annotations makes the intent of the code unclear, especially to newcomers to the framework.
  2. Composability: Spring Framework 4.0 (and previous versions of the framework to a lesser extent) provides extensive support for composed annotations (i.e., annotations composed by multiple meta-annotations) and meta-annotation attribute overriding in such composed annotations; however, the value attribute is (for good reasons) not supported as a candidate for overriding.
Example: Multiple Attribute Declarations

Consider the following annotated handler method from a Spring MVC controller for JUG events.

@RequestMapping("/events")
public String list(Model model) {
	model.addAttribute("events", this.eventService.findAllEvents());
	return "events/list";
}

In the above example, only the path for the request mapping is declared as an annotation attribute. In such use cases, the use of the unnamed value attribute is ideal. But how does the code look if multiple attributes must be declared?

@RequestMapping(value = "/events", method = GET)
public String list(Model model) {
	/* body */
}

In the above example, supplying the path on its own is insufficient for the use case at hand. Thus, both the path and method must be supplied. Since the path can only be specified via the value attribute, the code becomes unclear. What does "value" mean in this context?

Wouldn't it be better to be able to be very explicit?

@RequestMapping(path = "/events", method = GET)
public String list(Model model) {
	/* body */
}

The above example introduces an alias for the value attribute. By specifying the path via a path attribute, the meaning of the code becomes clear, even to readers who are not familiar with Spring MVC. Although this annotation is supported in both Servlet and Portlet environments, it has been decided to name the alias path since the Servlet use case is more common.

Example: Meta-annotation Attribute Overrides

Consider the @ContextConfiguration from the Spring TestContext Framework (TCF). When it was introduced in Spring 2.5, it only supported a locations attribute for specifying the resource locations to use for loading an ApplicationContext. After observing common use cases for this annotation in the developer community it became apparent that developers typically only need to declare the locations and none of the other available attributes. So in Spring 3.0, the value attribute was introduced in @ContextConfiguration as an alias for the existing locations attribute.

The fact that both the value and locations attributes are supported in @ContextConfiguration not only overcomes the aforementioned readability issue, but it also allows for @ContextConfiguration to be used as a meta-annotation on a composed annotation with attribute overrides for the locations.

@Transactional
@ContextConfiguration
@Retention(RetentionPolicy.RUNTIME)
public @interface TransactionalTestConfig {

	String[] locations() default {};
}

@TransactionalTestConfig(locations = "/test-config.xml")
public class RepositoryTests {
	/* test body */
}

The above example demonstrates how to override the locations attribute of @ContextConfiguration in a custom composed annotation (@TransactionalTestConfig).

Without the locations alias, this overriding would not be possible.


Deliverables

  1. Introduce a set of common test fixtures that assist in ensuring that annotation attribute look-up is tested consistently across modules.
  2. Special consideration should be taken with regard to component names.
    • The framework provides support for determining the name of an annotated component via special handling of the value attribute in @Component classes or any class meta-annotated with @Component; however, it may prove useful to declare an alias for component names (e.g., componentName) in order to allow the name of a component to be specified via a custom composed annotation without the ambiguity of the value attribute.
    • Component annotations:
      • @Component
      • @Service
      • @Repository
      • @Controller
      • @ControllerAdvice
      • @RestController
      • @Configuration
  3. For each annotation in the Candidate Annotations section below (which has not been rejected):
    • Ensure that the annotation has an alias for the value attribute with a meaningful name.
    • Ensure that the alias name is unique enough that it will not likely lead to naming collisions when multiple annotations are used to construct composed annotations.
      • For example, something as generic as name should likely be avoided if possible.
    • Review the Javadoc for the value attribute and its alias and revise as necessary in order to ensure clarity of purpose.
    • Ensure that all code in the framework that currently looks up the value of said annotation now uses the newly introduced functionality for looking up annotation attributes with aliases (see Introduce unified support for declaring and looking up annotation attribute aliases [SPR-11512] #16137).

Candidate Annotations

The tables below contain annotations in the Spring Framework that fall into one of two categories:

  1. The annotation has a value attribute in addition to other attributes.
  2. The annotation only has a value attribute (i.e., no additional attributes), but it might still benefit from having an alias so that it can be overridden in custom composed annotations (e.g., using the simple name-based convention).

Annotations with a value attribute and other attributes
module Annotation Alias Exists? Alias Name
spring-context @Cacheable (/) cacheNames
spring-context @CacheEvict (/) cacheNames
spring-context @CachePut (/) cacheNames
spring-context @ComponentScan (/) basePackages
spring-context @ComponentScan.Filter (/) classes
spring-context @ImportResource (/) locations
spring-context @ManagedResource (/) objectName
spring-context @Scope (/) name
spring-messaging @Header (/) name
spring-messaging @Payload (/) expression
spring-messaging @SendToUser (/) destinations
spring-test @ActiveProfiles (/) profiles
spring-test @ContextConfiguration (/) locations
spring-test @Sql (/) scripts
spring-test @TestExecutionListeners (/) listeners
spring-test @TestPropertySource (/) locations
spring-tx @Transactional (/) transactionManager
spring-web @ControllerAdvice (/) basePackages
spring-web @CookieValue (/) name
spring-web @CrossOrigin (/) origins
spring-web @MatrixVariable (/) name
spring-web @RequestHeader (/) name
spring-web @RequestMapping (/) path
spring-web @RequestParam (/) name
spring-web @RequestPart (/) name
spring-web @ResponseStatus (/) code
spring-web @SessionAttributes (/) names
spring-webmvc-portlet @ActionMapping (/) name
spring-webmvc-portlet @RenderMapping (/) windowState

Rejected Candidates

The following table contains annotations that in fact have a value attribute (and typically no other attributes) but have been rejected as candidates for receiving aliases.

Note, however, that just because an annotation has been rejected as a candidate for receiving a value-alias, this does not mean that the value attribute will not be able to be used in composed annotations. On the contrary, once the work for #16138 is complete, there will be an annotation-based mechanism for overriding value annotation attributes in meta-annotations.

module Annotation
spring-beans @Qualifier
spring-context @Async
spring-context @DependsOn
spring-context @Lazy
spring-context @Profile
spring-context @Validated
spring-core @Order
spring-messaging @DestinationVariable
spring-messaging @MessageExceptionHandler
spring-messaging @MessageMapping
spring-messaging @SendTo
spring-messaging @SubscribeMapping
spring-test @BootstrapWith
spring-test @Repeat
spring-test @Rollback
spring-test @WebAppConfiguration
spring-webmvc-portlet @ResourceMapping

Affects: 4.0 GA

Issue Links:

1 votes, 11 watchers

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Mar 4, 2014

Sam Brannen commented

This issue depends directly on the outcome of #16137.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Mar 4, 2014

Sam Brannen commented

If you have been following this issue, you might find it useful to know that some of the original deliverables of this task have been split off into separate issues:

Feel free to follow any of these new issues as well as the new parent of this issue, #16136.

Regards,

Sam

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented May 21, 2015

Sam Brannen commented

If you're following this issue, you will likely be interested in knowing that both #16137 and #16138 have been resolved, effectively paving the way for this issue. ;)

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

This issue has been resolved through numerous commits over the past several weeks.

With regard to the defined Deliverables:

  1. "A set of common test fixtures that assist in ensuring that annotation attribute look-up is tested consistently across modules" has not been implemented since @AliasFor semantics are transparently enforced by all related utility methods in AnnotationUtils, AnnotatedElementUtils, and AnnotationAttributes.
  2. Support for attribute aliases in component names (i.e., aliases for the value attributes in @Component, @Service, etc.) has not been implemented since @AliasFor can be used in a custom composed annotation to alias any explicitly named attribute in a meta-annotation.
  3. Each annotation in the Candidate Annotations table has been retrofitted with an attribute alias for its value attribute.

@spring-projects-issues spring-projects-issues added in: messaging Issues in messaging modules (jms, messaging) in: test Issues in the test module in: data Issues in data modules (jdbc, orm, oxm, tx) type: enhancement A general enhancement in: core Issues in core modules (aop, beans, core, context, expression) in: web Issues in web modules (web, webmvc, webflux, websocket) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 4.2 RC2 milestone Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) in: data Issues in data modules (jdbc, orm, oxm, tx) in: messaging Issues in messaging modules (jms, messaging) in: test Issues in the test module in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants