1. Java中有两个通用目录
用户目录 java.home
System.out.println(System.getProperty("user.home"));
工作目录 java.dir
System.out.println(System.getProperty("user.dir"));
当我们创建一个文件,它默认的绝对路径也是这个IDEA项目总工程的绝对目录
new File("").getAbsolutePath();
所以springboot源码中在多模块的项目中正是由于这个原因会导致获取资源文件的目录有误: 他获取到的是总项目的目录下的资源结构, DocumentRoot类中源码如下:
private File getCommonDocumentRoot() {
for (String commonDocRoot : COMMON_DOC_ROOTS) {
File root = new File(commonDocRoot);
if (root.exists() && root.isDirectory()) {
return root.getAbsoluteFile();
}
}
return null;
}
获取的是这三个目录下的文件,由于本身获取的项目目录就有误,所以后续获取资源文件有误:
private static final String[] COMMON_DOC_ROOTS = { "src/main/webapp", "public",
"static" };
解决方案:
通过代码引用(Find Usages)找到最上层的引用类 TomcatServletWebServerFactory(这个看个人容器的选择,这儿选择的是Tomcat容器)
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);
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);
}
通过源码可以看到当 documentRoot == null 的时候,会创建一个 tomcat-docbase 的临时目录, 最后又把创建的这个临时目录放到 TomcatEmbeddedContext 作用域中, 通过方法,类引用 拿到最顶层的 TomcatServletWebServerFactoryCustomizer, WebServerFactoryCustomizer<TomcatServletWebServerFactory>, 然后重写 customize 方法, 拼接新的资源路径
@FunctionalInterface
public interface WebServerFactoryCustomizer<T extends WebServerFactory> {
void customize(T factory);
}
2. ContentNegotiationManager 源码分析