/*
* 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
*
* 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 org.springframework.context.annotation;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.core.io.DescriptiveResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
Represents a user-defined @Configuration
class. Includes a set of Bean
methods, including all such methods defined in the ancestry of the class, in a 'flattened-out' manner. Author: Chris Beams, Juergen Hoeller, Phillip Webb See Also: Since: 3.0
/**
* Represents a user-defined {@link Configuration @Configuration} class.
* Includes a set of {@link Bean} methods, including all such methods
* defined in the ancestry of the class, in a 'flattened-out' manner.
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Phillip Webb
* @since 3.0
* @see BeanMethod
* @see ConfigurationClassParser
*/
final class ConfigurationClass {
private final AnnotationMetadata metadata;
private final Resource resource;
@Nullable
private String beanName;
private final Set<ConfigurationClass> importedBy = new LinkedHashSet<>(1);
private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
new LinkedHashMap<>();
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
new LinkedHashMap<>();
final Set<String> skippedBeanMethods = new HashSet<>();
Create a new ConfigurationClass
with the given name. Params: - metadataReader – reader used to parse the underlying
Class
- beanName – must not be
null
See Also:
/**
* Create a new {@link ConfigurationClass} with the given name.
* @param metadataReader reader used to parse the underlying {@link Class}
* @param beanName must not be {@code null}
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
public ConfigurationClass(MetadataReader metadataReader, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.beanName = beanName;
}
Create a new ConfigurationClass
representing a class that was imported using the Import
annotation or automatically processed as a nested configuration class (if importedBy is not null
). Params: - metadataReader – reader used to parse the underlying
Class
- importedBy – the configuration class importing this one or
null
Since: 3.1.1
/**
* Create a new {@link ConfigurationClass} representing a class that was imported
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if importedBy is not {@code null}).
* @param metadataReader reader used to parse the underlying {@link Class}
* @param importedBy the configuration class importing this one or {@code null}
* @since 3.1.1
*/
public ConfigurationClass(MetadataReader metadataReader, @Nullable ConfigurationClass importedBy) {
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.importedBy.add(importedBy);
}
Create a new ConfigurationClass
with the given name. Params: - clazz – the underlying
Class
to represent - beanName – name of the
@Configuration
class bean
See Also:
/**
* Create a new {@link ConfigurationClass} with the given name.
* @param clazz the underlying {@link Class} to represent
* @param beanName name of the {@code @Configuration} class bean
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
public ConfigurationClass(Class<?> clazz, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
this.metadata = AnnotationMetadata.introspect(clazz);
this.resource = new DescriptiveResource(clazz.getName());
this.beanName = beanName;
}
Create a new ConfigurationClass
representing a class that was imported using the Import
annotation or automatically processed as a nested configuration class (if imported is true
). Params: - clazz – the underlying
Class
to represent - importedBy – the configuration class importing this one (or
null
)
Since: 3.1.1
/**
* Create a new {@link ConfigurationClass} representing a class that was imported
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if imported is {@code true}).
* @param clazz the underlying {@link Class} to represent
* @param importedBy the configuration class importing this one (or {@code null})
* @since 3.1.1
*/
public ConfigurationClass(Class<?> clazz, @Nullable ConfigurationClass importedBy) {
this.metadata = AnnotationMetadata.introspect(clazz);
this.resource = new DescriptiveResource(clazz.getName());
this.importedBy.add(importedBy);
}
Create a new ConfigurationClass
with the given name. Params: - metadata – the metadata for the underlying class to represent
- beanName – name of the
@Configuration
class bean
See Also:
/**
* Create a new {@link ConfigurationClass} with the given name.
* @param metadata the metadata for the underlying class to represent
* @param beanName name of the {@code @Configuration} class bean
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
public ConfigurationClass(AnnotationMetadata metadata, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
this.metadata = metadata;
this.resource = new DescriptiveResource(metadata.getClassName());
this.beanName = beanName;
}
public AnnotationMetadata getMetadata() {
return this.metadata;
}
public Resource getResource() {
return this.resource;
}
public String getSimpleName() {
return ClassUtils.getShortName(getMetadata().getClassName());
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@Nullable
public String getBeanName() {
return this.beanName;
}
Return whether this configuration class was registered via @Import
or automatically registered due to being nested within another configuration class. See Also: Since: 3.1.1
/**
* Return whether this configuration class was registered via @{@link Import} or
* automatically registered due to being nested within another configuration class.
* @since 3.1.1
* @see #getImportedBy()
*/
public boolean isImported() {
return !this.importedBy.isEmpty();
}
Merge the imported-by declarations from the given configuration class into this one.
Since: 4.0.5
/**
* Merge the imported-by declarations from the given configuration class into this one.
* @since 4.0.5
*/
public void mergeImportedBy(ConfigurationClass otherConfigClass) {
this.importedBy.addAll(otherConfigClass.importedBy);
}
Return the configuration classes that imported this class,
or an empty Set if this configuration was not imported.
See Also: Since: 4.0.5
/**
* Return the configuration classes that imported this class,
* or an empty Set if this configuration was not imported.
* @since 4.0.5
* @see #isImported()
*/
public Set<ConfigurationClass> getImportedBy() {
return this.importedBy;
}
public void addBeanMethod(BeanMethod method) {
this.beanMethods.add(method);
}
public Set<BeanMethod> getBeanMethods() {
return this.beanMethods;
}
public void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
this.importedResources.put(importedResource, readerClass);
}
public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}
public Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> getImportBeanDefinitionRegistrars() {
return this.importBeanDefinitionRegistrars;
}
public Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
return this.importedResources;
}
public void validate(ProblemReporter problemReporter) {
// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods")) {
if (this.metadata.isFinal()) {
problemReporter.error(new FinalConfigurationProblem());
}
for (BeanMethod beanMethod : this.beanMethods) {
beanMethod.validate(problemReporter);
}
}
}
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof ConfigurationClass &&
getMetadata().getClassName().equals(((ConfigurationClass) other).getMetadata().getClassName())));
}
@Override
public int hashCode() {
return getMetadata().getClassName().hashCode();
}
@Override
public String toString() {
return "ConfigurationClass: beanName '" + this.beanName + "', " + this.resource;
}
Configuration classes must be non-final to accommodate CGLIB subclassing.
/**
* Configuration classes must be non-final to accommodate CGLIB subclassing.
*/
private class FinalConfigurationProblem extends Problem {
public FinalConfigurationProblem() {
super(String.format("@Configuration class '%s' may not be final. Remove the final modifier to continue.",
getSimpleName()), new Location(getResource(), getMetadata()));
}
}
}