在使用到SpringBoot的工程中,如果要用到JSP文件,JSP文件需要放到src/main/webapp目录下,参考spring-boot-2.1.7.RELEASE.jar的源代码,看一下为什么会这样:
org.springframework.boot.web.servlet.server.DocumentRoot
package org.springframework.boot.web.servlet.server;
import java.io.File;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.CodeSource;
import java.util.Arrays;
import java.util.Locale;
import org.apache.commons.logging.Log;
/**
* Manages a {@link ServletWebServerFactory} document root.
*
* @author Phillip Webb
* @see AbstractServletWebServerFactory
*/
class DocumentRoot {
private static final String[] COMMON_DOC_ROOTS = { "src/main/webapp", "public", "static" };
private final Log logger;
private File directory;
DocumentRoot(Log logger) {
this.logger = logger;
}
public File getDirectory() {
return this.directory;
}
public void setDirectory(File directory) {
this.directory = directory;
}
/**
* Returns the absolute document root when it points to a valid directory, logging a
* warning and returning {@code null} otherwise.
* @return the valid document root
*/
public final File getValidDirectory() {
File file = this.directory;
file = (file != null) ? file : getWarFileDocumentRoot();
file = (file != null) ? file : getExplodedWarFileDocumentRoot();
file = (file != null) ? file : getCommonDocumentRoot();
if (file == null && this.logger.isDebugEnabled()) {
logNoDocumentRoots();
}
else if (this.logger.isDebugEnabled()) {
this.logger.debug("Document root: " + file);
}
return file;
}
private File getCommonDocumentRoot() {
for (String commonDocRoot : COMMON_DOC_ROOTS) {
File root = new File(commonDocRoot);
if (root.exists() && root.isDirectory()) {
return root.getAbsoluteFile();
}
}
return null;
}
.....................
}
从以上代码可以看到,SpringBoot为ServletWebServer提供了三个根目录,用于查找JSP等资源,其中src/main/webapp就是最常用到的目录。
如果使用Tomcat作为ServletWebServer,则会将上面代码中获取到的目录作为Tomcat的documentRoot。
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
package org.springframework.boot.web.embedded.tomcat;
import java.io.*;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;
import javax.servlet.ServletContainerInitializer;
import org.apache.catalina.*;
import org.apache.catalina.WebResourceRoot.ResourceSetType;
import org.apache.catalina.WebResourceSet;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.session.StandardManager;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.Tomcat.FixContextListener;
import org.apache.catalina.util.LifecycleBase;
import org.apache.catalina.webresources.*;
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.http2.Http2Protocol;
import org.apache.tomcat.util.scan.StandardJarScanFilter;
import org.springframework.boot.web.server.*;
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.*;
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory
implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
private static final Set<Class<?>> NO_CLASSES = Collections.emptySet();
/**
* The class name of default protocol used.
*/
public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
private File baseDirectory;
private List<Valve> engineValves = new ArrayList<>();
private List<Valve> contextValves = new ArrayList<>();
private List<LifecycleListener> contextLifecycleListeners = getDefaultLifecycleListeners();
private List<TomcatContextCustomizer> tomcatContextCustomizers = new ArrayList<>();
private List<TomcatConnectorCustomizer> tomcatConnectorCustomizers = new ArrayList<>();
private List<Connector> additionalTomcatConnectors = new ArrayList<>();
private ResourceLoader resourceLoader;
private String protocol = DEFAULT_PROTOCOL;
private Set<String> tldSkipPatterns = new LinkedHashSet<>(TldSkipPatterns.DEFAULT);
private Charset uriEncoding = DEFAULT_CHARSET;
private int backgroundProcessorDelay;
/**
* Create a new {@link TomcatServletWebServerFactory} instance.
*/
public TomcatServletWebServerFactory() {
}
/**
* Create a new {@link TomcatServletWebServerFactory} that listens for requests using
* the specified port.
* @param port the port to listen on
*/
public TomcatServletWebServerFactory(int port) {
super(port);
}
/**
* Create a new {@link TomcatServletWebServerFactory} with the specified context path
* and port.
* @param contextPath the root context path
* @param port the port to listen on
*/
public TomcatServletWebServerFactory(String contextPath, int port) {
super(contextPath, port);
}
.....................
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = getValidDocumentRoot();
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
if (documentRoot != null) {
context.setResources(new LoaderHidingResourceRoot(context));
}
context.setName(getContextPath());
context.setDisplayName(getDisplayName());
context.setPath(getContextPath());
File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
context.setDocBase(docBase.getAbsolutePath());
context.addLifecycleListener(new FixContextListener());
context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
: ClassUtils.getDefaultClassLoader());
resetDefaultLocaleMapping(context);
addLocaleMappings(context);
context.setUseRelativeRedirects(false);
try {
context.setCreateUploadTargets(true);
}
catch (NoSuchMethodError ex) {
// Tomcat is < 8.5.39. Continue.
}
configureTldSkipPatterns(context);
WebappLoader loader = new WebappLoader(context.getParentClassLoader());
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
loader.setDelegate(true);
context.setLoader(loader);
if (isRegisterDefaultServlet()) {
addDefaultServlet(context);
}
if (shouldRegisterJspServlet()) {
addJspServlet(context);
addJasperInitializer(context);
}
context.addLifecycleListener(new StaticResourceConfigurer(context));
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
host.addChild(context);
configureContext(context, initializersToUse);
postProcessContext(context);
}
/**
* Returns the absolute document root when it points to a valid directory, logging a
* warning and returning {@code null} otherwise.
* @return the valid document root
*/
protected final File getValidDocumentRoot() {
return this.documentRoot.getValidDirectory();
}
..................................
}
protected void prepareContext(Host host, ServletContextInitializer[] initializers) 方法中第一行就是获取src/main/webapp作为documentRoot。