/*
* Copyright 2002-2019 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
*
* http://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 org.springframework.context.annotation;
import java.beans.Introspector;
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;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceRef;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.EmbeddedValueResolver;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.jndi.support.SimpleJndiBeanFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;
BeanPostProcessor
implementation that supports common Java annotations out of the box, in particular the JSR-250 annotations in the javax.annotation
package. These common Java annotations are supported in many Java EE 5 technologies (e.g. JSF 1.2), as well as in Java 6's JAX-WS. This post-processor includes support for the PostConstruct
and PreDestroy
annotations - as init annotation and destroy annotation, respectively - through inheriting from InitDestroyAnnotationBeanPostProcessor
with pre-configured annotation types.
The central element is the Resource
annotation for annotation-driven injection of named beans, by default from the containing Spring BeanFactory, with only mappedName
references resolved in JNDI. The "alwaysUseJndiLookup" flag
enforces JNDI lookups equivalent to standard Java EE 5 resource injection for name
references and default names as well. The target beans can be simple POJOs, with no special requirements other than the type having to match.
The JAX-WS WebServiceRef
annotation is supported too, analogous to 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 EJB
annotation, analogous to Resource
as well, 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.
The common annotations supported by this post-processor are available in
Java 6 (JDK 1.6) as well as in Java EE 5/6 (which provides a standalone jar for
its common annotations as well, allowing for use in any Java 5 based application).
For default usage, resolving resource names as Spring bean names,
simply define the following in your application context:
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
For direct JNDI access, resolving resource names as JNDI resource references
within the Java EE application's "java:comp/env/" namespace, use the following:
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
<property name="alwaysUseJndiLookup" value="true"/>
</bean>
mappedName
references will always be resolved in JNDI, allowing for global JNDI names (including "java:" prefix) as well. The "alwaysUseJndiLookup" flag just affects name
references and default names (inferred from the field name / property name). NOTE: A default CommonAnnotationBeanPostProcessor will be registered
by the "context:annotation-config" and "context:component-scan" XML tags.
Remove or turn off the default annotation configuration there if you intend
to specify a custom CommonAnnotationBeanPostProcessor bean definition!
NOTE: Annotation injection will be performed before XML injection; thus
the latter configuration will override the former for properties wired through
both approaches.
Author: Juergen Hoeller See Also: Since: 2.5
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
* that supports common Java annotations out of the box, in particular the JSR-250
* annotations in the {@code javax.annotation} package. These common Java
* annotations are supported in many Java EE 5 technologies (e.g. JSF 1.2),
* as well as in Java 6's JAX-WS.
*
* <p>This post-processor includes support for the {@link javax.annotation.PostConstruct}
* and {@link javax.annotation.PreDestroy} annotations - as init annotation
* and destroy annotation, respectively - through inheriting from
* {@link InitDestroyAnnotationBeanPostProcessor} with pre-configured annotation types.
*
* <p>The central element is the {@link javax.annotation.Resource} annotation
* for annotation-driven injection of named beans, by default from the containing
* Spring BeanFactory, with only {@code mappedName} references resolved in JNDI.
* The {@link #setAlwaysUseJndiLookup "alwaysUseJndiLookup" flag} enforces JNDI lookups
* equivalent to standard Java EE 5 resource injection for {@code name} references
* 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 javax.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 javax.ejb.EJB} annotation,
* analogous to {@link javax.annotation.Resource} as well, 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.
*
* <p>The common annotations supported by this post-processor are available in
* Java 6 (JDK 1.6) as well as in Java EE 5/6 (which provides a standalone jar for
* its common annotations as well, allowing for use in any Java 5 based application).
*
* <p>For default usage, resolving resource names as Spring bean names,
* simply define the following in your application context:
*
* <pre class="code">
* <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/></pre>
*
* For direct JNDI access, resolving resource names as JNDI resource references
* within the Java EE application's "java:comp/env/" namespace, use the following:
*
* <pre class="code">
* <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
* <property name="alwaysUseJndiLookup" value="true"/>
* </bean></pre>
*
* {@code mappedName} references will always be resolved in JNDI,
* allowing for global JNDI names (including "java:" prefix) as well. The
* "alwaysUseJndiLookup" flag just affects {@code name} references and
* default names (inferred from the field name / property name).
*
* <p><b>NOTE:</b> A default CommonAnnotationBeanPostProcessor will be registered
* by the "context:annotation-config" and "context:component-scan" XML tags.
* Remove or turn off the default annotation configuration there if you intend
* to specify a custom CommonAnnotationBeanPostProcessor bean definition!
* <p><b>NOTE:</b> Annotation injection will be performed <i>before</i> XML injection; thus
* the latter configuration will override the former for properties wired through
* both approaches.
*
* @author Juergen Hoeller
* @since 2.5
* @see #setAlwaysUseJndiLookup
* @see #setResourceFactory
* @see org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor
* @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
*/
@SuppressWarnings("serial")
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
@Nullable
private static Class<? extends Annotation> webServiceRefClass;
@Nullable
private static Class<? extends Annotation> ejbRefClass;
static {
try {
@SuppressWarnings("unchecked")
Class<? extends Annotation> clazz = (Class<? extends Annotation>)
ClassUtils.forName("javax.xml.ws.WebServiceRef", CommonAnnotationBeanPostProcessor.class.getClassLoader());
webServiceRefClass = clazz;
}
catch (ClassNotFoundException ex) {
webServiceRefClass = null;
}
try {
@SuppressWarnings("unchecked")
Class<? extends Annotation> clazz = (Class<? extends Annotation>)
ClassUtils.forName("javax.ejb.EJB", CommonAnnotationBeanPostProcessor.class.getClassLoader());
ejbRefClass = clazz;
}
catch (ClassNotFoundException ex) {
ejbRefClass = null;
}
}
private final Set<String> ignoredResourceTypes = new HashSet<>(1);
private boolean fallbackToDefaultTypeMatch = true;
private boolean alwaysUseJndiLookup = false;
private transient BeanFactory jndiFactory = new SimpleJndiBeanFactory();
@Nullable
private transient BeanFactory resourceFactory;
@Nullable
private transient BeanFactory beanFactory;
@Nullable
private transient StringValueResolver embeddedValueResolver;
private final transient Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);
Create a new CommonAnnotationBeanPostProcessor, with the init and destroy annotation types set to PostConstruct
and PreDestroy
, respectively. /**
* Create a new CommonAnnotationBeanPostProcessor,
* with the init and destroy annotation types set to
* {@link javax.annotation.PostConstruct} and {@link javax.annotation.PreDestroy},
* respectively.
*/
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
}
Ignore the given resource type when resolving @Resource
annotations. By default, the javax.xml.ws.WebServiceContext
interface will be ignored, since it will be resolved by the JAX-WS runtime.
Params: - resourceType – the resource type to ignore
/**
* 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.
* @param resourceType the resource type to ignore
*/
public void ignoreResourceType(String resourceType) {
Assert.notNull(resourceType, "Ignored resource type must not be null");
this.ignoredResourceTypes.add(resourceType);
}
Set whether to allow a fallback to a type match if no explicit name has been
specified. The default name (i.e. the field name or bean property name) will
still be checked first; if a bean of that name exists, it will be taken.
However, if no bean of that name exists, a by-type resolution of the
dependency will be attempted if this flag is "true".
Default is "true". Switch this flag to "false" in order to enforce a
by-name lookup in all cases, throwing an exception in case of no name match.
See Also: - resolveDependency.resolveDependency
/**
* Set whether to allow a fallback to a type match if no explicit name has been
* specified. The default name (i.e. the field name or bean property name) will
* still be checked first; if a bean of that name exists, it will be taken.
* However, if no bean of that name exists, a by-type resolution of the
* dependency will be attempted if this flag is "true".
* <p>Default is "true". Switch this flag to "false" in order to enforce a
* by-name lookup in all cases, throwing an exception in case of no name match.
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#resolveDependency
*/
public void setFallbackToDefaultTypeMatch(boolean fallbackToDefaultTypeMatch) {
this.fallbackToDefaultTypeMatch = fallbackToDefaultTypeMatch;
}
Set whether to always use JNDI lookups equivalent to standard Java EE 5 resource
injection, even for name
attributes and default names.
Default is "false": Resource names are used for Spring bean lookups in the containing BeanFactory; only mappedName
attributes point directly into JNDI. Switch this flag to "true" for enforcing Java EE style JNDI lookups in any case, even for name
attributes and default names.
See Also:
/**
* Set whether to always use JNDI lookups equivalent to standard Java EE 5 resource
* injection, <b>even for {@code name} attributes and default names</b>.
* <p>Default is "false": Resource names are used for Spring bean lookups in the
* containing BeanFactory; only {@code mappedName} attributes point directly
* into JNDI. Switch this flag to "true" for enforcing Java EE style JNDI lookups
* in any case, even for {@code name} attributes and default names.
* @see #setJndiFactory
* @see #setResourceFactory
*/
public void setAlwaysUseJndiLookup(boolean alwaysUseJndiLookup) {
this.alwaysUseJndiLookup = alwaysUseJndiLookup;
}
Specify the factory for objects to be injected into @Resource
/ @WebServiceRef
/ @EJB
annotated fields and setter methods, for mappedName
attributes that point directly into JNDI. This factory will also be used if "alwaysUseJndiLookup" is set to "true" in order to enforce JNDI lookups even for name
attributes and default names. The default is a SimpleJndiBeanFactory
for JNDI lookup behavior equivalent to standard Java EE 5 resource injection.
See Also:
/**
* Specify the factory for objects to be injected into {@code @Resource} /
* {@code @WebServiceRef} / {@code @EJB} annotated fields and setter methods,
* <b>for {@code mappedName} attributes that point directly into JNDI</b>.
* This factory will also be used if "alwaysUseJndiLookup" is set to "true" in order
* to enforce JNDI lookups even for {@code name} attributes and default names.
* <p>The default is a {@link org.springframework.jndi.support.SimpleJndiBeanFactory}
* for JNDI lookup behavior equivalent to standard Java EE 5 resource injection.
* @see #setResourceFactory
* @see #setAlwaysUseJndiLookup
*/
public void setJndiFactory(BeanFactory jndiFactory) {
Assert.notNull(jndiFactory, "BeanFactory must not be null");
this.jndiFactory = jndiFactory;
}
Specify the factory for objects to be injected into @Resource
/ @WebServiceRef
/ @EJB
annotated fields and setter methods, for name
attributes and default names.
The default is the BeanFactory that this post-processor is defined in,
if any, looking up resource names as Spring bean names. Specify the resource
factory explicitly for programmatic usage of this post-processor.
Specifying Spring's SimpleJndiBeanFactory
leads to JNDI lookup behavior equivalent to standard Java EE 5 resource injection, even for name
attributes and default names. This is the same behavior that the "alwaysUseJndiLookup" flag enables.
See Also:
/**
* Specify the factory for objects to be injected into {@code @Resource} /
* {@code @WebServiceRef} / {@code @EJB} annotated fields and setter methods,
* <b>for {@code name} attributes and default names</b>.
* <p>The default is the BeanFactory that this post-processor is defined in,
* if any, looking up resource names as Spring bean names. Specify the resource
* factory explicitly for programmatic usage of this post-processor.
* <p>Specifying Spring's {@link org.springframework.jndi.support.SimpleJndiBeanFactory}
* leads to JNDI lookup behavior equivalent to standard Java EE 5 resource injection,
* even for {@code name} attributes and default names. This is the same behavior
* that the "alwaysUseJndiLookup" flag enables.
* @see #setAlwaysUseJndiLookup
*/
public void setResourceFactory(BeanFactory resourceFactory) {
Assert.notNull(resourceFactory, "BeanFactory must not be null");
this.resourceFactory = resourceFactory;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
this.beanFactory = beanFactory;
if (this.resourceFactory == null) {
this.resourceFactory = beanFactory;
}
if (beanFactory instanceof ConfigurableBeanFactory) {
this.embeddedValueResolver = new EmbeddedValueResolver((ConfigurableBeanFactory) beanFactory);
}
}
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
@Override
public void resetBeanDefinition(String beanName) {
this.injectionMetadataCache.remove(beanName);
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) {
return true;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}
@Deprecated
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
return postProcessProperties(pvs, bean, beanName);
}
private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildResourceMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
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 (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static fields");
}
currElements.add(new EjbRefElement(field, field, null));
}
else if (field.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
currElements.add(new ResourceElement(field, field, null));
}
}
});
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
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 (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
}
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 1) {
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
}
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new ResourceElement(method, bridgedMethod, pd));
}
}
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
Obtain a lazily resolving resource proxy for the given name and type, delegating to getResource
on demand once a method call comes in. Params: - element – the descriptor for the annotated field/method
- requestingBeanName – the name of the requesting bean
See Also: Returns: the resource object (never null
) Since: 4.2
/**
* Obtain a lazily resolving resource proxy for the given name and type,
* delegating to {@link #getResource} on demand once a method call comes in.
* @param element the descriptor for the annotated field/method
* @param requestingBeanName the name of the requesting bean
* @return the resource object (never {@code null})
* @since 4.2
* @see #getResource
* @see Lazy
*/
protected Object buildLazyResourceProxy(final LookupElement element, final @Nullable String requestingBeanName) {
TargetSource ts = new TargetSource() {
@Override
public Class<?> getTargetClass() {
return element.lookupType;
}
@Override
public boolean isStatic() {
return false;
}
@Override
public Object getTarget() {
return getResource(element, requestingBeanName);
}
@Override
public void releaseTarget(Object target) {
}
};
ProxyFactory pf = new ProxyFactory();
pf.setTargetSource(ts);
if (element.lookupType.isInterface()) {
pf.addInterface(element.lookupType);
}
ClassLoader classLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() : null);
return pf.getProxy(classLoader);
}
Obtain the resource object for the given name and type.
Params: - element – the descriptor for the annotated field/method
- requestingBeanName – the name of the requesting bean
Throws: - NoSuchBeanDefinitionException – if no corresponding target resource found
Returns: the resource object (never null
)
/**
* Obtain the resource object for the given name and type.
* @param element the descriptor for the annotated field/method
* @param requestingBeanName the name of the requesting bean
* @return the resource object (never {@code null})
* @throws NoSuchBeanDefinitionException if no corresponding target resource found
*/
protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
if (StringUtils.hasLength(element.mappedName)) {
return this.jndiFactory.getBean(element.mappedName, element.lookupType);
}
if (this.alwaysUseJndiLookup) {
return this.jndiFactory.getBean(element.name, element.lookupType);
}
if (this.resourceFactory == null) {
throw new NoSuchBeanDefinitionException(element.lookupType,
"No resource factory configured - specify the 'resourceFactory' property");
}
return autowireResource(this.resourceFactory, element, requestingBeanName);
}
Obtain a resource object for the given name and type through autowiring
based on the given factory.
Params: - factory – the factory to autowire against
- element – the descriptor for the annotated field/method
- requestingBeanName – the name of the requesting bean
Throws: - NoSuchBeanDefinitionException – if no corresponding target resource found
Returns: the resource object (never null
)
/**
* Obtain a resource object for the given name and type through autowiring
* based on the given factory.
* @param factory the factory to autowire against
* @param element the descriptor for the annotated field/method
* @param requestingBeanName the name of the requesting bean
* @return the resource object (never {@code null})
* @throws NoSuchBeanDefinitionException if no corresponding target resource found
*/
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
Object resource;
Set<String> autowiredBeanNames;
String name = element.name;
if (factory instanceof AutowireCapableBeanFactory) {
AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
DependencyDescriptor descriptor = element.getDependencyDescriptor();
if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
autowiredBeanNames = new LinkedHashSet<>();
resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
if (resource == null) {
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
}
}
else {
resource = beanFactory.resolveBeanByName(name, descriptor);
autowiredBeanNames = Collections.singleton(name);
}
}
else {
resource = factory.getBean(name, element.lookupType);
autowiredBeanNames = Collections.singleton(name);
}
if (factory instanceof ConfigurableBeanFactory) {
ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
for (String autowiredBeanName : autowiredBeanNames) {
if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
}
}
}
return resource;
}
Class representing generic injection information about an annotated field
or setter method, supporting @Resource and related annotations.
/**
* Class representing generic injection information about an annotated field
* or setter method, supporting @Resource and related annotations.
*/
protected abstract class LookupElement extends InjectionMetadata.InjectedElement {
protected String name = "";
protected boolean isDefaultName = false;
protected Class<?> lookupType = Object.class;
@Nullable
protected String mappedName;
public LookupElement(Member member, @Nullable PropertyDescriptor pd) {
super(member, pd);
}
Return the resource name for the lookup.
/**
* Return the resource name for the lookup.
*/
public final String getName() {
return this.name;
}
Return the desired type for the lookup.
/**
* Return the desired type for the lookup.
*/
public final Class<?> getLookupType() {
return this.lookupType;
}
Build a DependencyDescriptor for the underlying field/method.
/**
* Build a DependencyDescriptor for the underlying field/method.
*/
public final DependencyDescriptor getDependencyDescriptor() {
if (this.isField) {
return new LookupDependencyDescriptor((Field) this.member, this.lookupType);
}
else {
return new LookupDependencyDescriptor((Method) this.member, this.lookupType);
}
}
}
Class representing injection information about an annotated field
or setter method, supporting the @Resource annotation.
/**
* Class representing injection information about an annotated field
* or setter method, supporting the @Resource annotation.
*/
private class ResourceElement extends LookupElement {
private final boolean lazyLookup;
public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
super(member, pd);
Resource resource = ae.getAnnotation(Resource.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));
}
}
else if (embeddedValueResolver != null) {
resourceName = embeddedValueResolver.resolveStringValue(resourceName);
}
if (Object.class != resourceType) {
checkResourceType(resourceType);
}
else {
// No resource type specified... check field/method.
resourceType = getResourceType();
}
this.name = (resourceName != null ? resourceName : "");
this.lookupType = resourceType;
String lookupValue = resource.lookup();
this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
Lazy lazy = ae.getAnnotation(Lazy.class);
this.lazyLookup = (lazy != null && lazy.value());
}
@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
getResource(this, requestingBeanName));
}
}
Class representing injection information about an annotated field
or setter method, supporting the @WebServiceRef annotation.
/**
* 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.
/**
* Class representing injection information about an annotated field
* or setter method, supporting the @EJB annotation.
*/
private class EjbRefElement extends LookupElement {
private final String beanName;
public EjbRefElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
super(member, pd);
EJB resource = ae.getAnnotation(EJB.class);
String resourceBeanName = resource.beanName();
String resourceName = resource.name();
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));
}
}
Class<?> resourceType = resource.beanInterface();
if (Object.class != resourceType) {
checkResourceType(resourceType);
}
else {
// No resource type specified... check field/method.
resourceType = getResourceType();
}
this.beanName = resourceBeanName;
this.name = resourceName;
this.lookupType = resourceType;
this.mappedName = resource.mappedName();
}
@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
if (StringUtils.hasLength(this.beanName)) {
if (beanFactory != null && beanFactory.containsBean(this.beanName)) {
// Local match found for explicitly specified local bean name.
Object bean = beanFactory.getBean(this.beanName, this.lookupType);
if (requestingBeanName != null && beanFactory instanceof ConfigurableBeanFactory) {
((ConfigurableBeanFactory) beanFactory).registerDependentBean(this.beanName, requestingBeanName);
}
return bean;
}
else if (this.isDefaultName && !StringUtils.hasLength(this.mappedName)) {
throw new NoSuchBeanDefinitionException(this.beanName,
"Cannot resolve 'beanName' in local BeanFactory. Consider specifying a general 'name' value instead.");
}
}
// JNDI name lookup - may still go to a local BeanFactory.
return getResource(this, requestingBeanName);
}
}
Extension of the DependencyDescriptor class,
overriding the dependency type with the specified resource type.
/**
* Extension of the DependencyDescriptor class,
* overriding the dependency type with the specified resource type.
*/
private static class LookupDependencyDescriptor extends DependencyDescriptor {
private final Class<?> lookupType;
public LookupDependencyDescriptor(Field field, Class<?> lookupType) {
super(field, true);
this.lookupType = lookupType;
}
public LookupDependencyDescriptor(Method method, Class<?> lookupType) {
super(new MethodParameter(method, 0), true);
this.lookupType = lookupType;
}
@Override
public Class<?> getDependencyType() {
return this.lookupType;
}
}
}