springboot的web开发
一.springboot对静态资源的映射规则
1.web jars的映射
- webjars:以jar包的方式引入静态资源
所有的/webjars/**,都去classpath:/META-INF/resources/webjars/找资源
2./**
访问当前项目的任何资源(默认静态资源的文件夹)
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/static/
- classpath:/public/
- /:当前项目的根路径
3.欢迎页
静态资源文件夹下的index.html页面,被/**映射
localhost:8080:/ -> 找index.html
4.favicon(网页图标)
所有的**/favicon.ico都在静态资源文件夹下找
5.自定义静态资源路径
在properties或yml配置里面定义(可以定义多个)
spring.resource.static.locations=classpath:/hello/,classpath:/world/
二.模板引擎
JSP、velocity、freeMark、thymeleaf(springboot推荐)
1.thymeleaf引入
- 引入thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-themaleaf</artifactId>
</dependency>
2.thymeleaf使用
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = Charset.forName("UTF‐8");
private static final MimeType DEFAULT_CONTENT_TYPE =MimeType.valueOf("text/html");
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
//
}
只要把html文件放入templates目录下,thymeleaf就能自动渲染,直接return:“success”;
不同的是要导入thymeleaf的名称空间,目的是有语法提示,如下
<html lang="en" xmlns:th="http://www.thymeleaf.org">
3.thymeleaf语法规则
(1)th:*
具体见官网文档usethymeleaf.pdf
- th:text;改变当前元素里面文本内容,th:utext不转义特殊字符
- th:任意html属性,来替换原生属性的值
@Controller
public class userController {
@RequestMapping("/user")
public String success(Map<String,Object> map){
map.put ( "hello","你好" );//会把map放入request作用域中去
return "success";
}
}
<div id="dic01" th:text="${hello}" th:id="${hello}">success</div>
- th:each
[[]]与th:text对应
[()]与th:utext对应
<span th:each="user:${list}">[[${user}]]<br></span>
(2)表达式
Literals(字面量)
Text literals: 'one text' , 'Another one!' ,…
Number literals: 0 , 34 , 3.0 , 12.3 ,…
Boolean literals: true , false
Null literal: null
Literal tokens: one , sometext , main ,…
Text operations:(文本操作)
String concatenation: +
Literal substitutions: |The name is ${name}|
Arithmetic operations:(数学运算)
Binary operators: + , ‐ , * , / , %
Minus sign (unary operator): ‐
Boolean operations:(布尔运算)
Binary operators: and , or
Boolean negation (unary operator): ! , not
Comparisons and equality:(比较运算)
Comparators: > , < , >= , <= ( gt , lt , ge , le )
Equality operators: == , != ( eq , ne )
Conditional operators:条件运算(三元运算符)
If‐then: (if) ? (then)
If‐then‐else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
Special tokens:
No‐Operation: _
- ${}
-
可以获取对象属性,调用方法
-
可以使用基本内置对象:#ctx,#vars,#locale,#request,#response,#session,#sevletContext1
-
可以使用内置工具对象:
- *{}选择表达式,和${}功能一样,和th:object配合使用
<div ><!--普通使用-->
<p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>
<div th:object="${session.user}"><!--配合th:object使用-->
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
- #{}获取国际化内容
- @{}定义url
<div href="details.html" th:href="@{/order/process(execId=${execId},execType='FAST')}"></div>
- ~{}片段引用表达式
<div th:insert="~{commons :: main}">...</div>
三.mvc自动配置
1.SpringBoot对SpringMVC的默认配置:(WebMvcAutoConfiguration)
- Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
- Support for serving static resources, including support for WebJars (see below).静态资源文件夹路径,webjars
- Static index.html support. 静态首页访问
- Custom Favicon support (see below). favicon.ico
- 自动注册了 of Converter , GenericConverter , Formatter beans.
- Support for HttpMessageConverters (see below).
- Automatic registration of MessageCodesResolver (see below).定义错误代码生成规则
- Automatic use of a ConfigurableWebBindingInitializer bean (see below).
2.扩展Spring MVC
- springmvc中的配置文件
<mvc:view‐controller path="/hello" view‐name="success"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean></bean>
</mvc:interceptor>
</mvc:interceptors>
- springboot扩展配置
编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;不能标注@EnableWebMvc;既保留了所有的自动配置,也能用我们扩展的配置;
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//浏览器发送 /atguigu 请求来到 success
registry.addViewController("/atguigu").setViewName("success");
}
}
原理:
- WebMvcAutoConfiguration是SpringMVC的自动配置类
- 在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)
- 容器中所有的WebMvcConfigurer都会一起起作用;
- 我们的配置类也会被调用;
3.全面接管springMVC
SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了
我们需要在配置类中添加@EnableWebMvc即可;
@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//浏览器发送 /atguigu 请求来到 success
registry.addViewController("/atguigu").setViewName("success");
}
}
四.默认访问首页的修改
@Controller
public class HelloController {
@RequestMapping("/")
//方式1
public String index(){
return "login";
}
}
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
//springboot2.0中用WebMvcConfigurationSupport代替
@Override//方式2
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController ( "/liangqiu" ).setViewName ( "login" );
}
@Bean//方式3
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter ( ) {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController ( "/index.html" ).setViewName ( "login" );
}
};
return adapter;
}
}
五.国际化
步骤:
1)、编写国际化配置文件;
2)、使用ResourceBundleMessageSource管理国际化资源文件
3)、在页面使用fmt:message取出国际化内容
4)、点击链接切换国际化
1.编写国际化配置文件
编写国际化配置文件,抽取页面需要显示的国际化消息,并且可以修改国际化文件的默认位置(默认为类路径下的messages文件)
spring.messages.basename=i18n.login
2.管理国际化资源文件
SpringBoot自动配置好了管理国际化资源文件的组件(以下为源代码)
@ConfigurationProperties(prefix = "spring.messages")
public class MessageSourceAutoConfiguration {
/**
* Comma‐separated list of basenames (essentially a fully‐qualified classpath
* location), each following the ResourceBundle convention with relaxed support for
* slash based locations. If it doesn't contain a package qualifier (such as
* "org.mypackage"), it will be resolved from the classpath root.
*/
private String basename = "messages";
//我们的配置文件可以直接放在类路径下叫messages.properties;
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(this.basename)) {
//设置国际化资源文件的基础名(去掉语言国家代码的)
messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(this.basename)));
}
if (this.encoding != null) {
messageSource.setDefaultEncoding(this.encoding.name());
}
messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);
messageSource.setCacheSeconds(this.cacheSeconds);
messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);
return messageSource;
}
}
3.去页面获取国际化的值;
以下为我的html代码,主要用#{}获取国际化内容
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Signin Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.3.1/css/bootstrap.css}" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="asserts/css/signin.css" th:href="@{asserts/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">
<form class="form-signin" action="dashboard.html">
<img class="mb-4" th:src="@{asserts/img/bootstrap-solid.svg}" src="asserts/img/bootstrap-solid.svg" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
<label class="sr-only" th:text="#{login.username}">Username</label>
<input type="text" class="form-control" placeholder="Username" required="" autofocus="" th:placeholder="#{login.username}">
<label class="sr-only" th:text="#{login.password}">Password</label>
<input type="password" class="form-control" placeholder="Password" required="" th:placeholder="#{login.password}">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me" >[[#{login.remmember}]]
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
<a class="btn btn-sm">中文</a>
<a class="btn btn-sm">English</a>
</form>
</body>
</html>
4.点击链接切换国际化
<a class="btn btn-sm" th:href="@{/index.html(l=zh_CN)}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l=en_CS)}">English</a>
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
String str = request.getParameter ( "l" );
Locale locale = Locale.getDefault ( );
if (!StringUtils.isEmpty ( str )){
String[] split = str.split("_");
locale = new Locale(split[0],split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver ();
}
六、拦截器
1.快速生效
开发期间模板引擎页面修改以后要实时生效
- 禁用模板引擎的缓存
spring.thymeleaf.cache=false
#禁用缓存
- 页面修改之后ctrl+f9,重新编译
2.拦截器
(1)编写拦截器
public class MyIntercepter implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object user = request.getSession ( ).getAttribute ( "user" );
if (user!=null){
return true;
}else{
request.setAttribute ( "msg","没有权限请先登录" );
request.getRequestDispatcher ( "/index.html" ).forward ( request,response );
return false;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
(2)注册拦截器
@Bean//方式3
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter ( ) {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor ( new MyIntercepter () ).addPathPatterns ( "/**" )
.excludePathPatterns ( "/index.html" ,"/liangqiu","/","/user/login","/asserts/**");
}
//除了要放行首页外,还要放行访问静态资源,这是有些时候首页样式表不起效的原因
};
return adapter;
}
七.thymeleaf公共页面元素抽取(片段引用)
1.步骤
- 抽取公共片段
<div th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>
- 引入公共片段
<div th:insert="~{footer :: copy}"></div>
~{templatename::#selector}:模板名::选择器
~{templatename::fragmentname}:模板名::片段名
- 默认效果
insert的公共片段在div标签中
如果使用th:insert等属性进行引入,可以不用写~{}:
行内写法可以加上:[[{}]];[({})];
<div>
<div th:insert="~{footer :: copy}"></div>
</div>
2.三种引入公共片段的th属性
- th:insert:将公共片段整个插入到声明引入的元素中
- th:replace:将声明引入的元素替换为公共片段
- th:include:将被引入的片段的内容包含进这个标签中
<footer th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</footer>
引入方式
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>
效果
<div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
</div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
<div>
© 2011 The Good Thymes Virtual Grocery
</div>
3.使用id选择器引入
<nav class="col-md-2 d-none d-md-block bg-light sidebar" id="sidebar">
</nav>
<div th:replace="~{dashboard::#sidebar}"></div>
4.引用片段的时候传入参数,以实现动态的html
步骤
- 公共片段定义参数
<a class="nav-link active" href="#" th:href="@{/main}"
th:class="${activeUri=='main'?'nav-link active':'nav-link'}">
...
<a class="nav-link active" href="/crud/emps" th:href="@{/emps}"
th:class="${activeUri=='emps'?'nav-link active':'nav-link'}">
- 不同场景传入不同参数
<div th:replace="~{commons/bar::#sidebar(activeUri=emps)}"></div>
<div th:replace="~{commons/bar::#sidebar(activeUri=main)}"></div>
- 效果图
在公共片段部分点中哪个按钮就高亮,就是点击不同的按钮,进入不同的html,然后引入公共片段,根据传入的参数显示不同的地方高亮。
八.springboot下的put/delete请求
1.springmvc中配置HiddenHttpMethodFilter(springboot自动配置好了)
2.页面创建一个post表单
3.创建一个input项,name="_method",值就是我们指定的请求方式
<form th:action="@{/emp}" method="post">
<input type="hidden" name="_method" value="put">
</form>
九.错误处理机制
1.springboot默认的错误处理机制
原理可参照ErrorMvcAutoConfiguration,错误的自动配置
(1)浏览器访问
返回一个默认错误处理页面
(2)其他客户端
默认响应一个json数据
2.如何定制错误响应
(1)如何定制错误的页面
- 有模板引擎的情况下;error/状态码; (将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的error文件夹下),发生此状态码的错误就会来到 对应的页面;我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html如:404.html);
页面能获取的信息:
timestamp:时间戳
status:状态码
error:错误提示
exception:异常对象
message:异常消息
errors:JSR303数据校验的错误都在这里 - 没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找;
- 以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;
(2)如何定制错误的json数据
- 自定义异常&返回定制json数据
@ControllerAdvice
public class MyExceptionHandler {
@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;
}
}
//没有自适应效果(浏览器和客户端都是json字符串)
- 转发到/error进行自适应响应效果处理
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(UserNotExistException.class)
public String handleException(Exception e, HttpServletRequest request){
Map<String,Object> map = new HashMap<>();
//传入我们自己的错误状态码 4xx 5xx,否则就不会进入定制错误页面的解析流程
/**
* 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",e.getMessage());
request.setAttribute("ext",map);//将定制数据传入request
return "forward:/error";
//定制数据没有携带出去
}
}
- 将我们的定制数据携带出去
出现错误以后,会来到/error请求,会被BasicErrorController处理,响应出去可以获取的数据是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法);
第一种办法:完全来编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中;(太麻烦,不推荐)
第二种办法:页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到;容器中DefaultErrorAttributes.getErrorAttributes();默认进行数据处理的;
//给容器中加入我们自己定义的ErrorAttributes
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(requestAttributes,includeStackTrace);
Map<String,Object> ext=(Map<String,Object>)requestAttribute.getAttribute("ext",0);
//从异常处理器获取定制数据,0表示从request中获取,1表示session
map.put("company","atguigu");
return map;
}
}
十.配置嵌入式servlet容器
1.如何定制和修改servlet容器的相关配置
(1)修改server相关的配置(ServerProperties)
server.port=8081
server.context‐path=/crud
server.tomcat.uri‐encoding=UTF‐8
//通用的Servlet容器设置
server.xxx
//Tomcat的设置
server.tomcat.xxx
(2)编写一个EmbeddedServletContainerCustomizer:嵌入式的Servlet容器的定制器;来修改Servlet容器的配置
@Bean //一定要将这个定制器加入到容器中
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
return new EmbeddedServletContainerCustomizer() {
//定制嵌入式的Servlet容器相关的规则
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setPort(8083);
}
};
}