Java学习|SpringBoot---杂七杂八---终篇

一、静态资源映射 通过之前自动装配原理源码的分析思路,现在可以很容易就锁定对应SpringMVC相关的配置类。,找到MVC自动配置类中的适配器中的增加资源的处理器。
Java学习|SpringBoot---杂七杂八---终篇
文章图片

访问路径会被映射为对应路径下的资源,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 = "/**"; 。一切好说,问题不大。
对于上面代码的第二种情况的映射路径,点到对应的配置类里面看源码,其实就包括四个映射路径。问题不是很大。
Java学习|SpringBoot---杂七杂八---终篇
文章图片

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

还是之前那个自动配置的大类,找到下面的方法。
Java学习|SpringBoot---杂七杂八---终篇
文章图片

锁定这个方法,点进去看源码。发现另一个方法也和其相关。
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; } }

Java学习|SpringBoot---杂七杂八---终篇
文章图片



2、扩展通用部分 官方文档说自动扩展的时候不能加@EnableWebMvc,只要加上@Configuraiton
Java学习|SpringBoot---杂七杂八---终篇
文章图片
点进去分析这个注解,导入了某个类。
@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模板 踩了坑,国际化出现乱码。。。
Java学习|SpringBoot---杂七杂八---终篇
文章图片

Java学习|SpringBoot---杂七杂八---终篇
文章图片

昨晚试了,根本没用啊,TM今早可以了。。。RGL。
链接:https://pan.baidu.com/s/1ONiJMDvSPrIYeMA4Labi9Q
提取码:38bb


以后SpringBoot的CRUD简单的系统都问题不大了,该补补java基础去了。

    推荐阅读