Spring 启动过程( context 加载)源码分析_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > Spring 启动过程( context 加载)源码分析

Spring 启动过程( context 加载)源码分析

 2014/9/29 13:02:25  liuyun025  程序员俱乐部  我要评论(0)
  • 摘要:springsourcecodeinvestigateinvestigatespringsourcecodefromthreeaspects:1)what'stheresponsibilityofspringwebapplicationcontext,whichisdefaulttoorg.springframework.web.context.support.XmlWebApplicationContext2
  • 标签:

spring source code investigate

?

investigate spring source code from three aspects:

1) what's the responsibility of spring web application context,?

? which is default to org.springframework.web.context.support.XmlWebApplicationContext

2) spring xml configuration parser and spring annotation parser, including spring bean and spring AOP

3) how spring MVC handle a request, including handler mapping and handler adapter

?

?

1. web.xml

? ? all things start from web.xml.?

? ? we registered spring context loader listener in web.xml as shown below.

?

<!-- Creates the Spring Container shared by all Servlets and Filters -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

?

2. ContextLoaderListener.java

Bootstrap listener to start up and shut down Spring's root web application context.

?

Spring web application ContextLoaderListener implements ServletContextListener.?

That means when servlet container start up, servlet container will call contextInitialized() method,

which is a callback method implemented in the class ContextLoaderListener.

?

/**

* Initialize the root web application context.

*/

public void contextInitialized(ServletContextEvent event) {

this.contextLoader = createContextLoader();

if (this.contextLoader == null) {

this.contextLoader = this;

}

this.contextLoader.initWebApplicationContext(event.getServletContext());

}

?

ContextLoaderListener delegate the actual initialization work of the root application context to ContextLoader.java

?

3. ContextLoader.java

The actual initialization work is done in the method initWebApplicationContext() in ContextLoader.java

see the following snapshot of method initWebApplicationContext().

?

/**

* Initialize Spring's web application context for the given servlet context,

* using the application context provided at construction time, or creating a new one

* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and

* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.

* @param servletContext current servlet context

* @return the new WebApplicationContext

*/

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

try {

// Store context in local instance variable, to guarantee that

// it is available on ServletContext shutdown.

if (this.context == null) {

this.context = createWebApplicationContext(servletContext);

}

if (this.context instanceof ConfigurableWebApplicationContext) {

configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext);

}

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

?

ClassLoader ccl = Thread.currentThread().getContextClassLoader();

if (ccl == ContextLoader.class.getClassLoader()) {

currentContext = this.context;

}

return this.context;

}

}

?

/**

* Instantiate the root WebApplicationContext for this loader, either the

* default context class or a custom context class if specified.

* <p>This implementation expects custom contexts to implement the

* {@link ConfigurableWebApplicationContext} interface.

* Can be overridden in subclasses.

* <p>In addition, {@link #customizeContext} gets called prior to refreshing the

* context, allowing subclasses to perform custom modifications to the context.

* @param sc current servlet context

* @param parent the parent ApplicationContext to use, or <code>null</code> if none

* @return the root WebApplicationContext

* @see ConfigurableWebApplicationContext

*/

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {

Class<?> contextClass = determineContextClass(sc);

if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {

throw new ApplicationContextException("Custom context class [" + contextClass.getName() +

"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");

}

ConfigurableWebApplicationContext wac =

(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

return wac;

}

?

?

/**

* Return the WebApplicationContext implementation class to use, either the

* default XmlWebApplicationContext or a custom context class if specified.

* @param servletContext current servlet context

* @return the WebApplicationContext implementation class to use

* @see #CONTEXT_CLASS_PARAM

* @see org.springframework.web.context.support.XmlWebApplicationContext

*/

protected Class<?> determineContextClass(ServletContext servletContext) {

String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);

if (contextClassName != null) {

try {

return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());

}

catch (ClassNotFoundException ex) {

throw new ApplicationContextException(

"Failed to load custom context class [" + contextClassName + "]", ex);

}

}

else {

contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());

try {

return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());

}

catch (ClassNotFoundException ex) {

throw new ApplicationContextException(

"Failed to load default context class [" + contextClassName + "]", ex);

}

}

}

?

/**

* Name of the class path resource (relative to the ContextLoader class)

* that defines ContextLoader's default strategy names.

*/

private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

?

?

private static final Properties defaultStrategies;

?

static {

// Load default strategy implementations from properties file.

// This is currently strictly internal and not meant to be customized

// by application developers.

try {

ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);

defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);

}

catch (IOException ex) {

throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());

}

}

?

actually, spring web application context is configured in property file ContextLoader.properties, which is

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

?

so, the actual application context is XmlWebApplicationContext.

?

?

the most important method is configureAndRefreshWebApplicationContext() called by initWebApplicationContext().

spring called refresh() method in configureAndRefreshWebApplicationContext() method.?

spring do very important things in refresh() method, which we will investigate later.

?

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {

if (ObjectUtils.identityToString(wac).equals(wac.getId())) {

// The application context id is still set to its original default value

// -> assign a more useful id based on available information

String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);

if (idParam != null) {

wac.setId(idParam);

}

else {

// Generate default id...

if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {

// Servlet <= 2.4: resort to name specified in web.xml, if any.

wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +

ObjectUtils.getDisplayString(sc.getServletContextName()));

}

else {

wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +

ObjectUtils.getDisplayString(sc.getContextPath()));

}

}

}

?

// Determine parent for root web application context, if any.

ApplicationContext parent = loadParentContext(sc);

?

wac.setParent(parent);

wac.setServletContext(sc);

String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);

if (initParameter != null) {

wac.setConfigLocation(initParameter);

}

customizeContext(sc, wac);

wac.refresh();

}

?

?

4. refresh() method

refresh() method is implemented in abstract class AbstractApplicationContext.java,?

which is extended by the actual application context class XmlWebApplicationContext.java

?

public void refresh() 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) {

// Destroy already created singletons to avoid dangling resources.

destroyBeans();

?

// Reset 'active' flag.

cancelRefresh(ex);

?

// Propagate exception to caller.

throw ex;

}

}

}

?

// Tell the subclass to refresh the internal bean factory.

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

?

// Prepare the bean factory for use in this context.

prepareBeanFactory(beanFactory);

?

?

5. XmlWebApplicationContext.java

root web application context class of Spring MVC application.

?

/**

* Loads the bean definitions via an XmlBeanDefinitionReader.

* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader

* @see #initBeanDefinitionReader

* @see #loadBeanDefinitions

*/

@Override

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {

// Create a new XmlBeanDefinitionReader for the given BeanFactory.

XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

?

// Configure the bean definition reader with this context's

// resource loading environment.

beanDefinitionReader.setEnvironment(this.getEnvironment());

beanDefinitionReader.setResourceLoader(this);

beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

?

// Allow a subclass to provide custom initialization of the reader,

// then proceed with actually loading the bean definitions.

initBeanDefinitionReader(beanDefinitionReader);

loadBeanDefinitions(beanDefinitionReader);

}

?

/**

* Load the bean definitions with the given XmlBeanDefinitionReader.

* <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;

* therefore this method is just supposed to load and/or register bean definitions.

* <p>Delegates to a ResourcePatternResolver for resolving location patterns

* into Resource instances.

* @throws java.io.IOException if the required XML document isn't found

* @see #refreshBeanFactory

* @see #getConfigLocations

* @see #getResources

* @see #getResourcePatternResolver

*/

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {

String[] configLocations = getConfigLocations();

if (configLocations != null) {

for (String configLocation : configLocations) {

reader.loadBeanDefinitions(configLocation);

}

}

}

?

XmlWebApplicationContext delegate XmlBeanDefinitionReader.loadBeanDefinitions() to perform

the actual work of loading bean definitions.

?

6. XmlBeanDefinitionReader.java

?

/**

* Load bean definitions from the specified XML file.

* @param encodedResource the resource descriptor for the XML file,

* allowing to specify an encoding to use for parsing the file

* @return the number of bean definitions found

* @throws BeanDefinitionStoreException in case of loading or parsing errors

*/

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {

Assert.notNull(encodedResource, "EncodedResource must not be null");

if (logger.isInfoEnabled()) {

logger.info("Loading XML bean definitions from " + encodedResource.getResource());

}

?

Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

if (currentResources == null) {

currentResources = new HashSet<EncodedResource>(4);

this.resourcesCurrentlyBeingLoaded.set(currentResources);

}

if (!currentResources.add(encodedResource)) {

throw new BeanDefinitionStoreException(

"Detected cyclic loading of " + encodedResource + " - check your import definitions!");

}

try {

InputStream inputStream = encodedResource.getResource().getInputStream();

try {

InputSource inputSource = new InputSource(inputStream);

if (encodedResource.getEncoding() != null) {

inputSource.setEncoding(encodedResource.getEncoding());

}

return doLoadBeanDefinitions(inputSource, encodedResource.getResource());

}

finally {

inputStream.close();

}

}

catch (IOException ex) {

throw new BeanDefinitionStoreException(

"IOException parsing XML document from " + encodedResource.getResource(), ex);

}

finally {

currentResources.remove(encodedResource);

if (currentResources.isEmpty()) {

this.resourcesCurrentlyBeingLoaded.remove();

}

}

}

?

/**

* Actually load bean definitions from the specified XML file.

* @param inputSource the SAX InputSource to read from

* @param resource the resource descriptor for the XML file

* @return the number of bean definitions found

* @throws BeanDefinitionStoreException in case of loading or parsing errors

*/

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)

throws BeanDefinitionStoreException {

try {

int validationMode = getValidationModeForResource(resource);

Document doc = this.documentLoader.loadDocument(

inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());

return registerBeanDefinitions(doc, resource);

}

}

?

?

/**

* Register the bean definitions contained in the given DOM document.

* Called by <code>loadBeanDefinitions</code>.

* <p>Creates a new instance of the parser class and invokes

* <code>registerBeanDefinitions</code> on it.

* @param doc the DOM document

* @param resource the resource descriptor (for context information)

* @return the number of bean definitions found

* @throws BeanDefinitionStoreException in case of parsing errors

* @see #loadBeanDefinitions

* @see #setDocumentReaderClass

* @see BeanDefinitionDocumentReader#registerBeanDefinitions

*/

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {

BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

documentReader.setEnvironment(this.getEnvironment());

int countBefore = getRegistry().getBeanDefinitionCount();

documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

return getRegistry().getBeanDefinitionCount() - countBefore;

}

?

/**

* Create the {@link XmlReaderContext} to pass over to the document reader.

*/

protected XmlReaderContext createReaderContext(Resource resource) {

if (this.namespaceHandlerResolver == null) {

this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();

}

return new XmlReaderContext(resource, this.problemReporter, this.eventListener,

this.sourceExtractor, this, this.namespaceHandlerResolver);

}

?

/**

* Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.

* Default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}.

*/

protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {

return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());

}

?

using DefaultNamespaceHandlerResolver to resolve xml elements.

?

?

7. DefaultBeanDefinitionDocumentReader.java

?

/**

* {@inheritDoc}

* <p>This implementation parses bean definitions according to the "spring-beans" XSD

* (or DTD, historically).

* <p>Opens a DOM Document; then initializes the default settings

* specified at the {@code <beans/>} level; then parses the contained bean definitions.

*/

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {

this.readerContext = readerContext;

?

logger.debug("Loading bean definitions");

Element root = doc.getDocumentElement();

?

doRegisterBeanDefinitions(root);

}

?

/**

* Register each bean definition within the given root {@code <beans/>} element.

* @throws IllegalStateException if {@code <beans profile="..."} attribute is present

* and Environment property has not been set

* @see #setEnvironment

*/

protected void doRegisterBeanDefinitions(Element root) {

String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);

if (StringUtils.hasText(profileSpec)) {

Assert.state(this.environment != null, "environment property must not be null");

String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);

if (!this.environment.acceptsProfiles(specifiedProfiles)) {

return;

}

}

?

// any nested <beans> elements will cause recursion in this method. In

// order to propagate and preserve <beans> default-* attributes correctly,

// keep track of the current (parent) delegate, which may be null. Create

// the new (child) delegate with a reference to the parent for fallback purposes,

// then ultimately reset this.delegate back to its original (parent) reference.

// this behavior emulates a stack of delegates without actually necessitating one.

BeanDefinitionParserDelegate parent = this.delegate;

this.delegate = createHelper(readerContext, root, parent);

?

preProcessXml(root);

parseBeanDefinitions(root, this.delegate);

postProcessXml(root);

?

this.delegate = parent;

}

?

protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {

BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext, environment);

delegate.initDefaults(root, parentDelegate);

return delegate;

}

?

?

/**

* Parse the elements at the root level in the document:

* "import", "alias", "bean".

* @param root the DOM root element of the document

*/

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {

if (delegate.isDefaultNamespace(root)) {

NodeList nl = root.getChildNodes();

for (int i = 0; i < nl.getLength(); i++) {

Node node = nl.item(i);

if (node instanceof Element) {

Element ele = (Element) node;

if (delegate.isDefaultNamespace(ele)) {

parseDefaultElement(ele, delegate);

}

else {

delegate.parseCustomElement(ele);

}

}

}

}

else {

delegate.parseCustomElement(root);

}

}

?

8. BeanDefinitionParserDelegate.java

?

public BeanDefinition parseCustomElement(Element ele) {

return parseCustomElement(ele, null);

}

?

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {

String namespaceUri = getNamespaceURI(ele);

NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);

if (handler == null) {

error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);

return null;

}

return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

}

?

this readerContext is DefaultNamespaceHandlerResolver.java

?

9. DefaultNamespaceHandlerResolver.java

?

/**

* Locate the {@link NamespaceHandler} for the supplied namespace URI

* from the configured mappings.

* @param namespaceUri the relevant namespace URI

* @return the located {@link NamespaceHandler}, or <code>null</code> if none found

*/

public NamespaceHandler resolve(String namespaceUri) {

Map<String, Object> handlerMappings = getHandlerMappings();

Object handlerOrClassName = handlerMappings.get(namespaceUri);

if (handlerOrClassName == null) {

return null;

}

else if (handlerOrClassName instanceof NamespaceHandler) {

return (NamespaceHandler) handlerOrClassName;

}

else {

String className = (String) handlerOrClassName;

try {

Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);

if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {

throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +

"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");

}

NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);

namespaceHandler.init();

handlerMappings.put(namespaceUri, namespaceHandler);

return namespaceHandler;

}

catch (ClassNotFoundException ex) {

throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +

namespaceUri + "] not found", ex);

}

catch (LinkageError err) {

throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +

namespaceUri + "]: problem with handler class file or dependent class", err);

}

}

}

?

/**

* Load the specified NamespaceHandler mappings lazily.

*/

private Map<String, Object> getHandlerMappings() {

if (this.handlerMappings == null) {

synchronized (this) {

if (this.handlerMappings == null) {

try {

Properties mappings =

PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);

if (logger.isDebugEnabled()) {

logger.debug("Loaded NamespaceHandler mappings: " + mappings);

}

Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>();

CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);

this.handlerMappings = handlerMappings;

}

catch (IOException ex) {

throw new IllegalStateException(

"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);

}

}

}

}

return this.handlerMappings;

}

?

?

/**

* The location to look for the mapping files. Can be present in multiple JAR files.

*/

public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

?

/** Resource location to search for */

private final String handlerMappingsLocation;

?

handlerMappingsLocation is default to META-INF/spring.handlers

?

method resolve() call namespaceHandler.init()

?

e.g. spring AOP spring.handlers :?

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

?

will call the init() method of AopNamespaceHandler, which is:

/**

* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the

* '<code>config</code>', '<code>spring-configured</code>', '<code>aspectj-autoproxy</code>'

* and '<code>scoped-proxy</code>' tags.

*/

public void init() {

// In 2.0 XSD as well as in 2.1 XSD.

registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());

registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());

registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

?

// Only in 2.0 XSD: moved to context namespace as of 2.1

registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());

}

?

10.?

?

/**

* 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() throws BeansException {

if (hasBeanFactory()) {

destroyBeans();

closeBeanFactory();

}

try {

DefaultListableBeanFactory beanFactory = createBeanFactory();

beanFactory.setSerializationId(getId());

customizeBeanFactory(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);

}

}

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

  • 相关文章
发表评论
用户名: 匿名