web 开发是开发中至关重要的一环,web 开发的核心内容包括内嵌 servlet 容器和 spring MVC。
1.springboot 的 web 开发支持
springboot 提供了 spring-boot-starter-web 为 web 开发予以支持,spring-boot-starter-web 为我们提供了嵌入的 Tomcat 以及 Spring MVC 的依赖。而 web 相关的自动配置存储在 spring-boot-autoconfigure.jar 和 org.springframework.boot.web 下。
2.Thymeleaf 模板引擎
尽可能的少用 JSP,因为在内嵌 Servlet 容器上 JSP 运行有点问题(内嵌 Tomcat、jetty 不支持以 jar 形式运行的 JSP,Undertow 不支持 JSP)。
springboot 提供了大量的模板引擎,包括 FreeMark、Groovy、Thymeleaf、Velocity 和 Mustache,Spring Boot 中推荐使用 Thymeleaf 作为模板引擎,因为 Thymeleaf 提供了完美的 Spring MVC 的支持。
2.1 Thymeleaf 基础知识
Thymeleaf 是一个 Java 类库,它是一个 xml/xhtml/html5 的模板引擎,可以作为 MVC 的 Web 应用的 VIEW 层。
Thymeleaf 还提供了额外的模块与 Spring MVC 集成,所以我们可以使用 Thymeleaf 完全的替代 JSP。
2.2 与 Spring MVC 集成
在 springmvc 中,若我们需要集成一个模板引擎的话,需要定义 ViewResolver,而 ViewResolver 需要定义一个 View ,定义前缀后缀。Thymeleaf 为我们提供的相关的驱动。
以上为默认提供的配置。
下面示例一个简单的例子:
package com.pangu.demo;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import com.pangu.demo.web.entity.Person;
@Controller
@SpringBootApplication
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
@RequestMapping("/thy")
public String thymeleaf(Model model){
Person person = new Person("etfox", 22);
List<Person> people = new ArrayList<Person>();
Person person1 = new Person("etfox1", 22);
Person person2 = new Person("etfox2", 22);
Person person3 = new Person("etfox3", 22);
people.add(person1);
people.add(person2);
people.add(person3);
model.addAttribute("singlePerson", person);
model.addAttribute("people", people);
return "index";
}
@RequestMapping(value = "/json",produces={MediaType.APPLICATION_JSON_VALUE})
public String json(Model model) {
Person single = new Person("aa",11);
model.addAttribute("single", single);
return "jsonView";
}
@Bean
public MappingJackson2JsonView jsonView() {
MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
return jsonView;
}
}
index.jsp:
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link th:href="@{bootstrap/css/bootstrap.min.css}" rel="stylesheet"/>
<link th:href="@{bootstrap/css/bootstrap-theme.min.css}" rel="stylesheet"/>
</head>
<body>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">访问model</h3>
</div>
<div class="panel-body">
<span th:text="${singlePerson.name}"></span>
</div>
</div>
<div th:if="${not #lists.isEmpty(people)}">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">列表</h3>
</div>
<div class="panel-body">
<ul class="list-group">
<li class="list-group-item" th:each="person:${people}">
<span th:text="${person.name}"></span>
<span th:text="${person.age}"></span>
<button class="btn" th:οnclick="'getName(\'' + ${person.name} + '\');'">获得名字</button>
</li>
</ul>
</div>
</div>
</div>
<script th:src="@{jquery.min.js}" type="text/javascript"></script><!-- 2 -->
<script th:src="@{bootstrap/js/bootstrap.min.js}"></script><!-- 2 -->
<script th:inline="javascript">
var single = [[${singlePerson}]];
console.log(single.name+"/"+single.age)
function getName(name){
console.log(name);
}
</script>
</body>
</html>
项目结构:
run。。。。。。。。。。。。。。。。
后记:SB 在自定义拦截器之后,之前的默认配置将不再生效,也就是说你配置的默认的静态路径啥的,刚碰到的坑!!!!!
3.Web 相关配置
3.1:Spring Boot 提供的自动配置
通过查看 WebMvcAutoConfiguration 及 WebMvcProperties 的源码,可以发现 Spring Boot 为我们提供了如下的自动配置。
3.1.1 自动配置的 ViewResolver
(1).ContentNegotiating ViewResolver
这是 spring MVC 提供的一个特殊的 ViewResolver,ContentNegotiatingViewResolver 不是自己处理 view,而是代理给不同的 ViewResolver 来处理不同的 View,所以它有最高的优先级。
(2).BeanNameViewResolver
在控制器(@Controller)中的一个方法的返回值的字符串(视图名)会根据 BeanNameViewResolver 去查找 Bean 的名称为返回字符串的 View 来渲染视图。是不是不好理解,下面举个栗子。
定义 BeanNameViewResolver 的 Bean:
@Bean
public BeanNameViewResolver beanNameViewResolver(){
BeanNameViewResolver resolver = new BeanNameViewResolver();
return resolver;
}
定义一个 View 的 Bean ,名称为 jsonView:
@Bean
public MappingJackson2JsonView jsonView() {
MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
return jsonView;
}
在控制器中,返回值为字符串 jsonView, 它会找 Bean 的名称为 jsonView 的视图来渲染:
@RequestMapping(value = "/json",produces={MediaType.APPLICATION_JSON_VALUE})
public String json(Model model) {
Person single = new Person("aa",11);
model.addAttribute("single", single);
return "jsonView";
}
(3).InternalResourceViewResolver
这个是一个极为常用的 ViewResolver,主要通过设置前缀后缀,以及控制器中方法来返回视图名的字符串,以的到实际的页面,springboot 的源码如下:
@Bean
@ConditionalOnMissingBean(InternalResourceViewResolver.class)
public InternalResourceViewResolver defaultViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.prefix);//前缀
resolver.setSuffix(this.suffix);//后缀
return resolver;
}
3.1.2 自动配置的静态资源
在自动配置类的 addResourceHandlers 方法中定义了以下静态资源自动配置。
(1)类路径文件
把类路径下的/static、/public、/resources 和 /META-INF/resources 文件夹下的静态文件直接映射为 /**,可以通过 http://localhost:xxxx/** 直接访问。
(2)webjar
何谓 webjar,webjar 就是我们常用的脚本框架封装的 jar 包中的 jar 包
把 webjar 的 META-INF/resources/webjar/ 下的静态文件映射为 /webjar/**,可以通过 http://localhost:8080/webjar/** 来访问。
3.1.3 自动配置的 Formatter 和 Converter
关于自动配置 Formatter 和 Converter,我们可以看一下 WebMvcAutoconfiguration 类中的定义:
@Override
public void addFormatters(FormatterRegistry registry) {
for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
registry.addConverter(converter);
}
for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
registry.addConverter(converter);
}
for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
registry.addFormatter(formatter);
}
}
private <T> Collection<T> getBeansOfType(Class<T> type) {
return this.beanFactory.getBeansOfType(type).values();
}
从代码中可以看出,只要我们定义了 Converter GenericConverter Formatter 接口类的实现 bean ,这些 bean 就会自动注册到 spring MVC 中。
3.1.4 自动配置的 HttpMessageConverters
在 WebMvcAutoConfiguration 中,我们注册了 messageConverters,代码如下:
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
@Lazy HttpMessageConverters messageConverters,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConverters = messageConverters;
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider
.getIfAvailable();
}
private final HttpMessageConverters messageConverters;
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.addAll(this.messageConverters.getConverters());
}
在 springboot 中如果要新增自定义的 HttpMessageConverter , 则只需定义一个你自己的 HttpMessageConverters 的 Bean,然后在此 bean 中注册自定义 HttpMessageConverter 即可。
3.1.5 静态首页的支持
把静态 index.html 文件放置在如下目录。
classpath:/META-INF/resources/index.html
classpath:/resources/index.html
classpath:/static/index.html
classpath:/public/index.html
当我们访问应用根目录 http://localhost:xxxx/ 时,会直接映射。
4.接管 Spring Boot 的 Web 配置
如果 Spring Boot 提供的 Spring MVC 默认配置不符合你的需求,则可以通过一个配置类(注解有@Configuration 的类)加上 @EnableWebMvc 注解来实现完全自己控制的 MVC 配置。
当然通常情况下可以满足绝大多数的需求。在既需要保留 Spring Boot 提供的便利,又需要增加自己的额外配置的时候,可以定义一个配置类继承 WebMvcConfigurerAdapter,无需使用 @EnableWebMvc 注解:
package
import
import
import
/**
@ClassName: WebMvcConfig
@Description: TODO
@author etfox
@date
*
@Copyright: 2018 www.etfox.com Inc. All rights reserved.
*/
@Configuration
public class WebMvcConfig implements
/**
<p>Title: addViewControllers</p>
<p>Description:http://127.0.0.1:8080/xx </p>
@param
@see
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/xx").setViewName("test");
}
}
值得注意的是,在这里重写 addViewControllers 方法,并不会覆盖 WebMvcAutoConfiguration 中的 addViewControllers( 此方法中,SpringBoot 将”/“映射至 index.html),这也就意味着我们自己的配置和 springboot 的配置同时生效,这也是我们推荐的添加自己的 MVC 配置的方式。
5. 注册 servlet、filter、listener
当使用嵌入式的 servlet 容器时,我们通过将 servlet、filter 和 listener 声明为 spring bean 而达到注册的效果:或者注册 ServletRegistrationBean、FilterRegistrationBean 和 ServletListenerRegistrationBean 的 Bean.
6.Tomcat 配置
servlet 容器的配置,Spring Boot 默认内嵌的 Tomcat 为 servlet 容器。
6.1 配置 Tomcat
关于 Tomcat 的所有属性都在 org.springframework.boot.autoconfigure.web.ServerProperties 配置类中做了定义,我们只需在 application.properties 配置属性做配置即可。通用的 servlet 容器配置都以 server 作为前缀,而Tomcat 特有配置都以 server 作为前缀,而 Tomcat 特有配置都以 server.tomcat 作为前缀。示例:
7.Favicon 配置
将 ico 文件配置在静态路径下即可,通常根目录。
8.WebSocket
8.1 什么是 WebSocket
WebScoket 为浏览器和服务端提供了双向异步通信的功能,即浏览器可以向服务端发送消息,服务端也可以向浏览器端发送消息。WebSocket 需浏览器的支持,如 IE 10+、Chrome 13+, Firefox 6+, 这对我们现在的浏览器来说都不是问题。
WebScoket 是通过一个 socket 来实现双工异步通信能力。但是直接使用 WebSocket( 或 SockJS:WebSocket 协议的模拟,增加了当浏览器不支持的时候兼容支持)协议开发程序显得特别繁琐,我们使用它的子协议 STOMP ,它是一个更高级别的协议, STOMP 协议使用一个基于帧的格式来定义消息,与 HTTP 的 request 和 response 类似(具有类似于 @RequestMapping 的 @MessageMapping),我们会在后面实战内容中观察 STOMP 的帧。
8.2 Spring Boot 提供的自动配置
Spring Boot 对内嵌的 Tomcat(7/8)、Jetty9 和 Undertow 使用 websocket 提供了支持,配置源码存在 autoconfigure.websocket 下。
Spring Boot 为 WebSocket 提供的 stater pom 是 spring-boot-starter-websocket。