Spring IOC- Container based on XML configuration
We first analyze the AbstractXmlApplicationContext this container loading process.
Dad is AbstractRefreshableApplicationContext AbstractXmlApplicationContext.
It was dad AbstractApplicationContext of one of the two sons.
1. Start
Start with a diagram:
Using the ClassPathXmlApplicationContext demo, and then there’s brother FileSystemXmlApplicationContext next to it, both of them are from the XML configuration file loading configuration
There is also a AnnotationConfigApplicationContext besides these two
2. Source code analysis
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
// Array of configuration files
private Resource[] configResources;
// Specify the parent container of ApplicationContext
public ClassPathXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true.null);
}
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true.null);
}
public ClassPathXmlApplicationContext(String[] configLocations, @Nullable ApplicationContext parent)
throws BeansException {
this(configLocations, true, parent);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
// According to the path provided, processing into an array of configuration files (semicolons, commas, Spaces, tabs, newlines)
setConfigLocations(configLocations);
if(refresh) { refresh(); }}}Copy the code
As you can see, the ClassPathXmlApplicationContext provides a series of construction method, main is to specify the location of the configuration files, and to set a parent container, and call the refresh () method, setConfigLocations () this is very simple, The main one, it seems, is in the Refresh method
setConfigLocations()
So what’s going on in the setConfigLocations() method
public void setConfigLocations(String... locations) {
if(locations ! =null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
/ / to look down
this.configLocations[i] = resolvePath(locations[i]).trim(); }}else {
this.configLocations = null; }}protected String resolvePath(String path) {
return getEnironment().resolveRequiredPlaceholders(path);
}
Copy the code
The setConfigLocations() method does two main things: Create environment object ConfigurableEnvironment and deal with the incoming string in the ClassPathXmlApplicationContext structure of placeholder, getEnvironment here () goes to create environment variables related to the operation
GetEnvironment () Obtains environment variables
public ConfigurableEnvironment getEnvironment(a) {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
Copy the code
In the getEnvironment() method above, we see a ConfigurableEnvironment class:
There are two important parts of this interface. One is to set up the Spring environment, which is the Spring. Profile configuration we often use. The other is system resource Property
Then look at the createEnvironment() method in getEnvironment(), which creates a StandardEnvironment:
public class StandardEnvironment extends AbstractEnvironment {
/** System environment property source name: {@value} System properties eg: system.getProperty ("java.home"); * /
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value} JVM attribute --name=ranger OR -Dname= Ranger */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
/** * Customize the set of property sources with those appropriate for any standard * Java environment: * Define the resource set using any suitable standard Java environment * <ul> * <li>{@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME}
* <li>{@value#SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME} * </ul> * JVM Properties have higher priority than system Properties * <p>Properties present in {@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME} will
* take precedence over those in {@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}.
* @seeAbstractEnvironment#customizePropertySources(MutablePropertySources) * According to the parent class definition of this method: AbstractEnvironment#customizePropertySources Subclasses can override this method to add a custom PropertySource as follows: Public Class Level1Environment extends AbstractEnvironment {@ Override protected void customizePropertySources(MutablePropertySources propertySources) { super.customizePropertySources(propertySources); // no-op from base class propertySources.addLast(new PropertySourceA(...) ); propertySources.addLast(new PropertySourceB(...) ); }} You can also add A custom PropertySource within A Level1Environment subclass, and the PropertySource has A priority, such as > A > B * in the above example@see #getSystemProperties()
* @see #getSystemEnvironment()
*/
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(newSystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); }}Copy the code
The customizePropertySources method in this class adds Java process variables and system environment variables to the resource list. It can be seen that an Environment is a collection of resources containing a set of PropertySource, which is the property of the resource
Once again return to resolvePath () method, has been tracking this method, finally to the org. Springframework. Util. PropertyPlaceholderHelper. ParseStringValue () method, This method basically handles all placeholders that use ${}
protected String parseStringValue( String value, PlaceholderResolver placeholderResolver, Set
visitedPlaceholders)
{
StringBuilder result = new StringBuilder(value);
int startIndex = value.indexOf(this.placeholderPrefix);
while(startIndex ! = -1) {
int endIndex = findPlaceholderEndIndex(result, startIndex);
if(endIndex ! = -1) {
String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
String originalPlaceholder = placeholder;
if(! visitedPlaceholders.add(originalPlaceholder)) {throw new IllegalArgumentException(
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
}
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// Now obtain the value for the fully resolved key...
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
if (propVal == null && this.valueSeparator ! =null) {
int separatorIndex = placeholder.indexOf(this.valueSeparator);
if(separatorIndex ! = -1) {
String actualPlaceholder = placeholder.substring(0, separatorIndex);
String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
if (propVal == null) { propVal = defaultValue; }}}if(propVal ! =null) {
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
if (logger.isTraceEnabled()) {
logger.trace("Resolved placeholder '" + placeholder + "'");
}
startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
}
else if (this.ignoreUnresolvablePlaceholders) {
// Proceed with unprocessed value.
startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
}
else {
throw new IllegalArgumentException("Could not resolve placeholder '" +
placeholder + "'" + " in value \"" + value + "\" ");
}
visitedPlaceholders.remove(originalPlaceholder);
}
else {
startIndex = -1; }}return result.toString();
}
Copy the code
The PropertyResolver will be resolved later
refresh()
This method is very long, and will be introduced in a long way. Let’s look at what it does:
public void refresh(a) throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...resetCommonCaches(); }}}Copy the code
Next step by step:
- Synchronized in order to avoid the conflict caused by restarting containers before they are started in multi-threaded environment
- The prepareRefresh() method does some preparatory work, recording the container’s startup time, marking up the startup status, and so on
protected void prepareRefresh(a) {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize the load configuration file method, which is not implemented specifically, an extension point left to the user
initPropertySources();
// Check environment variables
getEnvironment().validateRequiredProperties();
this.earlyApplicationEvents = new LinkedHashSet<>();
}
Copy the code
One method for validateRequiredProperties (), the method is to check whether all the required environment variables is empty, if is empty, stop start, and then throw an exception
- The obtainFreshBeanFactory() method is responsible for the BeanFactory initialization, Bean loading, and registration events
protected ConfigurableListableBeanFactory obtainFreshBeanFactory(a) {
/ / core
refreshBeanFactory();
// Returns the created BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ":" + beanFactory);
}
return beanFactory;
}
Copy the code
- refreshBeanFactory():
/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. */
@Override
protected final void refreshBeanFactory(a) throws BeansException {
// Determine if the current ApplicationContext has a BeanFactory. If so, destroy all beans and close the BeanFactory
// Note that an application can have more than one BeanFactory. This is to determine whether the current ApplicationContext has a BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
/ / initialize DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// Set two configuration properties of BeanFactory: whether Bean overrides are allowed and whether circular references are allowed
customizeBeanFactory(beanFactory);
// Load the Bean into the BeanFactory
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory; }}catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for "+ getDisplayName(), ex); }}Copy the code
Initialize a DefaultListableBeanFactory here, look at this class inheritance
This is a very big guy
- loadBeanDefinitions()
BeanDefinition first, we know that a BeanFactory is a Bean container, and a BeanDefinition is a form of Bean (it contains properties related to the class the Bean points to, whether it is a singleton, whether it is lazy to load, Bean dependencies, etc.). BeanFactory is the BeanDefinition that’s saved.
BeanDefinition interface definition:
public interface BeanDefinition extends AttributeAccessor.BeanMetadataElement {
// The Bean lifecycle is provided in sington and prototype by default. In WebApplicationContext there are request, Session, globalSession, Application, WebSocket, etc
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
// Set the parent Bean
void setParentName(String parentName);
// Get the parent Bean
String getParentName(a);
// Set the class name of the Bean
void setBeanClassName(String beanClassName);
// Get the class name of the Bean
String getBeanClassName(a);
// Set the scope of the bean
void setScope(String scope);
String getScope(a);
// Set lazy loading
void setLazyInit(boolean lazyInit);
boolean isLazyInit(a);
// Set all the beans that the Bean depends on
void setDependsOn(String... dependsOn);
// Returns all dependencies for the Bean
String[] getDependsOn();
// Sets whether this Bean can be injected into other beans
void setAutowireCandidate(boolean autowireCandidate);
// Whether this Bean can be injected into other beans
boolean isAutowireCandidate(a);
// Multiple implementations of the same interface. If no name is specified, Spring will preferentially select beans with primary set to true
void setPrimary(boolean primary);
// Is primary
boolean isPrimary(a);
// Specify the factory name
void setFactoryBeanName(String factoryBeanName);
// Get the factory name
String getFactoryBeanName(a);
// Specify the name of the factory method in the factory class
void setFactoryMethodName(String factoryMethodName);
// Get the factory method name in the factory class
String getFactoryMethodName(a);
// Constructor arguments
ConstructorArgumentValues getConstructorArgumentValues(a);
// The property value in the Bean, which will be described later when injecting the property value into the Bean
MutablePropertyValues getPropertyValues(a);
/ / if the singleton
boolean isSingleton(a);
/ / whether the prototype
boolean isPrototype(a);
// If the Bean is set to abstract, it cannot be instantiated and is often used as a parent Bean for inheritance
boolean isAbstract(a);
int getRole(a);
String getDescription(a);
String getResourceDescription(a);
BeanDefinition getOriginatingBeanDefinition(a);
}
Copy the code
The loadBeanDefinitions() method reads the configuration file and loads each BeanDefinition into the BeanFactory
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Instantiate XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Initialize BeanDefinitionReader
initBeanDefinitionReader(beanDefinitionReader);
// Continue reading
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if(configResources ! =null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if(configLocations ! =null) { reader.loadBeanDefinitions(configLocations); }}Copy the code
Then call the loadBeanDefinitions() method of XmlBeanDefinitionReader
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
// Process all configuration files
for (Resource resource : resources) {
// Keep reading
counter += loadBeanDefinitions(resource);
}
// Finally returns the number of all beanDefinitions loaded
return counter;
}
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
try {
// Convert the configuration file to a Resource object
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// Continue reading
int loadCount = loadBeanDefinitions(resources);
if(actualResources ! =null) {
for(Resource resource : resources) { actualResources.add(resource); }}if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex); }}else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if(actualResources ! =null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
returnloadCount; }}Copy the code
XmlBeanDefinitionReader is mentioned several times. The main interface for this class is BeanDefinitionReader
How does this class read a configuration file, create a beanDefinitionMap, and then register it with the container beanDefinitionMap, which we’ll talk about later, if we look too far, we’ll never get back
- prepareBeanFactory(ConfigurableListableBeanFactory beanFactory)
Now back to the main thread of the refresh() method, and now refresh has gone to prepare Something for the BeanFactory, which, by its name, is actually the classloader for the BeanFactory, Add BeanPostProcessor, and then manually register a few special beans
/**
* Configure the factory's standard context characteristics,
* such as the context's ClassLoader and post-processors.
* @param beanFactory the BeanFactory to configure
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
// Set the classloader for the current BeanFacoty
beanFactory.setBeanClassLoader(getClassLoader());
/ / set BeanExpressionResolver
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
/ / register PropertyEditor
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
// This processor is responsible for the callback when all beans that implement the Aware interface are initialized
// For example,bean retrieves ApplicationContext and Implement ApplicationContextAware
// Of course, it also handles beans that implement EnvironmentAware, ResourceLoaderAware, and so on
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// The following lines mean that if a bean depends on the implementation class of any of the following interfaces, ignore them during autowiring
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
// The following lines assign values to specific beans. If any beans depend on the following, the corresponding values will be injected
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
// Register event listeners
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// Register a BeanPostProcessor if there is a bean named loadTimeWeaver
// LoadTimeWeaver is a LoadTimeWeaver
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
// Register the environment bean
if(! beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); }// System properties
if(! beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); }/ / the JVM properties
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
Copy the code
- postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
This is an extension provided in Spring that calls the class and leaves it up to the subclasses to extend, and the subclasses can add some special BeanFactoryPostProcessor implementation classes or do something at this point. The method definition is as follows
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for registering special
* BeanPostProcessors etc in certain ApplicationContext implementations.
* @param beanFactory the bean factory used by the application context
*/
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}Copy the code
- invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)
Here is the offer of an extension Spring, if you have the bean implementation Spring BeanFactoryPostProcessor, so the container after initialization, Spring will be responsible for the inside of the call postProcessBeanFactory method of this interface are defined as follows:
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanfactory) throws BeansException;
}
Copy the code
- registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory);
Here, again, is an extension point that registers the bean handlers that intercept bean creation. Each bean creation calls all beanPostProcessors to register the implementation classes of BeanPostProcessor. BeanFactoryPostProcessor This interface has two methods: PostProcessBeforeInitialization and postProcessAfterInitialization respectively in Bean initialization before and after the initialization enforced
- initMessageSource()
Initializes the MessageSource of the current ApplicationContext for internationalization
- initApplicationEventMulticaster()
Initialize the current radio apparatus, the BeanFactory events will register a SimpleApplicationEventMulticaster singleton, can call ApplicationEventMulticaster multicastEvent ways to trigger an event, ApplicationEventMulticaster invokes the corresponding ApplicationListener, behind to speak Spring event mechanism in detail again
- onRefresh()
Here’s another extension point for subclasses,
/**
* Template method which can be overridden to add context-specific refresh work.
* Called on initialization of special beans, before instantiation of singletons.
* <p>This implementation is empty.
* @throws BeansException in case of errors
* @see #refresh()
*/
protected void onRefresh(a) throws BeansException {
// For subclasses: do nothing by default.
}
Copy the code
- registerListeners();
Detect the Listener and register with the container
protected void registerListeners(a) {
// Add some listeners for manual set first
for(ApplicationListener<? > listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); }// Get the name of the listener and set it to the broadcaster
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true.false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish if there are early application events
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if(earlyEventsToProcess ! =null) {
for(ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); }}}Copy the code
- finishBeanFactoryInitialization()
Having gone a long way in the refresh method before, but still not completing the bean initialization and dependency injection, Spring feels it’s time to initialize the singleton bean that isn’t lazily loaded
It’s going to be a tough ride here again
/** * Finish the initialization of this context's bean factory, * initializing all remaining singleton beans. */
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
// Initialize ConversionService for the current BeanFactory
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if(! beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); }// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false.false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
Copy the code
-
ConversionService this class is related to type conversion. One of the scenarios is to convert parameters passed from the front end to the controller method format
-
EmbeddedValueResolver The EmbeddedValueResolver provides an easy way to read configuration file properties
@Component
public class PropertiesUtil implements EmbeddedValueResolverAware {
private StringValueResolver resolver;
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
/** * get the attribute by passing the attribute name */
public String getPropertiesValue(String key) {
StringBuilder name = new StringBuilder("${").append(key).append("}");
returnresolver.resolveStringValue(name.toString()); }}Copy the code
- beanFactory.preInstantiateSingletons()
This method is important
public void preInstantiateSingletons(a) throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}
// this.beandefinitionNames saves all beanNames
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
for (String beanName : beanNames) {
// Return a merged RootBeanDefinition, traversing the parent bean definition,if the specified bean corresponds to a child bean definition.
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// Not abstract, singleton, and not lazily loaded
if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {/ / processing FactoryBean
if (isFactoryBean(beanName)) {
// Prefix beanName with an ampersand
finalFactoryBean<? > factory = (FactoryBean<? >) getBean(FACTORY_BEAN_PREFIX + beanName);// Determine if the current FactoryBean is an implementation of SmartFactoryBean
boolean isEagerInit;
if(System.getSecurityManager() ! =null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run(a) {
return((SmartFactoryBean<? >) factory).isEagerInit(); } }, getAccessControlContext()); }else {
isEagerInit = (factory instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }if(isEagerInit) { getBean(beanName); }}else {
// Non-factoryBeans are initialized directly using this methodgetBean(beanName); }}}/ / if the bean implementation SmartInitializingSingleton interface, so get back here
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if(System.getSecurityManager() ! =null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run(a) {
smartSingleton.afterSingletonsInstantiated();
return null;
}
}, getAccessControlContext());
}
else{ smartSingleton.afterSingletonsInstantiated(); }}}}Copy the code
As you can see, the getBean(String beanName) method is finally called, whether or not it is a FactoryBean
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null.null.false);
}
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
// Get beanName: A FactoryBean(preceded by an '&') and a beanName can be converted to the most orthodox beanName
// The main logic is that if it is a FactoryBean, the & is removed. If it is an alias, the real name is obtained from the alias
final String beanName = transformedBeanName(name);
// The last return value
Object bean;
// Check to see if it is initialized
Object sharedInstance = getSingleton(beanName);
// If it is already initialized and no args is passed, it means get
if(sharedInstance ! =null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("...");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); }}// If it is a normal Bean, it returns the instance object it created
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// If this bean of type prototype exists
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// If the current BeanDefinition does not exist and has a parent BeanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
// Returns the query result of the parent container
if(args ! =null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
returnparentBeanFactory.getBean(nameToLookup, requiredType); }}if(! typeCheckOnly) {// typeCheckOnly is false to put the current beanName into an alreadyCreated Set.
markBeanAsCreated(beanName);
}
/* * to create the bean */
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Initialize all the beans of the dependency, as defined in Depends -on
String[] dependsOn = mbd.getDependsOn();
if(dependsOn ! =null) {
for (String dep : dependsOn) {
// Check if there are loop dependencies
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// Register dependencies
registerDependentBean(dep, beanName);
// Initialize the dependents firstgetBean(dep); }}// if it is a singleton
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject(a) throws BeansException {
try {
// Perform the create Bean, as described below
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throwex; }}}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }// 如果是prototype
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
// Perform the create Bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// If it is not singleton and Prototype, then it is a custom scope, such as session in the Web project, which is left to the application of the custom scope
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject(a) throws BeansException {
beforePrototypeCreation(beanName);
try {
// Perform the create Bean
return createBean(beanName, mbd, args);
}
finally{ afterPrototypeCreation(beanName); }}}); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); }catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); }}}catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throwex; }}// Check the type of bean
if(requiredType ! =null&& bean ! =null && !requiredType.isInstance(bean)) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
}
Copy the code
The bean is checked to see if it exists and returned if it does. If the createBean(String beanName, RootBeanDefinition MBD, Object[] args) method is not called
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// Make sure the Class in BeanDefinition is loadedClass<? > resolvedClass = resolveBeanClass(mbd, beanName);if(resolvedClass ! =null&&! mbd.hasBeanClass() && mbd.getBeanClassName() ! =null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overwrites if < look-method /> and < rel-method /> are defined in the bean.
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// Return if there is a proxy
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if(bean ! =null) {
returnbean; }}catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
/ / create a bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// If it is. FactoryBean, it is removed from the cache
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// Instantiate the Bean. This method is the endpoint
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
/ / the bean instance
finalObject bean = (instanceWrapper ! =null ? instanceWrapper.getWrappedInstance() : null);
/ / bean typeClass<? > beanType = (instanceWrapper ! =null ? instanceWrapper.getWrappedClass() : null);
mbd.resolvedTargetType = beanType;
synchronized (mbd.postProcessingLock) {
if(! mbd.postProcessed) {try {
/ / cycle call implements MergedBeanDefinitionPostProcessor postProcessMergedBeanDefinition method of interface
Spring has several default implementations of this interface, the most familiar of which operates on the @autowired annotation
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true; }}// Resolve loop dependencies
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// When A is being created, A depends on B, so early expose is made by (8) putting A into the singleton factory as an ObjectFactory, where B needs to reference A, but A is being created, and gets the ObjectFactory from the singleton factory, thus allowing cyclic dependency
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject(a) throws BeansException {
returngetEarlyBeanReference(beanName, mbd, bean); }}); } Object exposedObject = bean;try {
// Responsible for attribute assembly, very important, below
// Assemble attributes
populateBean(beanName, mbd, instanceWrapper);
if(exposedObject ! =null) {
// Handle the various callbacks after the bean is initialized, such as init-method, InitializingBean interface, BeanPostProcessor interfaceexposedObject = initializeBean(beanName, exposedObject, mbd); }}catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); }}// Similarly, if there are cyclic dependencies
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if(earlySingletonReference ! =null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if(! actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); }}}}// Register the bean with the appropriate Scope
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
Copy the code
- populateBean((String beanName, RootBeanDefinition mbd, BeanWrapper bw)
This method completes the injection of properties inside the bean
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
// All attributes of the bean
PropertyValues pvs = mbd.getPropertyValues();
if (bw == null) {
if(! pvs.isEmpty()) {throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
return; }}boolean continueWithPropertyPopulation = true;
if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// If false is returned, no further attribute setting is required and no further BeanPostProcessor processing is required
if(! ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation =false;
break; }}}}if(! continueWithPropertyPopulation) {return;
}
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Find all attribute values by name. If it is a bean dependency, initialize the dependent bean first. Recording dependencies
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Assemble by type. complicated
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
booleanneedsDepCheck = (mbd.getDependencyCheck() ! = RootBeanDefinition.DEPENDENCY_CHECK_NONE);if(hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// This is a BeanPostProcessor for @autowired that was mentioned above
// It sets all attributes that mark @autoWired and @Value annotations
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return; }}}}if(needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); }}// Set the property value of the bean instance
applyPropertyValues(beanName, mbd, bw, pvs);
}
Copy the code
- finishRefresh()
protected void finishRefresh(a) {
// Clear the resource cache used by the previous sequence of operations
clearResourceCaches();
// Initialize LifecycleProcessor
initLifecycleProcessor();
The internal implementation of this method is to start all beans that implement the Lifecycle interface
getLifecycleProcessor().onRefresh();
// Publish the ContextRefreshedEvent event
publishEvent(new ContextRefreshedEvent(this));
/ / check spring. LiveBeansView. MbeanDomain exists, there will create a MBeanServer
LiveBeansView.registerApplicationContext(this);
}
Copy the code
3. Afterword.
Here is only for the container initialization to do the overall analysis of the bean creation and acquisition also involves a lot of knowledge points. We’ll talk about that later