SpringBoot源码解析(五)

Spring的是如何内置Tomcat的?

首先引入maven依赖,依赖可以定义为插件。就是Tomcat插件或者说是web容器。

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
        </dependency>

看一下引入的是Tomcat什么版本

    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-annotations-api</artifactId>
      <version>9.0.48</version>
      <scope>compile</scope>
    </dependency>

SpringBoot打包方式定义

	<packaging>jar</packaging>

内置Tomcat源码,所以每启动一个springboot也也会同时启动一个Tomcat。使springboot作为web进行发布。

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.catalina.core;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.naming.NamingException;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.ServletRegistration.Dynamic;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestListener;
import javax.servlet.ServletSecurityElement;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
import javax.servlet.annotation.ServletSecurity;
import javax.servlet.descriptor.JspConfigDescriptor;
import javax.servlet.http.HttpServletMapping;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionIdListener;
import javax.servlet.http.HttpSessionListener;

import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Service;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.mapper.MappingData;
import org.apache.catalina.util.Introspection;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.util.URLEncoder;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.CharChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.buf.UDecoder;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.http.RequestUtil;
import org.apache.tomcat.util.res.StringManager;


/**
 * Standard implementation of <code>ServletContext</code> that represents
 * a web application's execution environment.  An instance of this class is
 * associated with each instance of <code>StandardContext</code>.
 *
 * @author Craig R. McClanahan
 * @author Remy Maucherat
 */
public class ApplicationContext implements ServletContext {

    protected static final boolean STRICT_SERVLET_COMPLIANCE;

    protected static final boolean GET_RESOURCE_REQUIRE_SLASH;


    static {
        STRICT_SERVLET_COMPLIANCE = Globals.STRICT_SERVLET_COMPLIANCE;

        String requireSlash = System.getProperty(
                "org.apache.catalina.core.ApplicationContext.GET_RESOURCE_REQUIRE_SLASH");
        if (requireSlash == null) {
            GET_RESOURCE_REQUIRE_SLASH = STRICT_SERVLET_COMPLIANCE;
        } else {
            GET_RESOURCE_REQUIRE_SLASH = Boolean.parseBoolean(requireSlash);
        }
    }

    // ----------------------------------------------------------- Constructors


    /**
     * Construct a new instance of this class, associated with the specified
     * Context instance.
     *
     * @param context The associated Context instance
     */
    public ApplicationContext(StandardContext context) {
        super();
        this.context = context;
        this.service = ((Engine) context.getParent().getParent()).getService();
        this.sessionCookieConfig = new ApplicationSessionCookieConfig(context);

        // Populate session tracking modes
        populateSessionTrackingModes();
    }


    // ----------------------------------------------------- Instance Variables


    /**
     * The context attributes for this context.
     */
    protected Map<String,Object> attributes = new ConcurrentHashMap<>();


    /**
     * List of read only attributes for this context.
     */
    private final Map<String,String> readOnlyAttributes = new ConcurrentHashMap<>();


    /**
     * The Context instance with which we are associated.
     */
    private final StandardContext context;


    /**
     * The Service instance with which we are associated.
     */
    private final Service service;


    /**
     * Empty String collection to serve as the basis for empty enumerations.
     */
    private static final List<String> emptyString = Collections.emptyList();


    /**
     * Empty Servlet collection to serve as the basis for empty enumerations.
     */
    private static final List<Servlet> emptyServlet = Collections.emptyList();


    /**
     * The facade around this object.
     */
    private final ServletContext facade = new ApplicationContextFacade(this);


    /**
     * The merged context initialization parameters for this Context.
     */
    private final Map<String,String> parameters = new ConcurrentHashMap<>();


    /**
     * The string manager for this package.
     */
    private static final StringManager sm = StringManager.getManager(Constants.Package);


    /**
     * Thread local data used during request dispatch.
     */
    private final ThreadLocal<DispatchData> dispatchData = new ThreadLocal<>();


    /**
     * Session Cookie config
     */
    private SessionCookieConfig sessionCookieConfig;

    /**
     * Session tracking modes
     */
    private Set<SessionTrackingMode> sessionTrackingModes = null;
    private Set<SessionTrackingMode> defaultSessionTrackingModes = null;
    private Set<SessionTrackingMode> supportedSessionTrackingModes = null;

    /**
     * Flag that indicates if a new {@link ServletContextListener} may be added
     * to the application. Once the first {@link ServletContextListener} is
     * called, no more may be added.
     */
    private boolean newServletContextListenerAllowed = true;


    // ------------------------------------------------- ServletContext Methods

    @Override
    public Object getAttribute(String name) {
        return attributes.get(name);
    }


    @Override
    public Enumeration<String> getAttributeNames() {
        Set<String> names = new HashSet<>(attributes.keySet());
        return Collections.enumeration(names);
    }


    @Override
    public ServletContext getContext(String uri) {

        // Validate the format of the specified argument
        if (uri == null || !uri.startsWith("/")) {
            return null;
        }

        Context child = null;
        try {
            // Look for an exact match
            Container host = context.getParent();
            child = (Context) host.findChild(uri);

            // Non-running contexts should be ignored.
            if (child != null && !child.getState().isAvailable()) {
                child = null;
            }

            // Remove any version information and use the mapper
            if (child == null) {
                int i = uri.indexOf("##");
                if (i > -1) {
                    uri = uri.substring(0, i);
                }
                // Note: This could be more efficient with a dedicated Mapper
                //       method but such an implementation would require some
                //       refactoring of the Mapper to avoid copy/paste of
                //       existing code.
                MessageBytes hostMB = MessageBytes.newInstance();
                hostMB.setString(host.getName());

                MessageBytes pathMB = MessageBytes.newInstance();
                pathMB.setString(uri);

                MappingData mappingData = new MappingData();
                service.getMapper().map(hostMB, pathMB, null, mappingData);
                child = mappingData.context;
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            return null;
        }

        if (child == null) {
            return null;
        }

        if (context.getCrossContext()) {
            // If crossContext is enabled, can always return the context
            return child.getServletContext();
        } else if (child == context) {
            // Can still return the current context
            return context.getServletContext();
        } else {
            // Nothing to return
            return null;
        }
    }


    @Override
    public String getContextPath() {
        return context.getPath();
    }


    @Override
    public String getInitParameter(final String name) {
        // Special handling for XML settings as the context setting must
        // always override anything that might have been set by an application.
        if (Globals.JASPER_XML_VALIDATION_TLD_INIT_PARAM.equals(name) &&
                context.getTldValidation()) {
            return "true";
        }
        if (Globals.JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM.equals(name)) {
            if (!context.getXmlBlockExternal()) {
                // System admin has explicitly changed the default
                return "false";
            }
        }
        return parameters.get(name);
    }


    @Override
    public Enumeration<String> getInitParameterNames() {
        Set<String> names = new HashSet<>(parameters.keySet());
        // Special handling for XML settings as these attributes will always be
        // available if they have been set on the context
        if (context.getTldValidation()) {
            names.add(Globals.JASPER_XML_VALIDATION_TLD_INIT_PARAM);
        }
        if (!context.getXmlBlockExternal()) {
            names.add(Globals.JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM);
        }
        return Collections.enumeration(names);
    }


    @Override
    public int getMajorVersion() {
        return Constants.MAJOR_VERSION;
    }


    @Override
    public int getMinorVersion() {
        return Constants.MINOR_VERSION;
    }


    /**
     * Return the MIME type of the specified file, or <code>null</code> if
     * the MIME type cannot be determined.
     *
     * @param file Filename for which to identify a MIME type
     */
    @Override
    public String getMimeType(String file) {

        if (file == null) {
            return null;
        }
        int period = file.lastIndexOf('.');
        if (period < 0) {
            return null;
        }
        String extension = file.substring(period + 1);
        if (extension.length() < 1) {
            return null;
        }
        return context.findMimeMapping(extension);

    }


    /**
     * Return a <code>RequestDispatcher</code> object that acts as a
     * wrapper for the named servlet.
     *
     * @param name Name of the servlet for which a dispatcher is requested
     */
    @Override
    public RequestDispatcher getNamedDispatcher(String name) {

        // Validate the name argument
        if (name == null) {
            return null;
        }

        // Create and return a corresponding request dispatcher
        Wrapper wrapper = (Wrapper) context.findChild(name);
        if (wrapper == null) {
            return null;
        }

        return new ApplicationDispatcher(wrapper, null, null, null, null, null, name);

    }


    @Override
    public String getRealPath(String path) {
        String validatedPath = validateResourcePath(path, true);
        return context.getRealPath(validatedPath);
    }


    @Override
    public RequestDispatcher getRequestDispatcher(final String path) {

        // Validate the path argument
        if (path == null) {
            return null;
        }
        if (!path.startsWith("/")) {
            throw new IllegalArgumentException(
                    sm.getString("applicationContext.requestDispatcher.iae", path));
        }

        // Same processing order as InputBuffer / CoyoteAdapter
        // First remove query string
        String uri;
        String queryString;
        int pos = path.indexOf('?');
        if (pos >= 0) {
            uri = path.substring(0, pos);
            queryString = path.substring(pos + 1);
        } else {
            uri = path;
            queryString = null;
        }

        // Remove path parameters
        String uriNoParams = stripPathParams(uri);

        // Then normalize
        String normalizedUri = RequestUtil.normalize(uriNoParams);
        if (normalizedUri == null) {
            return null;
        }

        // Mapping is against the normalized uri

        if (getContext().getDispatchersUseEncodedPaths()) {
            // Decode
            String decodedUri = UDecoder.URLDecode(normalizedUri, StandardCharsets.UTF_8);

            // Security check to catch attempts to encode /../ sequences
            normalizedUri = RequestUtil.normalize(decodedUri);
            if (!decodedUri.equals(normalizedUri)) {
                getContext().getLogger().warn(
                        sm.getString("applicationContext.illegalDispatchPath", path),
                        new IllegalArgumentException());
                return null;
            }

            // URI needs to include the context path
            uri = URLEncoder.DEFAULT.encode(getContextPath(), StandardCharsets.UTF_8) + uri;
        } else {
            // uri is passed to the constructor for ApplicationDispatcher and is
            // ultimately used as the value for getRequestURI() which returns
            // encoded values. Therefore, since the value passed in for path
            // was decoded, encode uri here.
            uri = URLEncoder.DEFAULT.encode(getContextPath() + uri, StandardCharsets.UTF_8);
        }

        // Use the thread local URI and mapping data
        DispatchData dd = dispatchData.get();
        if (dd == null) {
            dd = new DispatchData();
            dispatchData.set(dd);
        }

        // Use the thread local mapping data
        MessageBytes uriMB = dd.uriMB;
        MappingData mappingData = dd.mappingData;

        try {
            // Map the URI
            CharChunk uriCC = uriMB.getCharChunk();
            try {
                uriCC.append(context.getPath());
                uriCC.append(normalizedUri);
                service.getMapper().map(context, uriMB, mappingData);
                if (mappingData.wrapper == null) {
                    return null;
                }
            } catch (Exception e) {
                // Should never happen
                log(sm.getString("applicationContext.mapping.error"), e);
                return null;
            }

            Wrapper wrapper = mappingData.wrapper;
            String wrapperPath = mappingData.wrapperPath.toString();
            String pathInfo = mappingData.pathInfo.toString();
            HttpServletMapping mapping = new ApplicationMapping(mappingData).getHttpServletMapping();

            // Construct a RequestDispatcher to process this request
            return new ApplicationDispatcher(wrapper, uri, wrapperPath, pathInfo,
                    queryString, mapping, null);
        } finally {
            // Recycle thread local data at the end of the request so references
            // are not held to a completed request as there is potential for
            // that to trigger a memory leak if a context is unloaded. Not
            // strictly necessary here for uriMB but it needs to be recycled at
            // some point so do it here for consistency with mappingData which
            // must be recycled here.
            uriMB.recycle();
            mappingData.recycle();
        }
    }


    // Package private to facilitate testing
    static String stripPathParams(String input) {
        // Shortcut
        if (input.indexOf(';') < 0) {
            return input;
        }

        StringBuilder sb = new StringBuilder(input.length());
        int pos = 0;
        int limit = input.length();
        while (pos < limit) {
            int nextSemiColon = input.indexOf(';', pos);
            if (nextSemiColon < 0) {
                nextSemiColon = limit;
            }
            sb.append(input.substring(pos, nextSemiColon));
            int followingSlash = input.indexOf('/', nextSemiColon);
            if (followingSlash < 0) {
                pos = limit;
            } else {
                pos = followingSlash;
            }
        }

        return sb.toString();
    }


    @Override
    public URL getResource(String path) throws MalformedURLException {

        String validatedPath = validateResourcePath(path, !GET_RESOURCE_REQUIRE_SLASH);

        if (validatedPath == null) {
            throw new MalformedURLException(
                    sm.getString("applicationContext.requestDispatcher.iae", path));
        }

        WebResourceRoot resources = context.getResources();
        if (resources != null) {
            return resources.getResource(validatedPath).getURL();
        }

        return null;
    }


    @Override
    public InputStream getResourceAsStream(String path) {

        String validatedPath = validateResourcePath(path, !GET_RESOURCE_REQUIRE_SLASH);

        if (validatedPath == null) {
            return null;
        }

        WebResourceRoot resources = context.getResources();
        if (resources != null) {
            return resources.getResource(validatedPath).getInputStream();
        }

        return null;
    }


    /*
     * Returns null if the input path is not valid or a path that will be
     * acceptable to resources.getResource().
     */
    private String validateResourcePath(String path, boolean addMissingInitialSlash) {
        if (path == null) {
            return null;
        }

        if (!path.startsWith("/")) {
            if (addMissingInitialSlash) {
                return "/" + path;
            } else {
                return null;
            }
        }

        return path;
    }


    @Override
    public Set<String> getResourcePaths(String path) {

        // Validate the path argument
        if (path == null) {
            return null;
        }
        if (!path.startsWith("/")) {
            throw new IllegalArgumentException (sm.getString("applicationContext.resourcePaths.iae", path));
        }

        WebResourceRoot resources = context.getResources();
        if (resources != null) {
            return resources.listWebAppPaths(path);
        }

        return null;
    }


    @Override
    public String getServerInfo() {
        return ServerInfo.getServerInfo();
    }


    @Override
    @Deprecated
    public Servlet getServlet(String name) {
        return null;
    }


    @Override
    public String getServletContextName() {
        return context.getDisplayName();
    }


    @Override
    @Deprecated
    public Enumeration<String> getServletNames() {
        return Collections.enumeration(emptyString);
    }


    @Override
    @Deprecated
    public Enumeration<Servlet> getServlets() {
        return Collections.enumeration(emptyServlet);
    }


    @Override
    public void log(String message) {
        context.getLogger().info(message);
    }


    @Override
    @Deprecated
    public void log(Exception exception, String message) {
        context.getLogger().error(message, exception);
    }


    @Override
    public void log(String message, Throwable throwable) {
        context.getLogger().error(message, throwable);
    }


    @Override
    public void removeAttribute(String name) {

        Object value = null;

        // Remove the specified attribute
        // Check for read only attribute
        if (readOnlyAttributes.containsKey(name)){
            return;
        }
        value = attributes.remove(name);
        if (value == null) {
            return;
        }

        // Notify interested application event listeners
        Object listeners[] = context.getApplicationEventListeners();
        if ((listeners == null) || (listeners.length == 0)) {
            return;
        }
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(
                context.getServletContext(), name, value);
        for (Object obj : listeners) {
            if (!(obj instanceof ServletContextAttributeListener)) {
                continue;
            }
            ServletContextAttributeListener listener = (ServletContextAttributeListener) obj;
            try {
                context.fireContainerEvent("beforeContextAttributeRemoved", listener);
                listener.attributeRemoved(event);
                context.fireContainerEvent("afterContextAttributeRemoved", listener);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                context.fireContainerEvent("afterContextAttributeRemoved", listener);
                // FIXME - should we do anything besides log these?
                log(sm.getString("applicationContext.attributeEvent"), t);
            }
        }
    }


    @Override
    public void setAttribute(String name, Object value) {
        // Name cannot be null
        if (name == null) {
            throw new NullPointerException(sm.getString("applicationContext.setAttribute.namenull"));
        }

        // Null value is the same as removeAttribute()
        if (value == null) {
            removeAttribute(name);
            return;
        }

        // Add or replace the specified attribute
        // Check for read only attribute
        if (readOnlyAttributes.containsKey(name)) {
            return;
        }

        Object oldValue = attributes.put(name, value);
        boolean replaced = oldValue != null;

        // Notify interested application event listeners
        Object listeners[] = context.getApplicationEventListeners();
        if ((listeners == null) || (listeners.length == 0)) {
            return;
        }
        ServletContextAttributeEvent event = null;
        if (replaced) {
            event = new ServletContextAttributeEvent(context.getServletContext(), name, oldValue);
        } else {
            event = new ServletContextAttributeEvent(context.getServletContext(), name, value);
        }

        for (Object obj : listeners) {
            if (!(obj instanceof ServletContextAttributeListener)) {
                continue;
            }
            ServletContextAttributeListener listener = (ServletContextAttributeListener) obj;
            try {
                if (replaced) {
                    context.fireContainerEvent("beforeContextAttributeReplaced", listener);
                    listener.attributeReplaced(event);
                    context.fireContainerEvent("afterContextAttributeReplaced", listener);
                } else {
                    context.fireContainerEvent("beforeContextAttributeAdded", listener);
                    listener.attributeAdded(event);
                    context.fireContainerEvent("afterContextAttributeAdded", listener);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                if (replaced) {
                    context.fireContainerEvent("afterContextAttributeReplaced", listener);
                } else {
                    context.fireContainerEvent("afterContextAttributeAdded", listener);
                }
                // FIXME - should we do anything besides log these?
                log(sm.getString("applicationContext.attributeEvent"), t);
            }
        }
    }


    @Override
    public FilterRegistration.Dynamic addFilter(String filterName, String className) {
        return addFilter(filterName, className, null);
    }


    @Override
    public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) {
        return addFilter(filterName, null, filter);
    }


    @Override
    public FilterRegistration.Dynamic addFilter(String filterName,
            Class<? extends Filter> filterClass) {
        return addFilter(filterName, filterClass.getName(), null);
    }


    private FilterRegistration.Dynamic addFilter(String filterName,
            String filterClass, Filter filter) throws IllegalStateException {

        if (filterName == null || filterName.equals("")) {
            throw new IllegalArgumentException(sm.getString(
                    "applicationContext.invalidFilterName", filterName));
        }

        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            //TODO Spec breaking enhancement to ignore this restriction
            throw new IllegalStateException(
                    sm.getString("applicationContext.addFilter.ise",
                            getContextPath()));
        }

        FilterDef filterDef = context.findFilterDef(filterName);

        // Assume a 'complete' FilterRegistration is one that has a class and
        // a name
        if (filterDef == null) {
            filterDef = new FilterDef();
            filterDef.setFilterName(filterName);
            context.addFilterDef(filterDef);
        } else {
            if (filterDef.getFilterName() != null &&
                    filterDef.getFilterClass() != null) {
                return null;
            }
        }

        if (filter == null) {
            filterDef.setFilterClass(filterClass);
        } else {
            filterDef.setFilterClass(filter.getClass().getName());
            filterDef.setFilter(filter);
        }

        return new ApplicationFilterRegistration(filterDef, context);
    }


    @Override
    public <T extends Filter> T createFilter(Class<T> c) throws ServletException {
        try {
            @SuppressWarnings("unchecked")
            T filter = (T) context.getInstanceManager().newInstance(c.getName());
            return filter;
        } catch (InvocationTargetException e) {
            ExceptionUtils.handleThrowable(e.getCause());
            throw new ServletException(e);
        } catch (ReflectiveOperationException | NamingException e) {
            throw new ServletException(e);
        }
    }


    @Override
    public FilterRegistration getFilterRegistration(String filterName) {
        FilterDef filterDef = context.findFilterDef(filterName);
        if (filterDef == null) {
            return null;
        }
        return new ApplicationFilterRegistration(filterDef, context);
    }


    @Override
    public ServletRegistration.Dynamic addServlet(String servletName, String className) {
        return addServlet(servletName, className, null, null);
    }


    @Override
    public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) {
        return addServlet(servletName, null, servlet, null);
    }


    @Override
    public ServletRegistration.Dynamic addServlet(String servletName,
            Class<? extends Servlet> servletClass) {
        return addServlet(servletName, servletClass.getName(), null, null);
    }


    @Override
    public Dynamic addJspFile(String jspName, String jspFile) {

        // jspName is validated in addServlet()
        if (jspFile == null || !jspFile.startsWith("/")) {
            throw new IllegalArgumentException(
                    sm.getString("applicationContext.addJspFile.iae", jspFile));
        }

        String jspServletClassName = null;
        Map<String,String> jspFileInitParams = new HashMap<>();

        Wrapper jspServlet = (Wrapper) context.findChild("jsp");

        if (jspServlet == null) {
            // No JSP servlet currently defined.
            // Use default JSP Servlet class name
            jspServletClassName = Constants.JSP_SERVLET_CLASS;
        } else {
            // JSP Servlet defined.
            // Use same JSP Servlet class name
            jspServletClassName = jspServlet.getServletClass();
            // Use same init parameters
            String[] params = jspServlet.findInitParameters();
            for (String param : params) {
                jspFileInitParams.put(param, jspServlet.findInitParameter(param));
            }
        }

        // Add init parameter to specify JSP file
        jspFileInitParams.put("jspFile", jspFile);

        return addServlet(jspName, jspServletClassName, null, jspFileInitParams);
    }


    private ServletRegistration.Dynamic addServlet(String servletName, String servletClass,
            Servlet servlet, Map<String,String> initParams) throws IllegalStateException {

        if (servletName == null || servletName.equals("")) {
            throw new IllegalArgumentException(sm.getString(
                    "applicationContext.invalidServletName", servletName));
        }

        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            //TODO Spec breaking enhancement to ignore this restriction
            throw new IllegalStateException(
                    sm.getString("applicationContext.addServlet.ise",
                            getContextPath()));
        }

        Wrapper wrapper = (Wrapper) context.findChild(servletName);

        // Assume a 'complete' ServletRegistration is one that has a class and
        // a name
        if (wrapper == null) {
            wrapper = context.createWrapper();
            wrapper.setName(servletName);
            context.addChild(wrapper);
        } else {
            if (wrapper.getName() != null &&
                    wrapper.getServletClass() != null) {
                if (wrapper.isOverridable()) {
                    wrapper.setOverridable(false);
                } else {
                    return null;
                }
            }
        }

        ServletSecurity annotation = null;
        if (servlet == null) {
            wrapper.setServletClass(servletClass);
            Class<?> clazz = Introspection.loadClass(context, servletClass);
            if (clazz != null) {
                annotation = clazz.getAnnotation(ServletSecurity.class);
            }
        } else {
            wrapper.setServletClass(servlet.getClass().getName());
            wrapper.setServlet(servlet);
            if (context.wasCreatedDynamicServlet(servlet)) {
                annotation = servlet.getClass().getAnnotation(ServletSecurity.class);
            }
        }

        if (initParams != null) {
            for (Map.Entry<String, String> initParam: initParams.entrySet()) {
                wrapper.addInitParameter(initParam.getKey(), initParam.getValue());
            }
        }

        ServletRegistration.Dynamic registration =
                new ApplicationServletRegistration(wrapper, context);
        if (annotation != null) {
            registration.setServletSecurity(new ServletSecurityElement(annotation));
        }
        return registration;
    }


    @Override
    public <T extends Servlet> T createServlet(Class<T> c) throws ServletException {
        try {
            @SuppressWarnings("unchecked")
            T servlet = (T) context.getInstanceManager().newInstance(c.getName());
            context.dynamicServletCreated(servlet);
            return servlet;
        } catch (InvocationTargetException e) {
            ExceptionUtils.handleThrowable(e.getCause());
            throw new ServletException(e);
        } catch (ReflectiveOperationException | NamingException e) {
            throw new ServletException(e);
        }
    }


    @Override
    public ServletRegistration getServletRegistration(String servletName) {
        Wrapper wrapper = (Wrapper) context.findChild(servletName);
        if (wrapper == null) {
            return null;
        }

        return new ApplicationServletRegistration(wrapper, context);
    }


    @Override
    public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
        return defaultSessionTrackingModes;
    }


    private void populateSessionTrackingModes() {
        // URL re-writing is always enabled by default
        defaultSessionTrackingModes = EnumSet.of(SessionTrackingMode.URL);
        supportedSessionTrackingModes = EnumSet.of(SessionTrackingMode.URL);

        if (context.getCookies()) {
            defaultSessionTrackingModes.add(SessionTrackingMode.COOKIE);
            supportedSessionTrackingModes.add(SessionTrackingMode.COOKIE);
        }

        // SSL not enabled by default as it can only used on its own
        // Context > Host > Engine > Service
        Connector[] connectors = service.findConnectors();
        // Need at least one SSL enabled connector to use the SSL session ID.
        for (Connector connector : connectors) {
            if (Boolean.TRUE.equals(connector.getProperty("SSLEnabled"))) {
                supportedSessionTrackingModes.add(SessionTrackingMode.SSL);
                break;
            }
        }
    }


    @Override
    public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
        if (sessionTrackingModes != null) {
            return sessionTrackingModes;
        }
        return defaultSessionTrackingModes;
    }


    @Override
    public SessionCookieConfig getSessionCookieConfig() {
        return sessionCookieConfig;
    }


    @Override
    public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes) {

        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            throw new IllegalStateException(
                    sm.getString("applicationContext.setSessionTracking.ise",
                            getContextPath()));
        }

        // Check that only supported tracking modes have been requested
        for (SessionTrackingMode sessionTrackingMode : sessionTrackingModes) {
            if (!supportedSessionTrackingModes.contains(sessionTrackingMode)) {
                throw new IllegalArgumentException(sm.getString(
                        "applicationContext.setSessionTracking.iae.invalid",
                        sessionTrackingMode.toString(), getContextPath()));
            }
        }

        // Check SSL has not be configured with anything else
        if (sessionTrackingModes.contains(SessionTrackingMode.SSL)) {
            if (sessionTrackingModes.size() > 1) {
                throw new IllegalArgumentException(sm.getString(
                        "applicationContext.setSessionTracking.iae.ssl",
                        getContextPath()));
            }
        }

        this.sessionTrackingModes = sessionTrackingModes;
    }


    @Override
    public boolean setInitParameter(String name, String value) {
        // Name cannot be null
        if (name == null) {
            throw new NullPointerException(sm.getString("applicationContext.setAttribute.namenull"));
        }
        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            throw new IllegalStateException(
                    sm.getString("applicationContext.setInitParam.ise",
                            getContextPath()));
        }

        return parameters.putIfAbsent(name, value) == null;
    }


    @Override
    public void addListener(Class<? extends EventListener> listenerClass) {
        EventListener listener;
        try {
            listener = createListener(listenerClass);
        } catch (ServletException e) {
            throw new IllegalArgumentException(sm.getString(
                    "applicationContext.addListener.iae.init",
                    listenerClass.getName()), e);
        }
        addListener(listener);
    }


    @Override
    public void addListener(String className) {

        try {
            if (context.getInstanceManager() != null) {
                Object obj = context.getInstanceManager().newInstance(className);

                if (!(obj instanceof EventListener)) {
                    throw new IllegalArgumentException(sm.getString(
                            "applicationContext.addListener.iae.wrongType",
                            className));
                }

                EventListener listener = (EventListener) obj;
                addListener(listener);
            }
        } catch (InvocationTargetException e) {
            ExceptionUtils.handleThrowable(e.getCause());
            throw new IllegalArgumentException(sm.getString(
                    "applicationContext.addListener.iae.cnfe", className),
                    e);
        } catch (ReflectiveOperationException| NamingException e) {
            throw new IllegalArgumentException(sm.getString(
                    "applicationContext.addListener.iae.cnfe", className),
                    e);
        }

    }


    @Override
    public <T extends EventListener> void addListener(T t) {
        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            throw new IllegalStateException(
                    sm.getString("applicationContext.addListener.ise",
                            getContextPath()));
        }

        boolean match = false;
        if (t instanceof ServletContextAttributeListener ||
                t instanceof ServletRequestListener ||
                t instanceof ServletRequestAttributeListener ||
                t instanceof HttpSessionIdListener ||
                t instanceof HttpSessionAttributeListener) {
            context.addApplicationEventListener(t);
            match = true;
        }

        if (t instanceof HttpSessionListener ||
                (t instanceof ServletContextListener && newServletContextListenerAllowed)) {
            // Add listener directly to the list of instances rather than to
            // the list of class names.
            context.addApplicationLifecycleListener(t);
            match = true;
        }

        if (match) {
            return;
        }

        if (t instanceof ServletContextListener) {
            throw new IllegalArgumentException(sm.getString(
                    "applicationContext.addListener.iae.sclNotAllowed",
                    t.getClass().getName()));
        } else {
            throw new IllegalArgumentException(sm.getString(
                    "applicationContext.addListener.iae.wrongType",
                    t.getClass().getName()));
        }
    }


    @Override
    public <T extends EventListener> T createListener(Class<T> c)
            throws ServletException {
        try {
            @SuppressWarnings("unchecked")
            T listener = (T) context.getInstanceManager().newInstance(c);
            if (listener instanceof ServletContextListener ||
                    listener instanceof ServletContextAttributeListener ||
                    listener instanceof ServletRequestListener ||
                    listener instanceof ServletRequestAttributeListener ||
                    listener instanceof HttpSessionListener ||
                    listener instanceof HttpSessionIdListener ||
                    listener instanceof HttpSessionAttributeListener) {
                return listener;
            }
            throw new IllegalArgumentException(sm.getString(
                    "applicationContext.addListener.iae.wrongType",
                    listener.getClass().getName()));
        } catch (InvocationTargetException e) {
            ExceptionUtils.handleThrowable(e.getCause());
            throw new ServletException(e);
        } catch (ReflectiveOperationException | NamingException e) {
            throw new ServletException(e);
        }
    }


    @Override
    public void declareRoles(String... roleNames) {

        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            //TODO Spec breaking enhancement to ignore this restriction
            throw new IllegalStateException(
                    sm.getString("applicationContext.addRole.ise",
                            getContextPath()));
        }

        if (roleNames == null) {
            throw new IllegalArgumentException(
                    sm.getString("applicationContext.roles.iae",
                            getContextPath()));
        }

        for (String role : roleNames) {
            if (role == null || role.isEmpty()) {
                throw new IllegalArgumentException(
                        sm.getString("applicationContext.role.iae",
                                getContextPath()));
            }
            context.addSecurityRole(role);
        }
    }


    @Override
    public ClassLoader getClassLoader() {
        ClassLoader result = context.getLoader().getClassLoader();
        if (Globals.IS_SECURITY_ENABLED) {
            ClassLoader tccl = Thread.currentThread().getContextClassLoader();
            ClassLoader parent = result;
            while (parent != null) {
                if (parent == tccl) {
                    break;
                }
                parent = parent.getParent();
            }
            if (parent == null) {
                System.getSecurityManager().checkPermission(
                        new RuntimePermission("getClassLoader"));
            }
        }

        return result;
    }


    @Override
    public int getEffectiveMajorVersion() {
        return context.getEffectiveMajorVersion();
    }


    @Override
    public int getEffectiveMinorVersion() {
        return context.getEffectiveMinorVersion();
    }


    @Override
    public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
        Map<String, ApplicationFilterRegistration> result = new HashMap<>();

        FilterDef[] filterDefs = context.findFilterDefs();
        for (FilterDef filterDef : filterDefs) {
            result.put(filterDef.getFilterName(),
                    new ApplicationFilterRegistration(filterDef, context));
        }

        return result;
    }


    @Override
    public JspConfigDescriptor getJspConfigDescriptor() {
        return context.getJspConfigDescriptor();
    }


    @Override
    public Map<String, ? extends ServletRegistration> getServletRegistrations() {
        Map<String, ApplicationServletRegistration> result = new HashMap<>();

        Container[] wrappers = context.findChildren();
        for (Container wrapper : wrappers) {
            result.put(wrapper.getName(),
                    new ApplicationServletRegistration(
                            (Wrapper) wrapper, context));
        }

        return result;
    }


    @Override
    public String getVirtualServerName() {
        // Constructor will fail if context or its parent is null
        Container host = context.getParent();
        Container engine = host.getParent();
        return engine.getName() + "/" + host.getName();
    }


    @Override
    public int getSessionTimeout() {
        return context.getSessionTimeout();
    }


    @Override
    public void setSessionTimeout(int sessionTimeout) {
        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            throw new IllegalStateException(
                    sm.getString("applicationContext.setSessionTimeout.ise",
                            getContextPath()));
        }

        context.setSessionTimeout(sessionTimeout);
    }


    @Override
    public String getRequestCharacterEncoding() {
        return context.getRequestCharacterEncoding();
    }


    @Override
    public void setRequestCharacterEncoding(String encoding) {
        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            throw new IllegalStateException(
                    sm.getString("applicationContext.setRequestEncoding.ise",
                            getContextPath()));
        }

        context.setRequestCharacterEncoding(encoding);
    }


    @Override
    public String getResponseCharacterEncoding() {
        return context.getResponseCharacterEncoding();
    }


    @Override
    public void setResponseCharacterEncoding(String encoding) {
        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            throw new IllegalStateException(
                    sm.getString("applicationContext.setResponseEncoding.ise",
                            getContextPath()));
        }

        context.setResponseCharacterEncoding(encoding);
    }


    // -------------------------------------------------------- Package Methods
    protected StandardContext getContext() {
        return this.context;
    }

    /**
     * Clear all application-created attributes.
     */
    protected void clearAttributes() {

        // Create list of attributes to be removed
        List<String> list = new ArrayList<>(attributes.keySet());

        // Remove application originated attributes
        // (read only attributes will be left in place)
        for (String key : list) {
            removeAttribute(key);
        }

    }


    /**
     * @return the facade associated with this ApplicationContext.
     */
    protected ServletContext getFacade() {
        return this.facade;
    }


    /**
     * Set an attribute as read only.
     */
    void setAttributeReadOnly(String name) {

        if (attributes.containsKey(name)) {
            readOnlyAttributes.put(name, name);
        }

    }


    protected void setNewServletContextListenerAllowed(boolean allowed) {
        this.newServletContextListenerAllowed = allowed;
    }

    /**
     * Internal class used as thread-local storage when doing path
     * mapping during dispatch.
     */
    private static final class DispatchData {

        public MessageBytes uriMB;
        public MappingData mappingData;

        public DispatchData() {
            uriMB = MessageBytes.newInstance();
            CharChunk uriCC = uriMB.getCharChunk();
            uriCC.setLimit(-1);
            mappingData = new MappingData();
        }
    }
}

概述

Spring Boot是一个用于构建独立、生产级别的基于Spring框架的Java应用程序的开发工具。它旨在简化Spring应用程序的创建和部署,并提供了许多开箱即用的功能。下面将详细介绍Spring Boot的优点和缺点。

优点

  1. 简化开发过程:Spring Boot通过自动配置和约定大于配置的原则,简化了Spring应用的初始化和开发过程。它提供了大量开箱即用的功能,如安全性、数据库访问、消息传递等,减少了开发者的工作量。
  2. 快速应用程序启动:由于Spring Boot集成了嵌入式的Tomcat或Jetty服务器,可以快速部署和启动应用程序,减少了部署时间。
  3. 易于测试:Spring Boot集成了大量用于单元测试和集成测试的工具,使得测试变得更加简单和快速。
  4. 易于部署:由于其独立的运行方式,Spring Boot应用程序可以轻松部署到云端或服务器上。
  5. 强大的社区支持:Spring Boot拥有庞大的开发者社区,为开发者提供了丰富的资源和支持。
  6. 集成度:Spring Boot与许多流行的技术和框架集成,如Thymeleaf、Bootstrap、Spring Data等,方便开发者快速构建应用程序。
  7. 安全性:Spring Boot提供了内置的安全特性,如CSRF保护、安全性过滤器等,并可轻松集成第三方安全解决方案。
  8. 监控和度量:Spring Boot集成了用于监控和度量的工具,如健康检查、性能指标等,方便开发者对应用程序进行实时监控和管理。

缺点

  1. 学习曲线陡峭:对于初学者来说,Spring Boot的强大功能和广泛集成可能会带来学习上的挑战。需要花费一定的时间来熟悉其概念和工具。
  2. 配置复杂性:虽然Spring Boot简化了开发过程,但有时过于简化的配置可能会导致某些情况下的配置复杂性增加。
  3. 性能考虑:虽然Spring Boot提供了快速的启动时间和集成度,但在处理大量请求或复杂计算时,性能可能不是最优的。
  4. 过度依赖Spring生态:由于Spring Boot基于Spring框架,因此对Spring生态系统的依赖较强。对于非Spring框架的应用程序,集成可能会变得复杂。
  5. 第三方库的兼容性:在某些情况下,新版本的Spring Boot可能与第三方库不兼容,需要进行额外的测试和调整。
  6. 资源占用:在某些情况下,由于Spring Boot应用程序的复杂性,可能会导致资源占用较多,如内存占用、CPU占用等。
  7. 不易调试:当应用程序出现错误时,由于其高度的集成性和自动配置特性,调试可能会变得复杂。
  8. 版本更新快:由于技术的快速发展,Spring Boot的版本更新速度较快。这可能导致对旧版本的支持有限,以及学习新版本的额外成本。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: Springboot源码解析PDF是一本深入解析Springboot框架的技术书籍,涵盖了Springboot的背景、原理、设计思路、运行机制、开发实践等方面。全书主要分为三部分:第一部分介绍Springboot的基础知识,包括Spring框架的常用注解、Springboot的配置、自动配置原理等;第二部分深入探讨Springboot的主要功能,如数据访问、Web开发、缓存、消息、安全等;第三部分着重介绍开发Springboot应用的最佳实践,包括Springboot与其他框架的结合使用、集成测试、监控与诊断等。 阅读Springboot源码解析PDF可以让开发者更深入理解Springboot的设计理念、技术实现以及应用场景,在实际项目开发中更加灵活、高效地使用Springboot。该书对于有一定JavaSpring框架基础的开发者来说是一本非常优秀的参考资料,也是Java开发者必不可少的技术读物。同时,该书也是借助源码解析的方式,让读者更加系统地学习Springboot技术,具有很高的实用性和参考价值。总之,阅读Springboot源码解析PDF有助于开发者更好地掌握Springboot技术,提高应用开发效率和代码质量。 ### 回答2: Spring Boot源码解析pdf是一本介绍Spring Boot框架的开源书籍,该书的目的是帮助开发者深入了解Spring Boot的内部工作原理和实现细节。 该书首先从Spring Boot框架的源码结构和核心模块入手,详细介绍了Spring Boot的MVC、ORM、缓存、安全等核心功能的实现原理。同时,该书还介绍了Spring Boot对微服务的支持和整合Spring Cloud的方式,让开发者更深入了解Spring Boot在分布式架构中的应用。 在讲解源码实现原理的同时,该书还指出了一些常见的开发问题和易错点,并提供了相应的解决方案。此外,该书还通过一系列的案例,全面展示了Spring Boot的实际开发应用场景,帮助开发者更好地应用Spring Boot框架。 总的来说,Spring Boot源码解析pdf是一本非常实用的书籍,能够帮助开发者快速掌握Spring Boot框架的内部实现原理,提高开发效率和代码质量。同时,该书还可以作为学习Spring Boot的参考资料,对于想要深入学习和研究Spring Boot的开发者来说是非常有用的。 ### 回答3: Spring Boot 是一个很受欢迎的 Java 框架,它简了开发者的工作,允许他们更快速地构建和部署应用程序。Spring Boot 的优点包括简洁的配置、内嵌的 Web 容器和现成的插件,让开发者可以更专注于业务逻辑。Spring Boot源码解析是学习它的关键。 Spring Boot源码解析Java初学者来说可能会比较复杂,但是它对于学习框架和原理是非常有益的。一个好的 Spring Boot 项目需要基于很好的基础,这就要求开发者了解其源码源码解析可以帮助开发者了解各种设计模式和组件的原理,有助于解决实际问题。 在 Spring Boot源码解析中,我们将会找到很多有用的信息。例如,我们可以看到 Spring Boot 如何通过注解处理器创建并配置 bean,这可以帮助我们更好地理解依赖注入和 IoC 容器。此外,我们还可以了解 Spring Boot 如何处理 HTTP 请求、创建模板、处理安全性等。这些知识可以帮助我们更好地理解 Spring Boot 内部的工作机制。 总之,Spring Boot源码解析是必不可少的一部分,它可以帮助开发者更好地了解和使用该框架,掌握在实践中所需的知识和技能。如果您是一名 Spring 开发者,那么深入了解 Spring Boot源码将会是一个很好的学习过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋力向前123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值