Retain support for legacy PostConstruct/PreDestroy/Inject variants

Also removes JAX-WS support from CommonAnnotationBeanPostProcessor.

Closes gh-27444
See gh-27422
This commit is contained in:
Juergen Hoeller
2021-09-21 17:07:42 +02:00
parent 6976bf774b
commit 774583dfa7
7 changed files with 92 additions and 183 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -23,6 +23,7 @@ import java.util.Set;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -91,11 +92,17 @@ public abstract class AnnotationConfigUtils {
"org.springframework.context.annotation.internalRequiredAnnotationProcessor";
/**
* The bean name of the internally managed JSR-250 annotation processor.
* The bean name of the internally managed common annotation processor.
*/
public static final String COMMON_ANNOTATION_PROCESSOR_BEAN_NAME =
"org.springframework.context.annotation.internalCommonAnnotationProcessor";
/**
* The bean name of the internally managed JSR-250 annotation processor.
*/
private static final String JSR250_ANNOTATION_PROCESSOR_BEAN_NAME =
"org.springframework.context.annotation.internalJsr250AnnotationProcessor";
/**
* The bean name of the internally managed JPA annotation processor.
*/
@@ -117,16 +124,18 @@ public abstract class AnnotationConfigUtils {
public static final String EVENT_LISTENER_FACTORY_BEAN_NAME =
"org.springframework.context.event.internalEventListenerFactory";
private static final boolean jsr250Present;
private static final boolean jpaPresent;
private static final ClassLoader classLoader = AnnotationConfigUtils.class.getClassLoader();
static {
ClassLoader classLoader = AnnotationConfigUtils.class.getClassLoader();
jsr250Present = ClassUtils.isPresent("jakarta.annotation.Resource", classLoader);
jpaPresent = ClassUtils.isPresent("jakarta.persistence.EntityManagerFactory", classLoader) &&
ClassUtils.isPresent(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, classLoader);
}
private static final boolean jakartaAnnotationsPresent =
ClassUtils.isPresent("jakarta.annotation.PostConstruct", classLoader);
private static final boolean jsr250Present =
ClassUtils.isPresent("javax.annotation.PostConstruct", classLoader);
private static final boolean jpaPresent =
ClassUtils.isPresent("jakarta.persistence.EntityManagerFactory", classLoader) &&
ClassUtils.isPresent(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, classLoader);
/**
@@ -172,13 +181,28 @@ public abstract class AnnotationConfigUtils {
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
// Check for Jakarta Annotations support, and if present add the CommonAnnotationBeanPostProcessor.
if (jakartaAnnotationsPresent && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add an InitDestroyAnnotationBeanPostProcessor
// for the javax variant of PostConstruct/PreDestroy.
if (jsr250Present && !registry.containsBeanDefinition(JSR250_ANNOTATION_PROCESSOR_BEAN_NAME)) {
try {
RootBeanDefinition def = new RootBeanDefinition(InitDestroyAnnotationBeanPostProcessor.class);
def.getPropertyValues().add("initAnnotationType", classLoader.loadClass("javax.annotation.PostConstruct"));
def.getPropertyValues().add("destroyAnnotationType", classLoader.loadClass("javax.annotation.PreDestroy"));
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, JSR250_ANNOTATION_PROCESSOR_BEAN_NAME));
}
catch (ClassNotFoundException ex) {
// Failed to load javax variants of the annotation types -> ignore.
}
}
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -21,13 +21,10 @@ import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -37,15 +34,10 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.namespace.QName;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import jakarta.ejb.EJB;
import jakarta.xml.ws.Service;
import jakarta.xml.ws.WebServiceClient;
import jakarta.xml.ws.WebServiceRef;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.ProxyFactory;
@@ -77,10 +69,9 @@ import org.springframework.util.StringValueResolver;
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
* that supports common Java annotations out of the box, in particular the JSR-250
* that supports common Java annotations out of the box, in particular the common
* annotations in the {@code jakarta.annotation} package. These common Java
* annotations are supported in many Jakarta EE technologies (e.g. JSF 1.2),
* as well as in Java 6's JAX-WS.
* annotations are supported in many Jakarta EE technologies (e.g. JSF and JAX-RS).
*
* <p>This post-processor includes support for the {@link jakarta.annotation.PostConstruct}
* and {@link jakarta.annotation.PreDestroy} annotations - as init annotation
@@ -95,12 +86,8 @@ import org.springframework.util.StringValueResolver;
* and default names as well. The target beans can be simple POJOs, with no special
* requirements other than the type having to match.
*
* <p>The JAX-WS {@link javax.xml.ws.WebServiceRef} annotation is supported too,
* analogous to {@link jakarta.annotation.Resource} but with the capability of creating
* specific JAX-WS service endpoints. This may either point to an explicitly defined
* resource by name or operate on a locally specified JAX-WS service class. Finally,
* this post-processor also supports the EJB 3 {@link jakarta.ejb.EJB} annotation,
* analogous to {@link jakarta.annotation.Resource} as well, with the capability to
* <p>This post-processor also supports the EJB 3 {@link jakarta.ejb.EJB} annotation,
* analogous to {@link jakarta.annotation.Resource}, with the capability to
* specify both a local bean name and a global JNDI name for fallback retrieval.
* The target beans can be plain POJOs as well as EJB 3 Session Beans in this case.
*
@@ -143,22 +130,15 @@ import org.springframework.util.StringValueResolver;
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
@Nullable
private static final Class<? extends Annotation> webServiceRefClass;
private static final Set<Class<? extends Annotation>> resourceAnnotationTypes = new LinkedHashSet<>(4);
@Nullable
private static final Class<? extends Annotation> ejbClass;
private static final Set<Class<? extends Annotation>> resourceAnnotationTypes = new LinkedHashSet<>(4);
static {
webServiceRefClass = loadAnnotationType("javax.xml.ws.WebServiceRef");
ejbClass = loadAnnotationType("jakarta.ejb.EJB");
resourceAnnotationTypes.add(Resource.class);
if (webServiceRefClass != null) {
resourceAnnotationTypes.add(webServiceRefClass);
}
ejbClass = loadAnnotationType("jakarta.ejb.EJB");
if (ejbClass != null) {
resourceAnnotationTypes.add(ejbClass);
}
@@ -195,15 +175,11 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
}
/**
* Ignore the given resource type when resolving {@code @Resource}
* annotations.
* <p>By default, the {@code javax.xml.ws.WebServiceContext} interface
* will be ignored, since it will be resolved by the JAX-WS runtime.
* Ignore the given resource type when resolving {@code @Resource} annotations.
* @param resourceType the resource type to ignore
*/
public void ignoreResourceType(String resourceType) {
@@ -361,13 +337,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
ReflectionUtils.doWithLocalFields(targetClass, field -> {
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
}
currElements.add(new WebServiceRefElement(field, field, null));
}
else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {
if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static fields");
}
@@ -389,17 +359,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
return;
}
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
}
else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
@@ -649,91 +609,6 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
}
/**
* Class representing injection information about an annotated field
* or setter method, supporting the @WebServiceRef annotation.
*/
private class WebServiceRefElement extends LookupElement {
private final Class<?> elementType;
private final String wsdlLocation;
public WebServiceRefElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
super(member, pd);
WebServiceRef resource = ae.getAnnotation(WebServiceRef.class);
String resourceName = resource.name();
Class<?> resourceType = resource.type();
this.isDefaultName = !StringUtils.hasLength(resourceName);
if (this.isDefaultName) {
resourceName = this.member.getName();
if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
resourceName = Introspector.decapitalize(resourceName.substring(3));
}
}
if (Object.class != resourceType) {
checkResourceType(resourceType);
}
else {
// No resource type specified... check field/method.
resourceType = getResourceType();
}
this.name = resourceName;
this.elementType = resourceType;
if (Service.class.isAssignableFrom(resourceType)) {
this.lookupType = resourceType;
}
else {
this.lookupType = resource.value();
}
this.mappedName = resource.mappedName();
this.wsdlLocation = resource.wsdlLocation();
}
@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
Service service;
try {
service = (Service) getResource(this, requestingBeanName);
}
catch (NoSuchBeanDefinitionException notFound) {
// Service to be created through generated class.
if (Service.class == this.lookupType) {
throw new IllegalStateException("No resource with name '" + this.name + "' found in context, " +
"and no specific JAX-WS Service subclass specified. The typical solution is to either specify " +
"a LocalJaxWsServiceFactoryBean with the given name or to specify the (generated) Service " +
"subclass as @WebServiceRef(...) value.");
}
if (StringUtils.hasLength(this.wsdlLocation)) {
try {
Constructor<?> ctor = this.lookupType.getConstructor(URL.class, QName.class);
WebServiceClient clientAnn = this.lookupType.getAnnotation(WebServiceClient.class);
if (clientAnn == null) {
throw new IllegalStateException("JAX-WS Service class [" + this.lookupType.getName() +
"] does not carry a WebServiceClient annotation");
}
service = (Service) BeanUtils.instantiateClass(ctor,
new URL(this.wsdlLocation), new QName(clientAnn.targetNamespace(), clientAnn.name()));
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException("JAX-WS Service class [" + this.lookupType.getName() +
"] does not have a (URL, QName) constructor. Cannot apply specified WSDL location [" +
this.wsdlLocation + "].");
}
catch (MalformedURLException ex) {
throw new IllegalArgumentException(
"Specified WSDL location [" + this.wsdlLocation + "] isn't a valid URL");
}
}
else {
service = (Service) BeanUtils.instantiateClass(this.lookupType);
}
}
return service.getPort(this.elementType);
}
}
/**
* Class representing injection information about an annotated field
* or setter method, supporting the @EJB annotation.