• 简单读!spring-mvc源码之url的暴露之路


    spring中,注册controller的url有多种方式:

      1. 你可以啥都不都干,直接使用 @RequestMapping 注解上体路径,然后加上 <component-scan>, 访问的时候就根据这个路径来加载handler了;
      2. 如果你想自定义一些路径的访问方式,那么你可以自定义 RequestMappingHandlerMapping, 然后使用一个 bean 去扫描即可(需排除原始controller的扫描);

    那么,url 具体是如何映射出来的呢?

      加载前面的细节就不多说了,总之一句话,tomcat转到spring后,一切就开始了。

    关于加载前情,有兴趣可以展开阅读下:

    // org.apache.catalina.core.StandardContext.startInternal(), 接入 应用 
    
        // 启动
        /**
         * Start this component and implement the requirements
         * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
         *
         * @exception LifecycleException if this component detects a fatal error
         *  that prevents this component from being used
         */
        @Override
        protected synchronized void startInternal() throws LifecycleException {
    
            if(log.isDebugEnabled())
                log.debug("Starting " + getBaseName());
    
            // Send j2ee.state.starting notification
            if (this.getObjectName() != null) {
                Notification notification = new Notification("j2ee.state.starting",
                        this.getObjectName(), sequenceNumber.getAndIncrement());
                broadcaster.sendNotification(notification);
            }
    
            setConfigured(false);
            boolean ok = true;
    
            // Currently this is effectively a NO-OP but needs to be called to
            // ensure the NamingResources follows the correct lifecycle
            if (namingResources != null) {
                namingResources.start();
            }
    
            // Post work directory
            postWorkDirectory();
    
            // Add missing components as necessary
            if (getResources() == null) {   // (1) Required by Loader
                if (log.isDebugEnabled())
                    log.debug("Configuring default Resources");
    
                try {
                    setResources(new StandardRoot(this));
                } catch (IllegalArgumentException e) {
                    log.error(sm.getString("standardContext.resourcesInit"), e);
                    ok = false;
                }
            }
            if (ok) {
                resourcesStart();
            }
    
            if (getLoader() == null) {
                WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
                webappLoader.setDelegate(getDelegate());
                setLoader(webappLoader);
            }
    
            // An explicit cookie processor hasn't been specified; use the default
            if (cookieProcessor == null) {
                cookieProcessor = new Rfc6265CookieProcessor();
            }
    
            // Initialize character set mapper
            getCharsetMapper();
    
            // Validate required extensions
            boolean dependencyCheck = true;
            try {
                dependencyCheck = ExtensionValidator.validateApplication
                    (getResources(), this);
            } catch (IOException ioe) {
                log.error(sm.getString("standardContext.extensionValidationError"), ioe);
                dependencyCheck = false;
            }
    
            if (!dependencyCheck) {
                // do not make application available if dependency check fails
                ok = false;
            }
    
            // Reading the "catalina.useNaming" environment variable
            String useNamingProperty = System.getProperty("catalina.useNaming");
            if ((useNamingProperty != null)
                && (useNamingProperty.equals("false"))) {
                useNaming = false;
            }
    
            if (ok && isUseNaming()) {
                if (getNamingContextListener() == null) {
                    NamingContextListener ncl = new NamingContextListener();
                    ncl.setName(getNamingContextName());
                    ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
                    addLifecycleListener(ncl);
                    setNamingContextListener(ncl);
                }
            }
    
            // Standard container startup
            if (log.isDebugEnabled())
                log.debug("Processing standard container startup");
    
    
            // Binding thread
            ClassLoader oldCCL = bindThread();
    
            try {
                if (ok) {
                    // Start our subordinate components, if any
                    Loader loader = getLoader();
                    if (loader instanceof Lifecycle) {
                        ((Lifecycle) loader).start();
                    }
    
                    // since the loader just started, the webapp classloader is now
                    // created.
                    setClassLoaderProperty("clearReferencesRmiTargets",
                            getClearReferencesRmiTargets());
                    setClassLoaderProperty("clearReferencesStopThreads",
                            getClearReferencesStopThreads());
                    setClassLoaderProperty("clearReferencesStopTimerThreads",
                            getClearReferencesStopTimerThreads());
                    setClassLoaderProperty("clearReferencesHttpClientKeepAliveThread",
                            getClearReferencesHttpClientKeepAliveThread());
                    setClassLoaderProperty("clearReferencesObjectStreamClassCaches",
                            getClearReferencesObjectStreamClassCaches());
    
                    // By calling unbindThread and bindThread in a row, we setup the
                    // current Thread CCL to be the webapp classloader
                    unbindThread(oldCCL);
                    oldCCL = bindThread();
    
                    // Initialize logger again. Other components might have used it
                    // too early, so it should be reset.
                    logger = null;
                    getLogger();
    
                    Realm realm = getRealmInternal();
                    if(null != realm) {
                        if (realm instanceof Lifecycle) {
                            ((Lifecycle) realm).start();
                        }
    
                        // Place the CredentialHandler into the ServletContext so
                        // applications can have access to it. Wrap it in a "safe"
                        // handler so application's can't modify it.
                        CredentialHandler safeHandler = new CredentialHandler() {
                            @Override
                            public boolean matches(String inputCredentials, String storedCredentials) {
                                return getRealmInternal().getCredentialHandler().matches(inputCredentials, storedCredentials);
                            }
    
                            @Override
                            public String mutate(String inputCredentials) {
                                return getRealmInternal().getCredentialHandler().mutate(inputCredentials);
                            }
                        };
                        context.setAttribute(Globals.CREDENTIAL_HANDLER, safeHandler);
                    }
    
                    // Notify our interested LifecycleListeners
                    fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
    
                    // Start our child containers, if not already started
                    for (Container child : findChildren()) {
                        if (!child.getState().isAvailable()) {
                            child.start();
                        }
                    }
    
                    // Start the Valves in our pipeline (including the basic),
                    // if any
                    if (pipeline instanceof Lifecycle) {
                        ((Lifecycle) pipeline).start();
                    }
    
                    // Acquire clustered manager
                    Manager contextManager = null;
                    Manager manager = getManager();
                    if (manager == null) {
                        if (log.isDebugEnabled()) {
                            log.debug(sm.getString("standardContext.cluster.noManager",
                                    Boolean.valueOf((getCluster() != null)),
                                    Boolean.valueOf(distributable)));
                        }
                        if ( (getCluster() != null) && distributable) {
                            try {
                                contextManager = getCluster().createManager(getName());
                            } catch (Exception ex) {
                                log.error("standardContext.clusterFail", ex);
                                ok = false;
                            }
                        } else {
                            contextManager = new StandardManager();
                        }
                    }
    
                    // Configure default manager if none was specified
                    if (contextManager != null) {
                        if (log.isDebugEnabled()) {
                            log.debug(sm.getString("standardContext.manager",
                                    contextManager.getClass().getName()));
                        }
                        setManager(contextManager);
                    }
    
                    if (manager!=null && (getCluster() != null) && distributable) {
                        //let the cluster know that there is a context that is distributable
                        //and that it has its own manager
                        getCluster().registerManager(manager);
                    }
                }
    
                if (!getConfigured()) {
                    log.error(sm.getString("standardContext.configurationFail"));
                    ok = false;
                }
    
                // We put the resources into the servlet context
                if (ok)
                    getServletContext().setAttribute
                        (Globals.RESOURCES_ATTR, getResources());
    
                if (ok ) {
                    if (getInstanceManager() == null) {
                        javax.naming.Context context = null;
                        if (isUseNaming() && getNamingContextListener() != null) {
                            context = getNamingContextListener().getEnvContext();
                        }
                        Map<String, Map<String, String>> injectionMap = buildInjectionMap(
                                getIgnoreAnnotations() ? new NamingResourcesImpl(): getNamingResources());
                        setInstanceManager(new DefaultInstanceManager(context,
                                injectionMap, this, this.getClass().getClassLoader()));
                    }
                    getServletContext().setAttribute(
                            InstanceManager.class.getName(), getInstanceManager());
                    InstanceManagerBindings.bind(getLoader().getClassLoader(), getInstanceManager());
                }
    
                // Create context attributes that will be required
                if (ok) {
                    getServletContext().setAttribute(
                            JarScanner.class.getName(), getJarScanner());
                }
    
                // Set up the context init params
                mergeParameters();
    
                // Call ServletContainerInitializers
                for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
                    initializers.entrySet()) {
                    try {
                        entry.getKey().onStartup(entry.getValue(),
                                getServletContext());
                    } catch (ServletException e) {
                        log.error(sm.getString("standardContext.sciFail"), e);
                        ok = false;
                        break;
                    }
                }
    
                // Configure and call application event listeners
                if (ok) {
                    if (!listenerStart()) {
                        log.error(sm.getString("standardContext.listenerFail"));
                        ok = false;
                    }
                }
    
                // Check constraints for uncovered HTTP methods
                // Needs to be after SCIs and listeners as they may programmatically
                // change constraints
                if (ok) {
                    checkConstraintsForUncoveredMethods(findConstraints());
                }
    
                try {
                    // Start manager
                    Manager manager = getManager();
                    if (manager instanceof Lifecycle) {
                        ((Lifecycle) manager).start();
                    }
                } catch(Exception e) {
                    log.error(sm.getString("standardContext.managerFail"), e);
                    ok = false;
                }
    
                // Configure and call application filters
                if (ok) {
                    if (!filterStart()) {
                        log.error(sm.getString("standardContext.filterFail"));
                        ok = false;
                    }
                }
    
                // Load and initialize all "load on startup" servlets
                if (ok) {
                    // 加载 servlet , startup 设置为1 的项 <load-on-startup>1</load-on-startup>
                    if (!loadOnStartup(findChildren())){
                        log.error(sm.getString("standardContext.servletFail"));
                        ok = false;
                    }
                }
    
                // Start ContainerBackgroundProcessor thread
                super.threadStart();
            } finally {
                // Unbinding thread
                unbindThread(oldCCL);
            }
    
            // Set available status depending upon startup success
            if (ok) {
                if (log.isDebugEnabled())
                    log.debug("Starting completed");
            } else {
                log.error(sm.getString("standardContext.startFailed", getName()));
            }
    
            startTime=System.currentTimeMillis();
    
            // Send j2ee.state.running notification
            if (ok && (this.getObjectName() != null)) {
                Notification notification =
                    new Notification("j2ee.state.running", this.getObjectName(),
                                     sequenceNumber.getAndIncrement());
                broadcaster.sendNotification(notification);
            }
    
            // The WebResources implementation caches references to JAR files. On
            // some platforms these references may lock the JAR files. Since web
            // application start is likely to have read from lots of JARs, trigger
            // a clean-up now.
            getResources().gc();
    
            // Reinitializing if something went wrong
            if (!ok) {
                setState(LifecycleState.FAILED);
            } else {
                setState(LifecycleState.STARTING);
            }
        }
    
        /**
         * Load and initialize all servlets marked "load on startup" in the
         * web application deployment descriptor.
         *
         * @param children Array of wrappers for all currently defined
         *  servlets (including those not declared load on startup)
         * @return <code>true</code> if load on startup was considered successful
         */
        public boolean loadOnStartup(Container children[]) {
    
            // Collect "load on startup" servlets that need to be initialized
            TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
            for (int i = 0; i < children.length; i++) {
                Wrapper wrapper = (Wrapper) children[i];
                int loadOnStartup = wrapper.getLoadOnStartup();
                if (loadOnStartup < 0)
                    continue;
                Integer key = Integer.valueOf(loadOnStartup);
                ArrayList<Wrapper> list = map.get(key);
                if (list == null) {
                    list = new ArrayList<>();
                    map.put(key, list);
                }
                list.add(wrapper);
            }
    
            // Load the collected "load on startup" servlets
            for (ArrayList<Wrapper> list : map.values()) {
                for (Wrapper wrapper : list) {
                    try {
                        // 调用 wrapper,
                        wrapper.load();
                    } catch (ServletException e) {
                        getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",
                              getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
                        // NOTE: load errors (including a servlet that throws
                        // UnavailableException from the init() method) are NOT
                        // fatal to application startup
                        // unless failCtxIfServletStartFails="true" is specified
                        if(getComputedFailCtxIfServletStartFails()) {
                            return false;
                        }
                    }
                }
            }
            return true;
    
        }
    
    // StandardWrapper.load()
        /**
         * Load and initialize an instance of this servlet, if there is not already
         * at least one initialized instance.  This can be used, for example, to
         * load servlets that are marked in the deployment descriptor to be loaded
         * at server startup time.
         * <p>
         * <b>IMPLEMENTATION NOTE</b>:  Servlets whose classnames begin with
         * <code>org.apache.catalina.</code> (so-called "container" servlets)
         * are loaded by the same classloader that loaded this class, rather than
         * the classloader for the current web application.
         * This gives such classes access to Catalina internals, which are
         * prevented for classes loaded for web applications.
         *
         * @exception ServletException if the servlet init() method threw
         *  an exception
         * @exception ServletException if some other loading problem occurs
         */
        @Override
        public synchronized void load() throws ServletException {
            instance = loadServlet();
    
            if (!instanceInitialized) {
                initServlet(instance);
            }
    
            if (isJspServlet) {
                StringBuilder oname = new StringBuilder(getDomain());
    
                oname.append(":type=JspMonitor");
    
                oname.append(getWebModuleKeyProperties());
    
                oname.append(",name=");
                oname.append(getName());
    
                oname.append(getJ2EEKeyProperties());
    
                try {
                    jspMonitorON = new ObjectName(oname.toString());
                    Registry.getRegistry(null, null)
                        .registerComponent(instance, jspMonitorON, null);
                } catch( Exception ex ) {
                    log.info("Error registering JSP monitoring with jmx " +
                             instance);
                }
            }
        }
    
    
        /**
         * Load and initialize an instance of this servlet, if there is not already
         * at least one initialized instance.  This can be used, for example, to
         * load servlets that are marked in the deployment descriptor to be loaded
         * at server startup time.
         * @return the loaded Servlet instance
         * @throws ServletException for a Servlet load error
         */
        public synchronized Servlet loadServlet() throws ServletException {
    
            // Nothing to do if we already have an instance or an instance pool
            if (!singleThreadModel && (instance != null))
                return instance;
    
            PrintStream out = System.out;
            if (swallowOutput) {
                SystemLogHandler.startCapture();
            }
    
            Servlet servlet;
            try {
                long t1=System.currentTimeMillis();
                // Complain if no servlet class has been specified
                if (servletClass == null) {
                    unavailable(null);
                    throw new ServletException
                        (sm.getString("standardWrapper.notClass", getName()));
                }
    
                InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
                try {
                    servlet = (Servlet) instanceManager.newInstance(servletClass);
                } catch (ClassCastException e) {
                    unavailable(null);
                    // Restore the context ClassLoader
                    throw new ServletException
                        (sm.getString("standardWrapper.notServlet", servletClass), e);
                } catch (Throwable e) {
                    e = ExceptionUtils.unwrapInvocationTargetException(e);
                    ExceptionUtils.handleThrowable(e);
                    unavailable(null);
    
                    // Added extra log statement for Bugzilla 36630:
                    // https://bz.apache.org/bugzilla/show_bug.cgi?id=36630
                    if(log.isDebugEnabled()) {
                        log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);
                    }
    
                    // Restore the context ClassLoader
                    throw new ServletException
                        (sm.getString("standardWrapper.instantiate", servletClass), e);
                }
    
                if (multipartConfigElement == null) {
                    MultipartConfig annotation =
                            servlet.getClass().getAnnotation(MultipartConfig.class);
                    if (annotation != null) {
                        multipartConfigElement =
                                new MultipartConfigElement(annotation);
                    }
                }
    
                // Special handling for ContainerServlet instances
                // Note: The InstanceManager checks if the application is permitted
                //       to load ContainerServlets
                if (servlet instanceof ContainerServlet) {
                    ((ContainerServlet) servlet).setWrapper(this);
                }
    
                classLoadTime=(int) (System.currentTimeMillis() -t1);
    
                if (servlet instanceof SingleThreadModel) {
                    if (instancePool == null) {
                        instancePool = new Stack<>();
                    }
                    singleThreadModel = true;
                }
    
                // 初始化 GenericServlet.servlet()
                initServlet(servlet);
    
                fireContainerEvent("load", this);
    
                loadTime=System.currentTimeMillis() -t1;
            } finally {
                if (swallowOutput) {
                    String log = SystemLogHandler.stopCapture();
                    if (log != null && log.length() > 0) {
                        if (getServletContext() != null) {
                            getServletContext().log(log);
                        } else {
                            out.println(log);
                        }
                    }
                }
            }
            return servlet;
    
        }
        
        
    // javax.servlet.GenericServlet.initServlet(),  注意,这是 tomcat 的包
    
        /**
         * Called by the servlet container to indicate to a servlet that the servlet
         * is being placed into service. See {@link Servlet#init}.
         * <p>
         * This implementation stores the {@link ServletConfig} object it receives
         * from the servlet container for later use. When overriding this form of
         * the method, call <code>super.init(config)</code>.
         *
         * @param config
         *            the <code>ServletConfig</code> object that contains
         *            configuration information for this servlet
         * @exception ServletException
         *                if an exception occurs that interrupts the servlet's
         *                normal operation
         * @see UnavailableException
         */
        @Override
        public void init(ServletConfig config) throws ServletException {
            this.config = config;
            // 转到 HttpServletBean
            this.init();
        }
    
    // 转到 org.springframework.web.servlet.HttpServletBean.init()
    
        /**
         * Map config parameters onto bean properties of this servlet, and
         * invoke subclass initialization.
         * @throws ServletException if bean properties are invalid (or required
         * properties are missing), or if subclass initialization fails.
         */
        @Override
        public final void init() throws ServletException {
            if (logger.isDebugEnabled()) {
                logger.debug("Initializing servlet '" + getServletName() + "'");
            }
    
            // Set bean properties from init parameters.
            try {
                PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                throw ex;
            }
    
            // servlet 权限移交,让子类初始化一切,即web.xml中配置的 DispatcherServlet, FrameworkServlet.initServletBean()
            // Let subclasses do whatever initialization they like.
            initServletBean();
    
            if (logger.isDebugEnabled()) {
                logger.debug("Servlet '" + getServletName() + "' configured successfully");
            }
        }
        
    // org.springframework.web.servlet.FrameworkServlet.initServletBean() 接收控制权,加载 spring
        /**
         * Overridden method of {@link HttpServletBean}, invoked after any bean properties
         * have been set. Creates this servlet's WebApplicationContext.
         */
        @Override
        protected final void initServletBean() throws ServletException {
            getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
            if (this.logger.isInfoEnabled()) {
                this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
            }
            long startTime = System.currentTimeMillis();
    
            try {
                // 初始化上下文
                this.webApplicationContext = initWebApplicationContext();
                initFrameworkServlet();
            }
            catch (ServletException ex) {
                this.logger.error("Context initialization failed", ex);
                throw ex;
            }
            catch (RuntimeException ex) {
                this.logger.error("Context initialization failed", ex);
                throw ex;
            }
    
            if (this.logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                        elapsedTime + " ms");
            }
        }
    
        /**
         * Initialize and publish the WebApplicationContext for this servlet.
         * <p>Delegates to {@link #createWebApplicationContext} for actual creation
         * of the context. Can be overridden in subclasses.
         * @return the WebApplicationContext instance
         * @see #FrameworkServlet(WebApplicationContext)
         * @see #setContextClass
         * @see #setContextConfigLocation
         */
        protected WebApplicationContext initWebApplicationContext() {
            WebApplicationContext rootContext =
                    WebApplicationContextUtils.getWebApplicationContext(getServletContext());
            WebApplicationContext wac = null;
    
            if (this.webApplicationContext != null) {
                // A context instance was injected at construction time -> use it
                wac = this.webApplicationContext;
                if (wac instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                    if (!cwac.isActive()) {
                        // The context has not yet been refreshed -> provide services such as
                        // setting the parent context, setting the application context id, etc
                        if (cwac.getParent() == null) {
                            // The context instance was injected without an explicit parent -> set
                            // the root application context (if any; may be null) as the parent
                            cwac.setParent(rootContext);
                        }
                        configureAndRefreshWebApplicationContext(cwac);
                    }
                }
            }
            if (wac == null) {
                // No context instance was injected at construction time -> see if one
                // has been registered in the servlet context. If one exists, it is assumed
                // that the parent context (if any) has already been set and that the
                // user has performed any initialization such as setting the context id
                wac = findWebApplicationContext();
            }
            if (wac == null) {
                // No context instance is defined for this servlet -> create a local one
                // 创建 webApplicationContext, 一般可以是 org.springframework.web.context.support.XmlWebApplicationContext
                wac = createWebApplicationContext(rootContext);
            }
    
            if (!this.refreshEventReceived) {
                // Either the context is not a ConfigurableApplicationContext with refresh
                // support or the context injected at construction time had already been
                // refreshed -> trigger initial onRefresh manually here.
                onRefresh(wac);
            }
    
            if (this.publishContext) {
                // Publish the context as a servlet context attribute.
                String attrName = getServletContextAttributeName();
                getServletContext().setAttribute(attrName, wac);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                            "' as ServletContext attribute with name [" + attrName + "]");
                }
            }
    
            return wac;
        }
        
        /**
         * Instantiate the WebApplicationContext for this servlet, either a default
         * {@link org.springframework.web.context.support.XmlWebApplicationContext}
         * or a {@link #setContextClass custom context class}, if set.
         * Delegates to #createWebApplicationContext(ApplicationContext).
         * @param parent the parent WebApplicationContext to use, or {@code null} if none
         * @return the WebApplicationContext for this servlet
         * @see org.springframework.web.context.support.XmlWebApplicationContext
         * @see #createWebApplicationContext(ApplicationContext)
         */
        protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
            return createWebApplicationContext((ApplicationContext) parent);
        }
        
        /**
         * Instantiate the WebApplicationContext for this servlet, either a default
         * {@link org.springframework.web.context.support.XmlWebApplicationContext}
         * or a {@link #setContextClass custom context class}, if set.
         * <p>This implementation expects custom contexts to implement the
         * {@link org.springframework.web.context.ConfigurableWebApplicationContext}
         * interface. Can be overridden in subclasses.
         * <p>Do not forget to register this servlet instance as application listener on the
         * created context (for triggering its {@link #onRefresh callback}, and to call
         * {@link org.springframework.context.ConfigurableApplicationContext#refresh()}
         * before returning the context instance.
         * @param parent the parent ApplicationContext to use, or {@code null} if none
         * @return the WebApplicationContext for this servlet
         * @see org.springframework.web.context.support.XmlWebApplicationContext
         */
        protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
            Class<?> contextClass = getContextClass();
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Servlet with name '" + getServletName() +
                        "' will try to create custom WebApplicationContext context of class '" +
                        contextClass.getName() + "'" + ", using parent context [" + parent + "]");
            }
            if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
                throw new ApplicationContextException(
                        "Fatal initialization error in servlet with name '" + getServletName() +
                        "': custom WebApplicationContext class [" + contextClass.getName() +
                        "] is not of type ConfigurableWebApplicationContext");
            }
            // 创建一个新的 org.springframework.web.context.support.XmlWebApplicationContext, 加载 bean...
            ConfigurableWebApplicationContext wac =
                    (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    
            wac.setEnvironment(getEnvironment());
            wac.setParent(parent);
            wac.setConfigLocation(getContextConfigLocation());
    
            configureAndRefreshWebApplicationContext(wac);
    
            return wac;
        }
    
        // 最后一步,为刷新spring做好准备
        protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
            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
                if (this.contextId != null) {
                    wac.setId(this.contextId);
                }
                else {
                    // Generate default id...
                    wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                            ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
                }
            }
    
            wac.setServletContext(getServletContext());
            wac.setServletConfig(getServletConfig());
            wac.setNamespace(getNamespace());
            wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
    
            // The wac environment's #initPropertySources will be called in any case when the context
            // is refreshed; do it eagerly here to ensure servlet property sources are in place for
            // use in any post-processing or initialization that occurs below prior to #refresh
            ConfigurableEnvironment env = wac.getEnvironment();
            if (env instanceof ConfigurableWebEnvironment) {
                ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
            }
    
            postProcessWebApplicationContext(wac);
            applyInitializers(wac);
            // 最后,调用 刷新接口,终于到我们熟悉的 refresh() 了。
            wac.refresh();
        }
        
    View Code

    到了spring的 refresh()方法(可以是 XmlWebApplicationContext, XmlClassPathApplicationContext...),即会进行所有的bean组件及其他组件的初始化操作了!忘文生义。

        
    // org.springframework.context.support.AbstractApplicationContext, 熟悉的加载方式来了
        @Override
        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.
                    // 初始化参数时,注册 url
                    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();
                }
            }
        }

    如上,就是spring的整个加载程序框架,简洁明了。不过,这不是我们要说的重点,我们要说的是 url 是如何暴露的?

      我们只需要看这一句: finishBeanFactoryInitialization(beanFactory); url 是从这里开始的!
      这句的目的是,完成bean的初始化操作,听起来还是有点抽象,我们来看一下细节:

        /**
         * 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.
            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(new StringValueResolver() {
                    @Override
                    public String resolveStringValue(String strVal) {
                        return 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.
            // 继续处理剩下的单例,即在此将 url 注册
            beanFactory.preInstantiateSingletons();
        }

      单从字面意思上看,更像获取许多的bean, 而且还不会使用。其实这里的意思是,通过 getBean 的方式,初始化一些特殊的bean, 比如: ConversionService, LoadTimeWeaverAware
      但是最后一句不同: beanFactory.preInstantiateSingletons(); 意思是要初始化 单例啊!

    到底要初始化啥单例呢?

    // org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons()
        public void preInstantiateSingletons() throws BeansException {
            if (logger.isDebugEnabled()) {
                logger.debug("Pre-instantiating singletons in " + this);
            }
    
            // Iterate over a copy to allow for init methods which in turn register new bean definitions.
            // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
            List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
    
            // Trigger initialization of all non-lazy singleton beans...
            for (String beanName : beanNames) {
                RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
                // 只有 singleton 才会进入
                if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                    if (isFactoryBean(beanName)) {
                        final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                        boolean isEagerInit;
                        if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                            isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                                @Override
                                public Boolean run() {
                                    return ((SmartFactoryBean<?>) factory).isEagerInit();
                                }
                            }, getAccessControlContext());
                        }
                        else {
                            isEagerInit = (factory instanceof SmartFactoryBean &&
                                    ((SmartFactoryBean<?>) factory).isEagerInit());
                        }
                        if (isEagerInit) {
                            getBean(beanName);
                        }
                    }
                    else {
                        // 明显不是 factoryBean,只是采用继承 RequestMappingHandlerMapping 的方式而已,所以仅仅是普通bean获取而已
                        getBean(beanName);
                    }
                }
            }
    
            // 如下只是给 SmartInitializingSingleton 的一条绿色通道而已,忽略
            // Trigger post-initialization callback for all applicable beans...
            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() {
                                smartSingleton.afterSingletonsInstantiated();
                                return null;
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        smartSingleton.afterSingletonsInstantiated();
                    }
                }
            }
        }

    下面我们还是简单过一下 beans 组件的加载流程吧,下面是扫略时间:

    // org.springframework.beans.factory.support.DefaultListableBeanFactory
    // org.springframework.beans.factory.support.AbstractBeanFactory
        public Object getBean(String name) throws BeansException {
            return doGetBean(name, null, null, false);
        }
    
        /**
         * Return an instance, which may be shared or independent, of the specified bean.
         * @param name the name of the bean to retrieve
         * @param requiredType the required type of the bean to retrieve
         * @param args arguments to use when creating a bean instance using explicit arguments
         * (only applied when creating a new instance as opposed to retrieving an existing one)
         * @param typeCheckOnly whether the instance is obtained for a type check,
         * not for actual use
         * @return an instance of the bean
         * @throws BeansException if the bean could not be created
         */
        @SuppressWarnings("unchecked")
        protected <T> T doGetBean(
                final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
                throws BeansException {
    
            final String beanName = transformedBeanName(name);
            Object bean;
    
            // Eagerly check singleton cache for manually registered singletons.
            Object sharedInstance = getSingleton(beanName);
            if (sharedInstance != null && args == null) {
                if (logger.isDebugEnabled()) {
                    if (isSingletonCurrentlyInCreation(beanName)) {
                        logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                                "' that is not fully initialized yet - a consequence of a circular reference");
                    }
                    else {
                        logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                    }
                }
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
            }
    
            else {
                // Fail if we're already creating this bean instance:
                // We're assumably within a circular reference.
                if (isPrototypeCurrentlyInCreation(beanName)) {
                    throw new BeanCurrentlyInCreationException(beanName);
                }
    
                // Check if bean definition exists in this factory.
                BeanFactory parentBeanFactory = getParentBeanFactory();
                if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                    // Not found -> check parent.
                    String nameToLookup = originalBeanName(name);
                    if (args != null) {
                        // Delegation to parent with explicit args.
                        return (T) parentBeanFactory.getBean(nameToLookup, args);
                    }
                    else {
                        // No args -> delegate to standard getBean method.
                        return parentBeanFactory.getBean(nameToLookup, requiredType);
                    }
                }
    
                if (!typeCheckOnly) {
                    markBeanAsCreated(beanName);
                }
    
                try {
                    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                    checkMergedBeanDefinition(mbd, beanName, args);
    
                    // Guarantee initialization of beans that the current bean depends on.
                    String[] dependsOn = mbd.getDependsOn();
                    if (dependsOn != null) {
                        for (String dep : dependsOn) {
                            if (isDependent(beanName, dep)) {
                                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                            }
                            registerDependentBean(dep, beanName);
                            try {
                                getBean(dep);
                            }
                            catch (NoSuchBeanDefinitionException ex) {
                                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                            }
                        }
                    }
    
                    // 单例的创建肯定是走到这里了
                    // Create bean instance.
                    if (mbd.isSingleton()) {
                        // 单例创建
                        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                            @Override
                            public Object getObject() throws BeansException {
                                try {
                                    // 稍后回调
                                    return createBean(beanName, mbd, args);
                                }
                                catch (BeansException ex) {
                                    // Explicitly remove instance from singleton cache: It might have been put there
                                    // eagerly by the creation process, to allow for circular reference resolution.
                                    // Also remove any beans that received a temporary reference to the bean.
                                    destroySingleton(beanName);
                                    throw ex;
                                }
                            }
                        });
                        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                    }
    
                    else if (mbd.isPrototype()) {
                        // It's a prototype -> create a new instance.
                        Object prototypeInstance = null;
                        try {
                            beforePrototypeCreation(beanName);
                            prototypeInstance = createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                        bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                    }
    
                    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() throws BeansException {
                                    beforePrototypeCreation(beanName);
                                    try {
                                        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);
                    throw ex;
                }
            }
    
            // Check if required type matches the type of the actual bean instance.
            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 new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                }
            }
            return (T) bean;
        }
        
        // org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
        /**
         * Return the (raw) singleton object registered under the given name,
         * creating and registering a new one if none registered yet.
         * @param beanName the name of the bean
         * @param singletonFactory the ObjectFactory to lazily create the singleton
         * with, if necessary
         * @return the registered singleton object
         */
        public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
            Assert.notNull(beanName, "'beanName' must not be null");
            synchronized (this.singletonObjects) {
                Object singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    if (this.singletonsCurrentlyInDestruction) {
                        throw new BeanCreationNotAllowedException(beanName,
                                "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                                "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                    }
                    beforeSingletonCreation(beanName);
                    boolean newSingleton = false;
                    boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = new LinkedHashSet<Exception>();
                    }
                    try {
                        // 调用上面创建的匿名类
                        singletonObject = singletonFactory.getObject();
                        newSingleton = true;
                    }
                    catch (IllegalStateException ex) {
                        // Has the singleton object implicitly appeared in the meantime ->
                        // if yes, proceed with it since the exception indicates that state.
                        singletonObject = this.singletonObjects.get(beanName);
                        if (singletonObject == null) {
                            throw ex;
                        }
                    }
                    catch (BeanCreationException ex) {
                        if (recordSuppressedExceptions) {
                            for (Exception suppressedException : this.suppressedExceptions) {
                                ex.addRelatedCause(suppressedException);
                            }
                        }
                        throw ex;
                    }
                    finally {
                        if (recordSuppressedExceptions) {
                            this.suppressedExceptions = null;
                        }
                        afterSingletonCreation(beanName);
                    }
                    if (newSingleton) {
                        addSingleton(beanName, singletonObject);
                    }
                }
                return (singletonObject != NULL_OBJECT ? singletonObject : null);
            }
        }
        
        // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
        /**
         * Central method of this class: creates a bean instance,
         * populates the bean instance, applies post-processors, etc.
         * @see #doCreateBean
         */
        @Override
        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 bean class is actually resolved at this point, and
            // clone the bean definition in case of a dynamically resolved Class
            // which cannot be stored in the shared merged bean definition.
            Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
            if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
                mbdToUse = new RootBeanDefinition(mbd);
                mbdToUse.setBeanClass(resolvedClass);
            }
    
            // Prepare method overrides.
            try {
                mbdToUse.prepareMethodOverrides();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                        beanName, "Validation of method overrides failed", ex);
            }
    
            try {
                // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
                Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
                if (bean != null) {
                    return bean;
                }
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                        "BeanPostProcessor before instantiation of bean failed", ex);
            }
    
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isDebugEnabled()) {
                logger.debug("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        
        /**
         * Actually create the specified bean. Pre-creation processing has already happened
         * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
         * <p>Differentiates between default bean instantiation, use of a
         * factory method, and autowiring a constructor.
         * @param beanName the name of the bean
         * @param mbd the merged bean definition for the bean
         * @param args explicit arguments to use for constructor or factory method invocation
         * @return a new instance of the bean
         * @throws BeanCreationException if the bean could not be created
         * @see #instantiateBean
         * @see #instantiateUsingFactoryMethod
         * @see #autowireConstructor
         */
        protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
                throws BeanCreationException {
    
            // Instantiate the bean.
            BeanWrapper instanceWrapper = null;
            if (mbd.isSingleton()) {
                instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
            }
            if (instanceWrapper == null) {
                instanceWrapper = createBeanInstance(beanName, mbd, args);
            }
            final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
            Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
            mbd.resolvedTargetType = beanType;
    
            // Allow post-processors to modify the merged bean definition.
            synchronized (mbd.postProcessingLock) {
                if (!mbd.postProcessed) {
                    try {
                        applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                    }
                    catch (Throwable ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Post-processing of merged bean definition failed", ex);
                    }
                    mbd.postProcessed = true;
                }
            }
    
            // Eagerly cache singletons to be able to resolve circular references
            // even when triggered by lifecycle interfaces like BeanFactoryAware.
            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");
                }
                addSingletonFactory(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        return getEarlyBeanReference(beanName, mbd, bean);
                    }
                });
            }
    
            // Initialize the bean instance.
            Object exposedObject = bean;
            try {
                // 此处允许应用再次更改属性值
                populateBean(beanName, mbd, instanceWrapper);
                if (exposedObject != null) {
                    // 初始化 bean,关键就要来了
                    exposedObject = 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);
                }
            }
    
            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 bean as disposable.
            try {
                registerDisposableBeanIfNecessary(beanName, bean, mbd);
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
            }
    
            return exposedObject;
        }
    
        
        // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
        /**
         * Initialize the given bean instance, applying factory callbacks
         * as well as init methods and bean post processors.
         * <p>Called from {@link #createBean} for traditionally defined beans,
         * and from {@link #initializeBean} for existing bean instances.
         * @param beanName the bean name in the factory (for debugging purposes)
         * @param bean the new bean instance we may need to initialize
         * @param mbd the bean definition that the bean was created with
         * (can also be {@code null}, if given an existing bean instance)
         * @return the initialized bean instance (potentially wrapped)
         * @see BeanNameAware
         * @see BeanClassLoaderAware
         * @see BeanFactoryAware
         * @see #applyBeanPostProcessorsBeforeInitialization
         * @see #invokeInitMethods
         * @see #applyBeanPostProcessorsAfterInitialization
         */
        protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    @Override
                    public Object run() {
                        invokeAwareMethods(beanName, bean);
                        return null;
                    }
                }, getAccessControlContext());
            }
            else {
                // Aware 接口处理
                invokeAwareMethods(beanName, bean);
            }
    
            Object wrappedBean = bean;
            if (mbd == null || !mbd.isSynthetic()) {
                wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
            }
    
            try {
                // invoke 初始化方法,已经快到尾声了,还不见暴露url
                invokeInitMethods(beanName, wrappedBean, mbd);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(
                        (mbd != null ? mbd.getResourceDescription() : null),
                        beanName, "Invocation of init method failed", ex);
            }
            if (mbd == null || !mbd.isSynthetic()) {
                wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
            }
            return wrappedBean;
        }
        
    View Code

    如上,看到了 bean 中调用了 invokeAwareMethods(), 看起来是想做点什么了!

        // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
        
        /**
         * Give a bean a chance to react now all its properties are set,
         * and a chance to know about its owning bean factory (this object).
         * This means checking whether the bean implements InitializingBean or defines
         * a custom init method, and invoking the necessary callback(s) if it does.
         * @param beanName the bean name in the factory (for debugging purposes)
         * @param bean the new bean instance we may need to initialize
         * @param mbd the merged bean definition that the bean was created with
         * (can also be {@code null}, if given an existing bean instance)
         * @throws Throwable if thrown by init methods or by the invocation process
         * @see #invokeCustomInitMethod
         */
        protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
                throws Throwable {
    
            boolean isInitializingBean = (bean instanceof InitializingBean);
            if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
                }
                if (System.getSecurityManager() != null) {
                    try {
                        AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                            @Override
                            public Object run() throws Exception {
                                ((InitializingBean) bean).afterPropertiesSet();
                                return null;
                            }
                        }, getAccessControlContext());
                    }
                    catch (PrivilegedActionException pae) {
                        throw pae.getException();
                    }
                }
                else {
                    // 初始化完成后回调,应该可以自定义一些东西了,url注册在此加入
                    ((InitializingBean) bean).afterPropertiesSet();
                }
            }
    
            if (mbd != null) {
                String initMethodName = mbd.getInitMethodName();
                if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                        !mbd.isExternallyManagedInitMethod(initMethodName)) {
                    invokeCustomInitMethod(beanName, bean, mbd);
                }
            }
        }

    最终有用的,好像就只有 afterPropertiedsSet()了,来看一下 RequestMappingHandlerMapping.afterPropertiesSet() 中都做了啥?但是不管怎么样,是时候展示真正的技术了!

        // org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
        @Override
        public void afterPropertiesSet() {
            this.config = new RequestMappingInfo.BuilderConfiguration();
            this.config.setUrlPathHelper(getUrlPathHelper());
            this.config.setPathMatcher(getPathMatcher());
            this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
            this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
            this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
            this.config.setContentNegotiationManager(getContentNegotiationManager());
    
            // 调用父类 afterPropertiesSet()
            super.afterPropertiesSet();
        }
        
        // org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
        /**
         * Detects handler methods at initialization.
         */
        @Override
        public void afterPropertiesSet() {
            initHandlerMethods();
        }
    
        /**
         * Scan beans in the ApplicationContext, detect and register handler methods.
         * @see #isHandler(Class)
         * @see #getMappingForMethod(Method, Class)
         * @see #handlerMethodsInitialized(Map)
         */
        protected void initHandlerMethods() {
            if (logger.isDebugEnabled()) {
                logger.debug("Looking for request mappings in application context: " + getApplicationContext());
            }
            String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                    BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                    getApplicationContext().getBeanNamesForType(Object.class));
    
            // 可以说,此处会取出所有的beanName, 挺费的,但是也是最全的,数量可能是成百上千
            for (String beanName : beanNames) {
                if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                    Class<?> beanType = null;
                    try {
                        beanType = getApplicationContext().getType(beanName);
                    }
                    catch (Throwable ex) {
                        // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                        if (logger.isDebugEnabled()) {
                            logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                        }
                    }
                    // isHandler() 为子类实现的方法,如 RequestMappingHandlerMapping 中为检测 Controller,RequestMapping 注解
                    if (beanType != null && isHandler(beanType)) {
                        // 检测 handler 设置,暴露 url
                        detectHandlerMethods(beanName);
                    }
                }
            }
            // 最后,再给一次机会给子类,处理 handler 处理完成的事,默认为空
            handlerMethodsInitialized(getHandlerMethods());
        }

    如上检测是否是需要进行url处理,有一个关键点: isHandler(), 如下:

        // RequestMappingHandlerMapping.isHandler() 
        /**
         * {@inheritDoc}
         * <p>Expects a handler to have either a type-level @{@link Controller}
         * annotation or a type-level @{@link RequestMapping} annotation.
         */
        @Override
        protected boolean isHandler(Class<?> beanType) {
            // 检测是否是需要处理 requestMapping, controller, 像 RestController 这样的注解,则是继承了多个注解得到,因此并不违背该检测
            return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                    AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
        }

    而 detectHandlerMethods() 方法,则是检测 controller 中具体有多少方法是需要映射出去的:

        // org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
        /**
         * Look for handler methods in a handler.
         * @param handler the bean name of a handler or a handler instance
         */
        protected void detectHandlerMethods(final Object handler) {
            Class<?> handlerType = (handler instanceof String ?
                    getApplicationContext().getType((String) handler) : handler.getClass());
            final Class<?> userType = ClassUtils.getUserClass(handlerType);
    
            // 将方法列举,选出需要处理的方法
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    new MethodIntrospector.MetadataLookup<T>() {
                        @Override
                        // 被 MethodIntrospector.doWith() 验证调用
                        public T inspect(Method method) {
                            try {
                                // 创建完成后,会有一次验证过程,调用自定义的 url 处理
                                return getMappingForMethod(method, userType);
                            }
                            catch (Throwable ex) {
                                throw new IllegalStateException("Invalid mapping on handler class [" +
                                        userType.getName() + "]: " + method, ex);
                            }
                        }
                    });
    
            if (logger.isDebugEnabled()) {
                logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
            }
            for (Map.Entry<Method, T> entry : methods.entrySet()) {
                Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
                T mapping = entry.getValue();
                // 循环暴露 url handler
                registerHandlerMethod(handler, invocableMethod, mapping);
            }
        }

    总算到暴露url的时候了,registerHandlerMethod(), 向多个变量中插入url,使后续可以多方位查找: mappingLookup, urlLookup, nameLookup, corsLookup, registry ...

      真是一口气把能做的都给做了啊!

      其中,暴露一些方法给子类进行自定义即在此处。

        // AbstractHandlerMethodMapping.registerHandlerMethod()
        /**
         * Register a handler method and its unique mapping. Invoked at startup for
         * each detected handler method.
         * @param handler the bean name of the handler or the handler instance
         * @param method the method to register
         * @param mapping the mapping conditions associated with the handler method
         * @throws IllegalStateException if another method was already registered
         * under the same mapping
         */
        protected void registerHandlerMethod(Object handler, Method method, T mapping) {
            this.mappingRegistry.register(mapping, handler, method);
        }
    
        // org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry
            public void register(T mapping, Object handler, Method method) {
                this.readWriteLock.writeLock().lock();
                try {
                    // 调用自定义方法,进行方法映射
                    HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                    // 检测映射是否存在重复
                    assertUniqueMethodMapping(handlerMethod, mapping);
    
                    if (logger.isInfoEnabled()) {
                        logger.info("Mapped "" + mapping + "" onto " + handlerMethod);
                    }
                    // 将 handlerMethod 放入 mappingLookup 中
                    this.mappingLookup.put(mapping, handlerMethod);
    
                    // 给使用者自定义路径留入口 
                    List<String> directUrls = getDirectUrls(mapping);
                    for (String url : directUrls) {
                        // 可能存在多个路径映射一个 handlerMethod 情况
                        this.urlLookup.add(url, mapping);
                    }
    
                    String name = null;
                    if (getNamingStrategy() != null) {
                        name = getNamingStrategy().getName(handlerMethod, mapping);
                        addMappingName(name, handlerMethod);
                    }
    
                    CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                    if (corsConfig != null) {
                        this.corsLookup.put(handlerMethod, corsConfig);
                    }
    
                    this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
                }
                finally {
                    this.readWriteLock.writeLock().unlock();
                }
            }
        

      这样一来,我们的 mapping 中就多出了许多的映射了,下次来请求的时候,只要在其中进行查询,即可知道需要使用什么样的方法了。

      可以看到,controller 的bean, 其实和其他bean也是一样的,只不它作为提供服务的一线,需要留下线索,所以对 afterPropertiesSet() 之后,多做了点事!这了是很多第三方框架的重要接入点之一。

      spring 作为框架,也确实给应用留足了空间,在任何可能需要的地方,都可以自行定义!

      不过,也因为这样,框架也在做许多很费力的事,比如循环中根本不考虑效率问题,反正我们机器也不在乎这点!

  • 相关阅读:
    Shell中调用、引用、包含另一个脚本文件的三种方法
    mysql基础
    传智博客(JavaWeb方面的所有知识)听课记录(经典)
    nginx配置负载均衡与反向代理
    nginx 详解
    iOS开发之集成ijkplayer视频直播
    Nginx配置文件nginx.conf中文详解(总结)
    WorldWind源码剖析系列:数学引擎类MathEngine
    WorldWind源码剖析系列:二维点类Point2d和三维点类Point3d
    WorldWind源码剖析系列:枚举类型
  • 原文地址:https://www.cnblogs.com/yougewe/p/9960541.html
Copyright © 2020-2023  润新知