Browse Source

Reinstate support for @javax.annotation.ManagedBean & @javax.inject.Named

This commit reinstates support for the legacy JSR-250
@javax.annotation.ManagedBean and JSR-330 @javax.inject.Named
annotations with regard to component name lookups and component
scanning.

Closes gh-31090
pull/31121/head
Sam Brannen 1 year ago
parent
commit
e1826d2322
  1. 1
      framework-platform/framework-platform.gradle
  2. 1
      spring-context/spring-context.gradle
  3. 11
      spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java
  4. 40
      spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
  5. 24
      spring-context/src/test/java/example/indexed/IndexedJavaxManagedBeanComponent.java
  6. 24
      spring-context/src/test/java/example/indexed/IndexedJavaxNamedComponent.java
  7. 24
      spring-context/src/test/java/example/scannable/JavaxManagedBeanComponent.java
  8. 24
      spring-context/src/test/java/example/scannable/JavaxNamedComponent.java
  9. 12
      spring-context/src/test/java/org/springframework/context/annotation/AnnotationBeanNameGeneratorTests.java
  10. 32
      spring-context/src/test/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProviderTests.java
  11. 4
      spring-context/src/test/resources/example/scannable/spring.components

1
framework-platform/framework-platform.gradle

@ -83,6 +83,7 @@ dependencies { @@ -83,6 +83,7 @@ dependencies {
api("jakarta.xml.bind:jakarta.xml.bind-api:3.0.1")
api("javax.annotation:javax.annotation-api:1.3.2")
api("javax.cache:cache-api:1.1.1")
api("javax.inject:javax.inject:1")
api("javax.money:money-api:1.1")
api("jaxen:jaxen:1.2.0")
api("junit:junit:4.13.2")

1
spring-context/spring-context.gradle

@ -21,6 +21,7 @@ dependencies { @@ -21,6 +21,7 @@ dependencies {
optional("jakarta.interceptor:jakarta.interceptor-api")
optional("jakarta.validation:jakarta.validation-api")
optional("javax.annotation:javax.annotation-api")
optional("javax.inject:javax.inject")
optional("javax.money:money-api")
optional("org.apache.groovy:groovy")
optional("org.apache-extras.beanshell:bsh")

11
spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java

@ -41,8 +41,10 @@ import org.springframework.util.StringUtils; @@ -41,8 +41,10 @@ import org.springframework.util.StringUtils;
* themselves annotated with {@code @Component}.
*
* <p>Also supports Jakarta EE's {@link jakarta.annotation.ManagedBean} and
* JSR-330's {@link jakarta.inject.Named} annotations, if available. Note that
* Spring component annotations always override such standard annotations.
* JSR-330's {@link jakarta.inject.Named} annotations (as well as their pre-Jakarta
* {@code javax.annotation.ManagedBean} and {@code javax.inject.Named} equivalents),
* if available. Note that Spring component annotations always override such
* standard annotations.
*
* <p>If the annotation's value doesn't indicate a bean name, an appropriate
* name will be built based on the short name of the class (with the first
@ -53,6 +55,7 @@ import org.springframework.util.StringUtils; @@ -53,6 +55,7 @@ import org.springframework.util.StringUtils;
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Sam Brannen
* @since 2.5
* @see org.springframework.stereotype.Component#value()
* @see org.springframework.stereotype.Repository#value()
@ -134,7 +137,9 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator { @@ -134,7 +137,9 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) ||
annotationType.equals("jakarta.annotation.ManagedBean") ||
annotationType.equals("jakarta.inject.Named");
annotationType.equals("javax.annotation.ManagedBean") ||
annotationType.equals("jakarta.inject.Named") ||
annotationType.equals("javax.inject.Named");
return (isStereotype && attributes != null && attributes.containsKey("value"));
}

40
spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java

@ -62,11 +62,13 @@ import org.springframework.util.Assert; @@ -62,11 +62,13 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* A component provider that provides candidate components from a base package. Can
* use {@link CandidateComponentsIndex the index} if it is available of scans the
* classpath otherwise. Candidate components are identified by applying exclude and
* include filters. {@link AnnotationTypeFilter}, {@link AssignableTypeFilter} include
* filters on an annotation/superclass that are annotated with {@link Indexed} are
* A component provider that scans for candidate components starting from a
* specified base package. Can use the {@linkplain CandidateComponentsIndex component
* index}, if it is available, and scans the classpath otherwise.
*
* <p>Candidate components are identified by applying exclude and include filters.
* {@link AnnotationTypeFilter} and {@link AssignableTypeFilter} include filters
* for an annotation/target-type that is annotated with {@link Indexed} are
* supported: if any other include filter is specified, the index is ignored and
* classpath scanning is used instead.
*
@ -201,8 +203,9 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC @@ -201,8 +203,9 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
* {@link Repository @Repository}, {@link Service @Service}, and
* {@link Controller @Controller} stereotype annotations.
* <p>Also supports Jakarta EE's {@link jakarta.annotation.ManagedBean} and
* JSR-330's {@link jakarta.inject.Named} annotations, if available.
*
* JSR-330's {@link jakarta.inject.Named} annotations (as well as their
* pre-Jakarta {@code javax.annotation.ManagedBean} and {@code javax.inject.Named}
* equivalents), if available.
*/
@SuppressWarnings("unchecked")
protected void registerDefaultFilters() {
@ -216,11 +219,27 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC @@ -216,11 +219,27 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Jakarta EE) not available - simply skip.
}
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API not available - simply skip.
}
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("jakarta.inject.Named", cl)), false));
logger.trace("JSR-330 'jakarta.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API (as included in Jakarta EE) not available - simply skip.
}
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
@ -306,7 +325,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC @@ -306,7 +325,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
/**
* Scan the class path for candidate components.
* Scan the component index or class path for candidate components.
* @param basePackage the package to check for annotated classes
* @return a corresponding Set of autodetected bean definitions
*/
@ -320,7 +339,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC @@ -320,7 +339,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
}
/**
* Determine if the index can be used by this instance.
* Determine if the component index can be used by this instance.
* @return {@code true} if the index is available and the configuration of this
* instance is supported by it, {@code false} otherwise
* @since 5.0
@ -345,7 +364,8 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC @@ -345,7 +364,8 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
if (filter instanceof AnnotationTypeFilter annotationTypeFilter) {
Class<? extends Annotation> annotationType = annotationTypeFilter.getAnnotationType();
return (AnnotationUtils.isAnnotationDeclaredLocally(Indexed.class, annotationType) ||
annotationType.getName().startsWith("jakarta."));
annotationType.getName().startsWith("jakarta.") ||
annotationType.getName().startsWith("javax."));
}
if (filter instanceof AssignableTypeFilter assignableTypeFilter) {
Class<?> target = assignableTypeFilter.getTargetType();

24
spring-context/src/test/java/example/indexed/IndexedJavaxManagedBeanComponent.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
/*
* Copyright 2002-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.indexed;
/**
* @author Sam Brannen
*/
@javax.annotation.ManagedBean
public class IndexedJavaxManagedBeanComponent {
}

24
spring-context/src/test/java/example/indexed/IndexedJavaxNamedComponent.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
/*
* Copyright 2002-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.indexed;
/**
* @author Sam Brannen
*/
@javax.inject.Named("myIndexedJavaxNamedComponent")
public class IndexedJavaxNamedComponent {
}

24
spring-context/src/test/java/example/scannable/JavaxManagedBeanComponent.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
/*
* Copyright 2002-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.scannable;
/**
* @author Sam Brannen
*/
@javax.annotation.ManagedBean("myJavaxManagedBeanComponent")
public class JavaxManagedBeanComponent {
}

24
spring-context/src/test/java/example/scannable/JavaxNamedComponent.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
/*
* Copyright 2002-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.scannable;
/**
* @author Sam Brannen
*/
@javax.inject.Named("myJavaxNamedComponent")
public class JavaxNamedComponent {
}

12
spring-context/src/test/java/org/springframework/context/annotation/AnnotationBeanNameGeneratorTests.java

@ -24,6 +24,8 @@ import java.lang.annotation.Target; @@ -24,6 +24,8 @@ import java.lang.annotation.Target;
import example.scannable.DefaultNamedComponent;
import example.scannable.JakartaManagedBeanComponent;
import example.scannable.JakartaNamedComponent;
import example.scannable.JavaxManagedBeanComponent;
import example.scannable.JavaxNamedComponent;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
@ -90,11 +92,21 @@ class AnnotationBeanNameGeneratorTests { @@ -90,11 +92,21 @@ class AnnotationBeanNameGeneratorTests {
assertGeneratedName(JakartaNamedComponent.class, "myJakartaNamedComponent");
}
@Test
void generateBeanNameWithJavaxNamedComponent() {
assertGeneratedName(JavaxNamedComponent.class, "myJavaxNamedComponent");
}
@Test
void generateBeanNameWithJakartaManagedBeanComponent() {
assertGeneratedName(JakartaManagedBeanComponent.class, "myJakartaManagedBeanComponent");
}
@Test
void generateBeanNameWithJavaxManagedBeanComponent() {
assertGeneratedName(JavaxManagedBeanComponent.class, "myJavaxManagedBeanComponent");
}
@Test
void generateBeanNameWithCustomStereotypeComponent() {
assertGeneratedName(DefaultNamedComponent.class, "thoreau");

32
spring-context/src/test/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProviderTests.java

@ -29,6 +29,8 @@ import java.util.stream.Stream; @@ -29,6 +29,8 @@ import java.util.stream.Stream;
import example.gh24375.AnnotatedComponent;
import example.indexed.IndexedJakartaManagedBeanComponent;
import example.indexed.IndexedJakartaNamedComponent;
import example.indexed.IndexedJavaxManagedBeanComponent;
import example.indexed.IndexedJavaxNamedComponent;
import example.profilescan.DevComponent;
import example.profilescan.ProfileAnnotatedComponent;
import example.profilescan.ProfileMetaAnnotatedComponent;
@ -40,6 +42,8 @@ import example.scannable.FooService; @@ -40,6 +42,8 @@ import example.scannable.FooService;
import example.scannable.FooServiceImpl;
import example.scannable.JakartaManagedBeanComponent;
import example.scannable.JakartaNamedComponent;
import example.scannable.JavaxManagedBeanComponent;
import example.scannable.JavaxNamedComponent;
import example.scannable.MessageBean;
import example.scannable.NamedComponent;
import example.scannable.NamedStubDao;
@ -100,9 +104,16 @@ class ClassPathScanningCandidateComponentProviderTests { @@ -100,9 +104,16 @@ class ClassPathScanningCandidateComponentProviderTests {
JakartaManagedBeanComponent.class
);
private static final Set<Class<?>> indexedJakartaComponents = Set.of(
private static final Set<Class<?>> scannedJavaxComponents = Set.of(
JavaxNamedComponent.class,
JavaxManagedBeanComponent.class
);
private static final Set<Class<?>> indexedComponents = Set.of(
IndexedJakartaNamedComponent.class,
IndexedJakartaManagedBeanComponent.class
IndexedJakartaManagedBeanComponent.class,
IndexedJavaxNamedComponent.class,
IndexedJavaxManagedBeanComponent.class
);
@ -111,25 +122,28 @@ class ClassPathScanningCandidateComponentProviderTests { @@ -111,25 +122,28 @@ class ClassPathScanningCandidateComponentProviderTests {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
provider.setResourceLoader(new DefaultResourceLoader(
CandidateComponentsTestClassLoader.disableIndex(getClass().getClassLoader())));
testDefault(provider, TEST_BASE_PACKAGE, true, false);
testDefault(provider, TEST_BASE_PACKAGE, true, true, false);
}
@Test
void defaultsWithIndex() {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
provider.setResourceLoader(new DefaultResourceLoader(TEST_BASE_CLASSLOADER));
testDefault(provider, "example", true, true);
testDefault(provider, "example", true, true, true);
}
private void testDefault(ClassPathScanningCandidateComponentProvider provider, String basePackage,
boolean includeScannedJakartaComponents, boolean includeIndexedJakartaComponents) {
boolean includeScannedJakartaComponents, boolean includeScannedJavaxComponents, boolean includeIndexedComponents) {
Set<Class<?>> expectedTypes = new HashSet<>(springComponents);
if (includeScannedJakartaComponents) {
expectedTypes.addAll(scannedJakartaComponents);
}
if (includeIndexedJakartaComponents) {
expectedTypes.addAll(indexedJakartaComponents);
if (includeScannedJavaxComponents) {
expectedTypes.addAll(scannedJavaxComponents);
}
if (includeIndexedComponents) {
expectedTypes.addAll(indexedComponents);
}
Set<BeanDefinition> candidates = provider.findCandidateComponents(basePackage);
@ -202,7 +216,7 @@ class ClassPathScanningCandidateComponentProviderTests { @@ -202,7 +216,7 @@ class ClassPathScanningCandidateComponentProviderTests {
private void testCustomAnnotationTypeIncludeFilter(ClassPathScanningCandidateComponentProvider provider) {
provider.addIncludeFilter(new AnnotationTypeFilter(Component.class));
testDefault(provider, TEST_BASE_PACKAGE, false, false);
testDefault(provider, TEST_BASE_PACKAGE, false, false, false);
}
@Test
@ -295,7 +309,7 @@ class ClassPathScanningCandidateComponentProviderTests { @@ -295,7 +309,7 @@ class ClassPathScanningCandidateComponentProviderTests {
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
assertScannedBeanDefinitions(candidates);
assertBeanTypes(candidates, FooServiceImpl.class, StubFooDao.class, ServiceInvocationCounter.class,
BarComponent.class, JakartaManagedBeanComponent.class);
BarComponent.class, JakartaManagedBeanComponent.class, JavaxManagedBeanComponent.class);
}
@Test

4
spring-context/src/test/resources/example/scannable/spring.components

@ -10,5 +10,9 @@ example.scannable.ServiceInvocationCounter=org.springframework.stereotype.Compon @@ -10,5 +10,9 @@ example.scannable.ServiceInvocationCounter=org.springframework.stereotype.Compon
example.scannable.sub.BarComponent=org.springframework.stereotype.Component
example.scannable.JakartaManagedBeanComponent=jakarta.annotation.ManagedBean
example.scannable.JakartaNamedComponent=jakarta.inject.Named
example.scannable.JavaxManagedBeanComponent=javax.annotation.ManagedBean
example.scannable.JavaxNamedComponent=javax.inject.Named
example.indexed.IndexedJakartaManagedBeanComponent=jakarta.annotation.ManagedBean
example.indexed.IndexedJakartaNamedComponent=jakarta.inject.Named
example.indexed.IndexedJavaxManagedBeanComponent=javax.annotation.ManagedBean
example.indexed.IndexedJavaxNamedComponent=javax.inject.Named

Loading…
Cancel
Save