/*
 * 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())); } } }