All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.context.annotation.ConfigurationClass Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2002-2024 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.MethodMetadata;
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 {@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 boolean scanned = false; private final Set importedBy = new LinkedHashSet<>(1); private final Set beanMethods = new LinkedHashSet<>(); private final Map> importedResources = new LinkedHashMap<>(); private final Map importBeanDefinitionRegistrars = new LinkedHashMap<>(); final Set skippedBeanMethods = new HashSet<>(); /** * 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} */ 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 {@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 * @since 3.1.1 */ ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) { this.metadata = metadataReader.getAnnotationMetadata(); this.resource = metadataReader.getResource(); this.importedBy.add(importedBy); } /** * 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 */ 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 {@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 * @since 3.1.1 */ ConfigurationClass(Class clazz, ConfigurationClass importedBy) { this.metadata = AnnotationMetadata.introspect(clazz); this.resource = new DescriptiveResource(clazz.getName()); this.importedBy.add(importedBy); } /** * 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 * @param scanned whether the underlying class has been registered through a scan */ ConfigurationClass(AnnotationMetadata metadata, String beanName, boolean scanned) { Assert.notNull(beanName, "Bean name must not be null"); this.metadata = metadata; this.resource = new DescriptiveResource(metadata.getClassName()); this.beanName = beanName; this.scanned = scanned; } AnnotationMetadata getMetadata() { return this.metadata; } Resource getResource() { return this.resource; } String getSimpleName() { return ClassUtils.getShortName(getMetadata().getClassName()); } void setBeanName(@Nullable String beanName) { this.beanName = beanName; } @Nullable String getBeanName() { return this.beanName; } /** * Return whether this configuration class has been registered through a scan. * @since 6.2 */ boolean isScanned() { return this.scanned; } /** * 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() */ boolean isImported() { return !this.importedBy.isEmpty(); } /** * Merge the imported-by declarations from the given configuration class into this one. * @since 4.0.5 */ 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. * @since 4.0.5 * @see #isImported() */ Set getImportedBy() { return this.importedBy; } void addBeanMethod(BeanMethod method) { this.beanMethods.add(method); } Set getBeanMethods() { return this.beanMethods; } void addImportedResource(String importedResource, Class readerClass) { this.importedResources.put(importedResource, readerClass); } Map> getImportedResources() { return this.importedResources; } void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) { this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata); } Map getImportBeanDefinitionRegistrars() { return this.importBeanDefinitionRegistrars; } @SuppressWarnings("NullAway") void validate(ProblemReporter problemReporter) { Map attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName()); // A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false if (attributes != null && (Boolean) attributes.get("proxyBeanMethods") && this.metadata.isFinal()) { problemReporter.error(new FinalConfigurationProblem()); } for (BeanMethod beanMethod : this.beanMethods) { beanMethod.validate(problemReporter); } // A configuration class may not contain overloaded bean methods unless it declares enforceUniqueMethods=false if (attributes != null && (Boolean) attributes.get("enforceUniqueMethods")) { Map beanMethodsByName = new LinkedHashMap<>(); for (BeanMethod beanMethod : this.beanMethods) { MethodMetadata current = beanMethod.getMetadata(); MethodMetadata existing = beanMethodsByName.put(current.getMethodName(), current); if (existing != null && existing.getDeclaringClassName().equals(current.getDeclaringClassName())) { problemReporter.error(new BeanMethodOverloadingProblem(existing.getMethodName())); } } } } @Override public boolean equals(@Nullable Object other) { return (this == other || (other instanceof ConfigurationClass that && getMetadata().getClassName().equals(that.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. */ private class FinalConfigurationProblem extends Problem { FinalConfigurationProblem() { super(String.format("@Configuration class '%s' may not be final. Remove the final modifier to continue.", getSimpleName()), new Location(getResource(), getMetadata())); } } /** * Configuration classes are not allowed to contain overloaded bean methods * by default (as of 6.0). */ private class BeanMethodOverloadingProblem extends Problem { BeanMethodOverloadingProblem(String methodName) { super(String.format("@Configuration class '%s' contains overloaded @Bean methods with name '%s'. Use " + "unique method names for separate bean definitions (with individual conditions etc) " + "or switch '@Configuration.enforceUniqueMethods' to 'false'.", getSimpleName(), methodName), new Location(getResource(), getMetadata())); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy