Spring Framework Core - IoC Container and Dependency Injection
—
Spring Core provides a comprehensive environment abstraction that manages application properties, profiles, and configuration from various sources. This system enables flexible configuration management across different deployment environments.
The Environment interface provides the main abstraction for the runtime environment and application properties.
Environment Interface
public interface Environment extends PropertyResolver {
String[] getActiveProfiles();
String[] getDefaultProfiles();
@Deprecated
boolean acceptsProfiles(String... profiles);
boolean acceptsProfiles(Profiles profiles);
}
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
void setActiveProfiles(String... profiles);
void addActiveProfile(String profile);
void setDefaultProfiles(String... profiles);
MutablePropertySources getPropertySources();
Map<String, Object> getSystemProperties();
Map<String, Object> getSystemEnvironment();
void merge(ConfigurableEnvironment parent);
}Standard Environment Implementation
public class StandardEnvironment extends AbstractEnvironment {
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
public StandardEnvironment();
@Override
protected void customizePropertySources(MutablePropertySources propertySources);
}
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
protected final Log logger = LogFactory.getLog(getClass());
private final Set<String> activeProfiles = new LinkedHashSet<>();
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
private final MutablePropertySources propertySources = new MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
public AbstractEnvironment();
protected void customizePropertySources(MutablePropertySources propertySources);
protected Set<String> getReservedDefaultProfiles();
protected boolean isProfileActive(String profile);
@Override
public String[] getActiveProfiles();
@Override
public void setActiveProfiles(String... profiles);
@Override
public void addActiveProfile(String profile);
@Override
public String[] getDefaultProfiles();
@Override
public void setDefaultProfiles(String... profiles);
@Override
public boolean acceptsProfiles(Profiles profiles);
@Override
public MutablePropertySources getPropertySources();
}Usage Examples
// Basic environment usage
Environment env = new StandardEnvironment();
// Check active profiles
String[] activeProfiles = env.getActiveProfiles();
boolean isProduction = env.acceptsProfiles(Profiles.of("production"));
boolean isDevelopment = env.acceptsProfiles(Profiles.of("development"));
// Complex profile expressions
boolean isCloudOrDocker = env.acceptsProfiles(Profiles.of("cloud | docker"));
boolean isNotTest = env.acceptsProfiles(Profiles.of("!test"));
// Configure environment programmatically
ConfigurableEnvironment configurableEnv = new StandardEnvironment();
configurableEnv.setActiveProfiles("production", "cloud");
configurableEnv.addActiveProfile("monitoring");
// Access properties
String serverPort = env.getProperty("server.port", "8080");
Integer maxConnections = env.getProperty("server.max-connections", Integer.class, 100);The property resolver system provides flexible property access with placeholder resolution and type conversion.
PropertyResolver Interface
public interface PropertyResolver {
boolean containsProperty(String key);
String getProperty(String key);
String getProperty(String key, String defaultValue);
<T> T getProperty(String key, Class<T> targetType);
<T> T getProperty(String key, Class<T> targetType, T defaultValue);
String getRequiredProperty(String key) throws IllegalStateException;
<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
String resolvePlaceholders(String text);
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}
public interface ConfigurablePropertyResolver extends PropertyResolver {
ConfigurableConversionService getConversionService();
void setConversionService(ConfigurableConversionService conversionService);
void setPlaceholderPrefix(String placeholderPrefix);
void setPlaceholderSuffix(String placeholderSuffix);
void setValueSeparator(String valueSeparator);
void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);
void setRequiredProperties(String... requiredProperties);
void validateRequiredProperties() throws MissingRequiredPropertiesException;
}PropertySourcesPropertyResolver
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
public PropertySourcesPropertyResolver(PropertySources propertySources);
@Override
public boolean containsProperty(String key);
@Override
public String getProperty(String key);
@Override
public <T> T getProperty(String key, Class<T> targetType);
protected <T> T getProperty(String key, Class<T> targetType, boolean resolveNestedPlaceholders);
protected PropertySource<?> findPropertySource(String key);
}Usage Examples
// Property resolution with type conversion
PropertyResolver resolver = new PropertySourcesPropertyResolver(propertySources);
// Basic property access
String appName = resolver.getProperty("app.name", "MyApp");
Integer port = resolver.getProperty("server.port", Integer.class, 8080);
Boolean enableSsl = resolver.getProperty("security.ssl.enabled", Boolean.class, false);
// Required properties (throw exception if missing)
String dbUrl = resolver.getRequiredProperty("database.url");
Integer maxPoolSize = resolver.getRequiredProperty("database.pool.max-size", Integer.class);
// Placeholder resolution
String template = "App ${app.name} running on port ${server.port}";
String resolved = resolver.resolvePlaceholders(template);
// Result: "App MyApp running on port 8080"
// Configure resolver
ConfigurablePropertyResolver configurable = new PropertySourcesPropertyResolver(propertySources);
configurable.setPlaceholderPrefix("#{");
configurable.setPlaceholderSuffix("}");
configurable.setValueSeparator(":");
configurable.setIgnoreUnresolvableNestedPlaceholders(true);
// Set required properties for validation
configurable.setRequiredProperties("database.url", "app.secret");
try {
configurable.validateRequiredProperties();
} catch (MissingRequiredPropertiesException ex) {
// Handle missing required properties
}Property sources provide the actual property values from various sources like files, environment variables, and system properties.
PropertySource Classes
public abstract class PropertySource<T> {
protected final String name;
protected final T source;
public PropertySource(String name, T source);
public PropertySource(String name);
public String getName();
public T getSource();
public boolean containsProperty(String name);
public abstract Object getProperty(String name);
@Override
public boolean equals(Object other);
@Override
public int hashCode();
@Override
public String toString();
public static PropertySource<?> named(String name);
}
public class MapPropertySource extends EnumerablePropertySource<Map<String, Object>> {
public MapPropertySource(String name, Map<String, Object> source);
@Override
public Object getProperty(String name);
@Override
public boolean containsProperty(String name);
@Override
public String[] getPropertyNames();
}
public class SystemEnvironmentPropertySource extends MapPropertySource {
public SystemEnvironmentPropertySource(String name, Map<String, Object> source);
@Override
public boolean containsProperty(String name);
@Override
public Object getProperty(String name);
protected boolean isSecurityManagerPresent();
protected String resolvePropertyName(String name);
}
public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
public EnumerablePropertySource(String name, T source);
public EnumerablePropertySource(String name);
@Override
public boolean containsProperty(String name);
public abstract String[] getPropertyNames();
}PropertySources Container
public interface PropertySources extends Iterable<PropertySource<?>> {
default Stream<PropertySource<?>> stream();
boolean contains(String name);
PropertySource<?> get(String name);
}
public class MutablePropertySources implements PropertySources {
public MutablePropertySources();
public MutablePropertySources(PropertySources propertySources);
@Override
public Iterator<PropertySource<?>> iterator();
@Override
public Spliterator<PropertySource<?>> spliterator();
@Override
public Stream<PropertySource<?>> stream();
@Override
public boolean contains(String name);
@Override
public PropertySource<?> get(String name);
public void addFirst(PropertySource<?> propertySource);
public void addLast(PropertySource<?> propertySource);
public void addBefore(String relativePropertySourceName, PropertySource<?> propertySource);
public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource);
public int precedenceOf(PropertySource<?> propertySource);
public PropertySource<?> remove(String name);
public void replace(String name, PropertySource<?> propertySource);
public int size();
@Override
public String toString();
}Usage Examples
// Create property sources from various sources
Map<String, Object> appProps = new HashMap<>();
appProps.put("app.name", "MyApplication");
appProps.put("app.version", "1.0.0");
PropertySource<?> appPropertySource = new MapPropertySource("applicationProperties", appProps);
// System environment and properties
PropertySource<?> systemEnvSource = new SystemEnvironmentPropertySource(
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
System.getenv()
);
PropertySource<?> systemPropsSource = new PropertiesPropertySource(
StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME,
System.getProperties()
);
// Manage property source precedence
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addFirst(appPropertySource); // Highest priority
propertySources.addLast(systemPropsSource); // Lowest priority
propertySources.addAfter("applicationProperties", systemEnvSource);
// Access through property resolver
PropertyResolver resolver = new PropertySourcesPropertyResolver(propertySources);
String appName = resolver.getProperty("app.name"); // "MyApplication"
// Property source operations
boolean hasAppProps = propertySources.contains("applicationProperties");
PropertySource<?> appSource = propertySources.get("applicationProperties");
int precedence = propertySources.precedenceOf(appSource); // 0 (highest)
// Replace property source
Map<String, Object> newProps = new HashMap<>(appProps);
newProps.put("app.version", "2.0.0");
PropertySource<?> updatedSource = new MapPropertySource("applicationProperties", newProps);
propertySources.replace("applicationProperties", updatedSource);Spring provides sophisticated profile support for environment-specific configuration.
Profiles Interface
public interface Profiles {
boolean matches(Predicate<String> activeProfiles);
static Profiles of(String... profiles);
}
class ProfilesParser {
static Profiles parse(String... expressions);
}Profile Expression Language
// Profile expressions support logical operators
Profiles production = Profiles.of("production");
Profiles cloudOrDocker = Profiles.of("cloud | docker");
Profiles notTest = Profiles.of("!test");
Profiles complexExpr = Profiles.of("(cloud | docker) & !test");Usage Examples
// Profile-based configuration
ConfigurableEnvironment env = new StandardEnvironment();
// Set profiles programmatically
env.setActiveProfiles("development", "debug");
env.addActiveProfile("local");
// Or via system properties
System.setProperty("spring.profiles.active", "production,cloud");
// Check profile conditions
boolean isDevEnvironment = env.acceptsProfiles(Profiles.of("development"));
boolean isCloudDeployment = env.acceptsProfiles(Profiles.of("cloud | docker"));
boolean isNotTesting = env.acceptsProfiles(Profiles.of("!test"));
// Complex profile logic in configuration
@Configuration
@Profile("production & !development")
public class ProductionConfig {
@Bean
@Profile("cloud | docker")
public DataSource cloudDataSource() {
// Cloud-specific datasource
}
@Bean
@Profile("!cloud")
public DataSource localDataSource() {
// Local datasource
}
}
// Conditional bean creation based on profiles
@Component
public class EnvironmentAwareService {
@Autowired
public EnvironmentAwareService(Environment env) {
if (env.acceptsProfiles(Profiles.of("development"))) {
// Development-specific initialization
} else if (env.acceptsProfiles(Profiles.of("production"))) {
// Production-specific initialization
}
}
}PropertyPlaceholderHelper
public class PropertyPlaceholderHelper {
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix);
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix,
String valueSeparator, boolean ignoreUnresolvablePlaceholders);
public String replacePlaceholders(String value, Properties properties);
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver);
protected String parseStringValue(String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders);
@FunctionalInterface
public interface PlaceholderResolver {
String resolvePlaceholder(String placeholderName);
}
}Usage Examples
// Custom placeholder resolution
PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}", ":", true);
// Simple properties-based resolution
Properties props = new Properties();
props.setProperty("server.host", "localhost");
props.setProperty("server.port", "8080");
String template = "Server running at ${server.host}:${server.port}";
String resolved = helper.replacePlaceholders(template, props);
// Result: "Server running at localhost:8080"
// Custom placeholder resolver
PlaceholderResolver resolver = placeholderName -> {
switch (placeholderName) {
case "timestamp": return String.valueOf(System.currentTimeMillis());
case "random": return String.valueOf(Math.random());
default: return null;
}
};
String dynamicTemplate = "Generated at ${timestamp} with random ${random}";
String dynamicResolved = helper.replacePlaceholders(dynamicTemplate, resolver);
// Default value support
String withDefaults = "Host: ${server.host:localhost}, Port: ${server.port:8080}";
String resolvedWithDefaults = helper.replacePlaceholders(withDefaults, props);ProfilesParser and Profile Expression Support
final class Profiles {
private final String[] expressions;
private Profiles(String[] expressions);
public static Profiles of(String... profiles);
public boolean matches(Predicate<String> activeProfiles);
private static class ParsedProfiles implements Profiles {
private final Set<ProfileExpression> expressions;
ParsedProfiles(String... expressions);
@Override
public boolean matches(Predicate<String> activeProfiles);
}
}Advanced Environment Configuration
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";
@Override
protected void customizePropertySources(MutablePropertySources propertySources);
@Override
public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig);
}Usage Examples
// Advanced profile expressions
Environment env = new StandardEnvironment();
// Logical AND
boolean isProdAndCloud = env.acceptsProfiles(Profiles.of("production & cloud"));
// Logical OR
boolean isDevOrTest = env.acceptsProfiles(Profiles.of("development | test"));
// Negation
boolean isNotProduction = env.acceptsProfiles(Profiles.of("!production"));
// Complex expressions with grouping
boolean complexCondition = env.acceptsProfiles(Profiles.of("(cloud | docker) & !test & production"));
// Multiple profile checks
String[] activeProfiles = env.getActiveProfiles();
boolean hasAnyActiveProfile = Arrays.stream(activeProfiles).anyMatch(profile ->
env.acceptsProfiles(Profiles.of(profile))
);
// Environment merging for hierarchical configurations
ConfigurableEnvironment child = new StandardEnvironment();
child.setActiveProfiles("development");
ConfigurableEnvironment parent = new StandardEnvironment();
parent.setActiveProfiles("base");
parent.getPropertySources().addFirst(new MapPropertySource("parentProps",
Map.of("parent.property", "parent-value")));
child.merge(parent);
// Child now has access to parent's property sources and profilesThis environment and configuration system provides the foundation for Spring's flexible, profile-aware configuration management, enabling applications to adapt their behavior based on deployment context while maintaining clean separation of concerns.
Install with Tessl CLI
npx tessl i tessl/maven-org-springframework--spring-core