SpringBoot---雜七雜八---終篇

一、靜態資源映射

       通過之前自動裝配原理源碼的分析思路,現在可以很容易就鎖定對應SpringMVC相關的配置類。,找到MVC自動配置類中的適配器中的增加資源的處理器。

Alt
       訪問路徑會被映射爲對應路徑下的資源,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 = "/**";。一切好說,問題不大。

       對於上面代碼的第二種情況的映射路徑,點到對應的配置類裏面看源碼,其實就包括四個映射路徑。問題不是很大。
Alt

	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。。。

 	<welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

       還是之前那個自動配置的大類,找到下面的方法。

Alt
       鎖定這個方法,點進去看源碼。發現另一個方法也和其相關。

		private Optional<Resource> 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");
        }

       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;
	    }
	}

Alt

2、擴展通用部分

       官方文檔說自動擴展的時候不能加@EnableWebMvc,只要加上@Configuraiton

Alt       點進去分析這個註解,導入了某個類。

	@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<HandlerMapping> handlerMappings;
	    @Nullable
	    private List<HandlerAdapter> handlerAdapters;
	    @Nullable
	    private List<HandlerExceptionResolver> handlerExceptionResolvers;
	    @Nullable
	    private RequestToViewNameTranslator viewNameTranslator;
	    @Nullable
	    private FlashMapManager flashMapManager;
	    @Nullable
	    private List<ViewResolver> 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模板

       踩了坑,國際化出現亂碼。。。
Alt
Alt
       昨晚試了,根本沒用啊,TM今早可以了。。。RGL。

       鏈接:https://pan.baidu.com/s/1ONiJMDvSPrIYeMA4Labi9Q

       提取碼:38bb

以後SpringBoot的CRUD簡單的系統都問題不大了,該補補java基礎去了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章