学习链接:https://www.liaoxuefeng.com/wiki/1252599548343744/1266265125480448

大部分内容来自此教程,以下只是我的学习笔记。

引入

Servlet是JavaEE中Web开发的基础,但是直接用Servlet来开发Web过于繁琐,就好比直接用JDBC来操作数据库。因此,在实际开发中,我们需要选用一个Servlet封装MVC框架来开发。因此引入Spring的MVC框架——Spring MVC。

Spring MVC与Spring和MVC的关系:

  • MVC是一种设计模式
  • Spring MVC是Spring的一个模块,专门用于构建Web应用程序,用来实现MVC

Spring MVC

配置

  • 我们编写正常的AppConfig后,只需加上@EnableWebMvc注解,就“激活”了Spring MVC。
  • 除了创建DataSourceJdbcTemplatePlatformTransactionManager外,AppConfig需要额外创建几个用于Spring MVC的Bean。如ViewResolver
  • controller的注解必须为@Controller,而不是@Component

启动

介绍最简单的方式:在web.xml中配置Spring MVC提供的DispatcherServlet 来实现。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0"?>
<web-app>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
        </init-param>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.itranswarp.learnjava.AppConfig</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

原理:会初始化一个DispatcherServlet。在DispatcherServlet启动时,会根据AppConfig的配置创建一个WebApplicationContext的IoC容器。完成所有Bean的初始化,并将容器绑到ServletContext上。因为DispatcherServlet持有IoC容器,能从IoC容器中获取所有@Controller的Bean,因此,DispatcherServlet接收到所有HTTP请求后,根据Controller方法配置的路径,就可以正确地把请求转发到指定方法,并根据返回的ModelAndView决定如何渲染页面。

Controller 方法写法

  • 注解:@Controller
  • 处理请求:@PostMapping, @GetMapping
  • 入参:以@RequestParam()标注,或者HttpServletRequestHttpServletResponse或者HttpSession
  • 出参:根据需要返回ModelAndView 或 其他

REST

REST(Representational State Transfer,表征状态转移)架构风格的网络服务。通常接收和返回JSON和XML格式,便于前后端分离的实现,编写基于 JSON 或 XML 响应的 RESTful Web 服务非常方便。

Jackson依赖

定义:流行的开源 Java 库,主要用于处理 JSON 数据

特点:Jackson 可以将 Java 对象转换成 JSON 格式(称为序列化),或者将 JSON 字符串转换成 Java 对象(称为反序列化)。

@RestController注解

使用@RestController替代@Controller后,每个方法自动变成API接口方法。Spring默认使用JSON作为输入和输出。

  • 出参:默认返回响应体(Response Body),而不是视图(View)。这是因为 @RestController 注解包含了 @ResponseBody,这意味着方法的返回值将直接作为 HTTP 响应的正文返回,而不是解析为跳转路径。
  • 入参:如果是@RequestBody类型的,也会被自动序列化为Json格式

控制序列化和反序列化规则

序列化/反序列化一般会作用于对象的所有字段,如果想要对某些字段隐藏(如password),则要使用Jackson提供的@JsonIgnore@JsonProperty注解来实现。

集成Filter

Spring MVC的DelegatingFilterProxy

当一个Filter作为Spring容器管理的Bean存在时,可以通过DelegatingFilterProxy间接地引用它并使其生效。

使用Interceptor

Spring MVC提供的一种功能类似Filter的拦截器:Interceptor。只作用于Controller拦截。

实现:

  1. implements HandlerInterceptor
  2. 选择复写preHandle()postHandle()afterCompletion()方法
  3. WebMvcConfigurer中注册所有的Interceptor

@ExceptionHandler注解

Spring MVC还提供了一个异常处理方法,即使用@ExceptionHandler注解。可以指定触发处理的异常类型,在方法体中定义当出现这些异常时需要有什么动作。如当出现某种异常时, 系统返回一个自定义的错误界面。

作用范围:仅针对当前controller类

CORS 控制跨域访问

CORS,全称Cross-Origin Resource Sharing,是HTML5规范定义的如何跨域访问资源。

默认情况下,浏览器按同源策略放行JavaScript调用API。不同源(域名、协议、端口都相同,才是同源)的目的地址默认不允许访问。如果网站A(http://a.com)要访问网站B,只有当网站B返回响应头Access-Control-Allow-Origin: http://a.com,才允许此访问。

要允许页面JavaScript访问REST API时,允许某些访问,可以通过设置CORS要实现

@CrossOrigin注解

在具体Controller类或方法上添加此注解,可以定义允许的来源地址。

CorsRegistry定义

WebMvcConfigurer中定义一个全局CORS配置。更加推荐此方法。

Spring提供的CorsFilter

需要修改web.xml,也比较繁琐。不推荐。

国际化

指网页对于多语言多地区的本土化展示支持。有以下术语:

  • 国际化 internationalization (i18n)
  • 本地化 localization (l10n)
  • 全球化=国家化+本地化 globalization (g11n)

在Java中,支持多语言和本地化是通过MessageFormat配合Locale实现的

Locale

浏览器的请求头会带Accept-Language头,用来指示用户浏览器设定的语言顺序。

资源文件

一般会根据语言的不同,生成不同命名的资源文件。key相同,value根据不同语句来配置。教程中的例子:

对于多语言,主文件名如果命名为messages,那么资源文件必须按如下方式命名并放入classpath中:

  • 默认语言,文件名必须为messages.properties
  • 简体中文,Locale是zh_CN,文件名必须为messages_zh_CN.properties
  • 日文,Locale是ja_JP,文件名必须为messages_ja_JP.properties

MessageSource

创建Spring提供的MessageSource实例,它自动读取所有的.properties文件,并提供一个统一接口来实现“翻译”

异步处理

概念

  • 同步:请求和返回使用的是同一个线程
  • 异步:当返回用时比较长时,使用同步处理可能会耗尽线程池里的线程。异步可以支持返回使用和请求不同的线程进行处理。这样,也会提高线程池的利用率。

Servlet3.0 开始支持异步。

Spring MVC中的异步实现

web.xml配置

1
2
3
4
5
<servlet>
        <servlet-name>dispatcher</servlet-name>
        ...
        <async-supported>true</async-supported>
</servlet>     

主要就是在dispatcher配置中增加async-supported配置(默认为false)

Controller编写

  • Callable 返回类型,自动放入线程池中执行,等待返回值写入
  • DeferredResult 返回类型,在另一个线程中调用setResult()将返回的值写入。此方法更加灵活,可以设置超时时间、正常响应、错误响应

Filter中的配置

同理,需要在web.xml中对filter的定义增加async-supported配置

注意

异步使用的是不同线程,因此执行的事务不是同一个,在Controller中绑定的ThreadLocal信息也无法在异步线程中获取。

Servlet3.0 支持的异步在处理高并发异步请求时,效率并不高。因为其实没有用到“原生”的异步。Java提供的NIO是标准的异步,但是编程复杂性更高。因此,要实现更高性能的异步IO,一般使用Netty这样的框架(基于NIO提供了更易于使用的API)。

使用WebSocket

WebSocket是一种基于HTTP的长链接技术,连接确认后此连接不会被服务器关闭,可随时互相发消息。

实现

浏览器发送升级为WebSocket的请求头

1
2
3
4
GET /chat HTTP/1.1
Host: www.example.com
Upgrade: websocket
Connection: Upgrade

和服务器握手成功即建立WebSocket连接。服务器也需要底层支持,Java的Servlet规范从3.1开始支持WebSocket。最新版本的Tomcat、Jetty等开源服务器均支持WebSocket。

服务端代码实现

这部分先略,用到了再补充