前言:个人理解是springboot+thymleaf多用于前后端不分离的,thymleaf也是springboot推荐的模板引擎,结合紧密,所以这两个内容放在一起
springboot不带数据源启动时出错解决
注:启动类上加入数据源自动配置类即可
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class MyblogApplication {
public static void main(String[] args) {
SpringApplication.run(MyblogApplication.class, args);
}
}
springboot静态资源访问出错
可能原因:
1、springboot2.x版本对静态资源有拦截,添加静态资源映射
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
}
2、静态资源文件路径错误
3、编译target文件中没有静态资源,clean清除后重新编译
thymleaf
注:
spring-boot-autoconfigure下的thymleaf文件夹中有一些自动配置相关信息
摘取一段ThymeleafProperties 源码,方便配置文件中thymleaf属性设置的理解
开发阶段:cache多禁用
@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;
//...
}
thymleaf的常见命令
参考自己平时使用总结:Thymeleaf常用知识点
springboot
springboot版本是2.3.4
注:
1、主要关注静态资源文件夹staticLocations,理解查找顺序
2、从上面依次往下面找。所有静态资源类似css、js、图标等都可以放在static下
3、注意下面的resources都是指resources根目录(有特殊标记)下的resources文件夹。
如果在资源根目录resources下没有新建resources文件夹,则会跳过。
除非修改静态路径了,那就稍微麻烦。
“classpath:/META-INF/resources/”,
“classpath:/resources/”,
“classpath:/static/”,
“classpath:/public/”
@ConfigurationProperties(
prefix = "spring.resources",
ignoreUnknownFields = false
)
public class ResourceProperties {
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 void setStaticLocations(String[] staticLocations) {
this.staticLocations = this.appendSlashIfNecessary(staticLocations);
}
private String[] appendSlashIfNecessary(String[] staticLocations) {
String[] normalized = new String[staticLocations.length];
for(int i = 0; i < staticLocations.length; ++i) {
String location = staticLocations[i];
normalized[i] = location.endsWith("/") ? location : location + "/";
}
return normalized;
}
//...
}
注:WebMvcAutoConfiguration的静态资源webjars,以后引入一些js会用到
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/**")) {
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));
}
}
}
扩展springMVC
注:
下面是WebMvcConfigurer接口,里面全部都是默认方法,可以重写实现想要扩展的功能。
不推荐利用WebMvcConfigurerAdapter 类,因为已废弃。可以看出WebMvcConfigurerAdapter 也是实现了WebMvcConfigurer接口。
@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer
public interface WebMvcConfigurer {
default void configurePathMatch(PathMatchConfigurer configurer) {
}
default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}
default void addFormatters(FormatterRegistry registry) {
}
default void addInterceptors(InterceptorRegistry registry) {
}
default void addResourceHandlers(ResourceHandlerRegistry registry) {
}
default void addCorsMappings(CorsRegistry registry) {
}
default void addViewControllers(ViewControllerRegistry registry) {
}
default void configureViewResolvers(ViewResolverRegistry registry) {
}
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}
default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
@Nullable
default Validator getValidator() {
return null;
}
@Nullable
default MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
国际化配置
注:全局配置文件中可以配置spring.messages.basename 国际化文件路径
public class MessageSourceAutoConfiguration {
private static final Resource[] NO_RESOURCES = new Resource[0];
public MessageSourceAutoConfiguration() {
}
@Bean
@ConfigurationProperties(
prefix = "spring.messages"
)
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
//...
}
// spring.messages.basename 配置文件路径
public class MessageSourceProperties {
private String basename = "messages";
private Charset encoding;
@DurationUnit(ChronoUnit.SECONDS)
private Duration cacheDuration;
private boolean fallbackToSystemLocale;
private boolean alwaysUseMessageFormat;
private boolean useCodeAsDefaultMessage;
public MessageSourceProperties() {
this.encoding = StandardCharsets.UTF_8; // 默认编码utf8
this.fallbackToSystemLocale = true;
this.alwaysUseMessageFormat = false;
this.useCodeAsDefaultMessage = false;
}
注:可以在url连接上携带区域信息,比如?l=zh_CN
/**
* 可以在url连接上携带区域信息
*/
public class MyLocaleResolver implements LocaleResolver {
/**
* l=zh_CN l=en_US
* Locale的构造函数,根据语言+国家。后面还有一个参数不明
* public Locale(String language, String country) {
* this(language, country, "");
* }
* @param request
* @return
*/
@Override
public Locale resolveLocale(HttpServletRequest request) {
String l = request.getParameter("l");
Locale locale = Locale.getDefault();
if(!StringUtils.isEmpty(l)){
String[] split = l.split("_");
locale = new Locale(split[0],split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
错误配置页面
注:
ErrorProperties 里面存在错误路径
BasicErrorController 控制
DefaultErrorViewResolver 默认错误视图解析
public class ErrorProperties {
@Value("${error.path:/error}")
private String path = "/error";
private boolean includeException;
private ErrorProperties.IncludeStacktrace includeStacktrace;
private ErrorProperties.IncludeAttribute includeMessage;
private ErrorProperties.IncludeAttribute includeBindingErrors;
private final ErrorProperties.Whitelabel whitelabel;
//...
}
@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
this(errorAttributes, errorProperties, Collections.emptyList());
}
//...
// 这里存在两种返回方式,根据请求头如果是text/html,表示浏览器请求则返回html,否则返回json
@RequestMapping(
produces = {"text/html"}
)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
return modelAndView != null ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = this.getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity(status);
} else {
Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity(body, status);
}
}
//...
}
// 关注:resolveErrorView、resolve、resolveResource
public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
private static final Map<Series, String> SERIES_VIEWS;
private ApplicationContext applicationContext;
private final ResourceProperties resourceProperties;
private final TemplateAvailabilityProviders templateAvailabilityProviders;
private int order = 2147483647;
//...
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
private ModelAndView resolve(String viewName, Map<String, Object> model) {
//默认SpringBoot可以去找到一个页面? error/404
String errorViewName = "error/" + viewName;
//模板引擎可以解析这个页面地址就用模板引擎解析
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
//模板引擎可用的情况下返回到errorViewName指定的视图地址
//模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面 error/404.html
return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);
}
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
String[] var3 = this.resourceProperties.getStaticLocations();
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
String location = var3[var5];
try {
Resource resource = this.applicationContext.getResource(location);
resource = resource.createRelative(viewName + ".html");
if (resource.exists()) {
return new ModelAndView(new DefaultErrorViewResolver.HtmlResourceView(resource), model);
}
} catch (Exception var8) {
}
}
return null;
}
//...
}
总结:
1、有模板引擎的情况下error/状态码;
1.1 将错误页面命名为 errorCode.html 放在模板引擎templates/error下,发生此状态码的错误就会来到对应的页面
1.2 使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html)
2、没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找
3、以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面
定制错误信息或者说做一些更改
首先,在控制层添加一个控制通知来处理传到控制层的异常
@ControllerAdvice
public class MyExceptionHandler {
//1、浏览器客户端返回的都是json
// @ResponseBody
// @ExceptionHandler(UserNotExistException.class)
// public Map<String,Object> handleException(Exception e){
// Map<String,Object> map = new HashMap<>();
// map.put("code","user.notexist");
// map.put("message",e.getMessage());
// return map;
// }
@ExceptionHandler(UserNotExistException.class)
public String handleException(Exception e, HttpServletRequest request){
Map<String,Object> map = new HashMap<>();
//传入我们自己的错误状态码 4xx 5xx ,若不设置状态码,那就被认为此页面已经异常处理了,返回的就是200
/**
* Integer statusCode = (Integer) request
.getAttribute("javax.servlet.error.status_code");
*/
request.setAttribute("javax.servlet.error.status_code",500);
map.put("code","user.notexist");
map.put("message","用户出错啦");
request.setAttribute("ext",map);
//转发到/error
return "forward:/error";
}
}
其次,注册自定义的MyErrorAttributes ,它继承DefaultErrorAttributes
注意:
DefaultErrorAttributes 类中
getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace)已经弃用
替换方法:
getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options)
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
// 这里相当于将默认处理的信息拿出来,添加点自定义信息
Map<String, Object> map = super.getErrorAttributes(webRequest, options);
map.put("company","atguigu");
//我们的异常处理器携带的数据
Map<String,Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0);
map.put("ext",ext);
return map;
}
/* // 以前的处理方法,因为已经deprecated,所以换成上面的
// 返回值的map就是页面和json能获取的所有字段
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes((WebRequest) requestAttributes, includeStackTrace);
map.put("company","atguigu");
//我们的异常处理器携带的数据
Map<String,Object> ext = (Map<String, Object>) requestAttributes.getAttribute("ext", 0);
map.put("ext",ext);
return map;
}*/
}