一、静态资源映射 通过之前自动装配原理源码的分析思路,现在可以很容易就锁定对应SpringMVC相关的配置类。,找到MVC自动配置类中的适配器中的增加资源的处理器。
文章图片
访问路径会被映射为对应路径下的资源,SpringBoot牛逼!
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
//说明/webjars/**路径下的资源会被映射为classpath:/META-INF/resources/webjars这个路径
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}}
}
上面代码的第二种情况的请求路径,
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
实际上就是WebMvcProperties类中的this.staticPathPattern = "/**";
。一切好说,问题不大。对于上面代码的第二种情况的映射路径,点到对应的配置类里面看源码,其实就包括四个映射路径。问题不是很大。
文章图片
public class ResourceProperties {
//这个还是按照优先级顺序排的, SpringBoot设计精妙啊!
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
private String[] staticLocations;
private boolean addMappings;
private final ResourceProperties.Chain chain;
private final ResourceProperties.Cache cache;
public ResourceProperties() {
this.staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
this.addMappings = true;
this.chain = new ResourceProperties.Chain();
this.cache = new ResourceProperties.Cache();
}
public String[] getStaticLocations() {
return this.staticLocations;
}
...
}
二、首页 加下来分析一波web的首页。以往的应用默认首页都是web.xml里面配置好的三个index。tomcat的conf文件夹下面的三大index。。。
index.html
index.htm
index.jsp
还是之前那个自动配置的大类,找到下面的方法。
文章图片
锁定这个方法,点进去看源码。发现另一个方法也和其相关。
private Optional getWelcomePage() {
String[] locations = WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
【Java学习|SpringBoot---杂七杂八---终篇】
this.resourceProperties.getStaticLocations()
这些路径就是之前静态资源映射第二种情况的四种路径。所以这也就解释了,为什么我们没有在之前说的四个路径下面为SpringBoot配置默认页面(index.html)的时候,访问会报错了。总结:一般把静态的网站资源(HTML、CSS、JS)放在类路径下的static文件夹下面。
三、模板引擎视图解析部分 so easy,继续跟踪源码。
下面看一下模板引擎Thymeleaf的视图解析源码,这个更简单了。
@Configuration(
proxyBeanMethods = false
)
@EnableConfigurationProperties({ThymeleafProperties.class})
@ConditionalOnClass({TemplateMode.class, SpringTemplateEngine.class})
@AutoConfigureAfter({WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class})
public class ThymeleafAutoConfiguration {
public ThymeleafAutoConfiguration() {
}
...
}
可以看到默认Thymeleaf解析的路径是类路径下templates路径下的html文件,去掉前后缀即可。
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
private boolean cache;
private Integer templateResolverOrder;
private String[] viewNames;
private String[] excludedViewNames;
private boolean enableSpringElCompiler;
private boolean renderHiddenMarkersBeforeCheckboxes;
private boolean enabled;
private final ThymeleafProperties.Servlet servlet;
private final ThymeleafProperties.Reactive reactive;
public ThymeleafProperties() {
this.encoding = DEFAULT_ENCODING;
this.cache = true;
this.renderHiddenMarkersBeforeCheckboxes = false;
this.enabled = true;
this.servlet = new ThymeleafProperties.Servlet();
this.reactive = new ThymeleafProperties.Reactive();
}
...
}
还有一点很奇怪,为什么引入了模板引擎,首页会是templates目录下面的index.html???
四、扩展MVC部分 1、扩展视图解析器 直接实现接口,加入到容器中,搞定,Debug前端控制器执行代码也是可以获取到自定义的视图解析器的。
@Bean只能用于类内部,@Component可以放在类上面
。 @Component
public class MyViewResolver implements ViewResolver {
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
//添加自己代码
return null;
}
}
文章图片
2、扩展通用部分 官方文档说自动扩展的时候不能加
@EnableWebMvc
,只要加上@Configuraiton
。文章图片
点进去分析这个注解,导入了某个类。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
就是导入了
DelegatingWebMvcConfiguration
类。发现这个类是继承于WebMvcConfigurationSupport
的。 public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
public DelegatingWebMvcConfiguration() {
}
...
}
IDEA全局搜索两下Shift键。
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
...
}
可以看到只有
WebMvcConfigurationSupport
这个类不存在的时候,默认配置才会生效,就相当于不能加上@EnableWebMvc
,否则SpringBoot默认的MVC配置全崩了。 public class DispatcherServlet extends FrameworkServlet {
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";
public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";
public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";
public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";
public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";
public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION";
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
private static final String DEFAULT_STRATEGIES_PREFIX = "org.springframework.web.servlet";
protected static final Log pageNotFoundLogger = LogFactory.getLog("org.springframework.web.servlet.PageNotFound");
private static final Properties defaultStrategies;
private boolean detectAllHandlerMappings = true;
private boolean detectAllHandlerAdapters = true;
private boolean detectAllHandlerExceptionResolvers = true;
private boolean detectAllViewResolvers = true;
private boolean throwExceptionIfNoHandlerFound = false;
private boolean cleanupAfterInclude = true;
@Nullable
private MultipartResolver multipartResolver;
@Nullable
private LocaleResolver localeResolver;
@Nullable
private ThemeResolver themeResolver;
@Nullable
private List handlerMappings;
@Nullable
private List handlerAdapters;
@Nullable
private List handlerExceptionResolvers;
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
@Nullable
private FlashMapManager flashMapManager;
@Nullable
private List viewResolvers;
...
}
问题不大,跟着
DispatcherServlet
配就可以了。3、demo 简单demo:
@Configuration
//@EnableWebMvc
public class MyMVCConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/index", "/", "index.html")
.excludePathPatterns("/asserts/js/**", "/asserts/css/**", "/asserts/img/**")
//.excludePathPatterns("/i18n/**")
.excludePathPatterns("/login");
}
@Bean
public MyLocaleResolver localeResolver() {
return new MyLocaleResolver();
}
}
五、Boot的CRUD模板 踩了坑,国际化出现乱码。。。
文章图片
文章图片
昨晚试了,根本没用啊,TM今早可以了。。。RGL。
链接:https://pan.baidu.com/s/1ONiJMDvSPrIYeMA4Labi9Q
提取码:38bb
以后SpringBoot的CRUD简单的系统都问题不大了,该补补java基础去了。
推荐阅读
- 面渣逆袭|Spring Bean生命周期,好像人的一生。。
- 阿里一面(ReadWriteLock 读写之间互斥吗(我竟然答不上来。。))
- Java|让你加班的不是你的老板,而是其他愿意加班的人
- JAVA人生|全球程序员收入报告(字节跳动高级工程师以年薪274万排名第五)
- xiaoxi整理
- Java版堆排序
- Java|Java初学者学习笔记(一)
- 笔记|二、对象与类、封装、构造方法
- 面试总结|1.1.1 java基础知识总结