文章目录
SpringMVC的Maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
DispatcherServlet
DispatcherServlet
称为前端控制器,它是springMVC的核心,本质是一个servlet,我们需要在web.xml配置。它需要一个WebApplicationContext
的对象作为配置来发现它需要的bean,并使用这些bean进行请求映射、视图解析、异常处理等等。所谓WebApplicationContext
是一个继承了ApplicationContext
接口,在WebApplicationContext
内有一个到ServletContext
和它关联的Servlet的链接,它将Spring和web连接了起来。
DispatcherServlet的初始化参数
参数 | 说明 |
---|---|
contextClass | 实现了ConfigurableWebApplicationContext的类,将由这个Servlet实例化并在本地配置。默认情况下,使用XmlWebApplicationContext。 |
contextConfigLocation | 指定XmlWebApplicationContext的配置文件 |
namespace | WebApplicationContext的命名空间。默认为servlet-name servlet。 |
throwExceptionIfNoHandlerFound | 当未找到请求的处理程序时,是否抛出NoHandlerFoundException。然后可以使用HandlerExceptionResolver捕获异常(例如,通过使用@ExceptionHandler控制器方法),并像处理其他异常一样处理该异常。默认情况下,这被设置为false,在这种情况下DispatcherServlet将响应状态设置为404 (NOT_FOUND)而不会引发异常。注意,如果还配置了默认servlet处理,则未解析的请求总是被转发到默认servlet,并且不会引发404。 |
ContextLoader
ContextLoader
类允许我们声明式的构造ApplicationContext
对象,ContextLoaderListener
继承了该类并实现了Servlet API中的监听器接口,它允许我们在web环境中以声明式的方式构造ApplicationContext
对象。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
该监听器会使用contextConfigLocation
属性指定的配置文件初始化IOC容器,如果找不到此属性则默认使用/WEB-INF/applicationContext.xml
文件作为配置文件。
WebApplicationContext的层级
对于许多应用程序来说,只有一个WebApplicationContext
就足够了。当然也可以有一个上下文层次结构,其中一个根WebApplicationContext
在多个DispatcherServle
实例之间共享,每个实例都有自己的子WebApplicationContext
配置,并且根WebApplicationContext
中的配置对于子WebApplicationContext
配置是完全可见的,反之则不然。
通过 ContextLoader
类的支持,我们很容易配置出这种上下文结构:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:webmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
如果不需要这种上下文结构,那么在配置DispatcherServlet
时将contextConfigLocation
设置为空,只配置根WebApplicationContext
就可以了。值得注意的是不能省略此参数,否则 ContextLoader
就会从默认位置搜索配置文件从而引发异常。
依赖bean
前文提到DispatcherServlet
需要依赖一些bean处理请求并呈现适当的响应,这些bean的类型如下:
类型 | 说明 |
---|---|
HandlerMapping(处理器映射器) | 将请求映射到一个处理器,以及一组用于预处理和后处理的拦截器。 |
HandlerAdapter(处理器适配器) | 帮助前端控制器调用处理器映射器映射到的处理器。 |
ViewResolver(视图解析器) | 解析从处理器返回到实际视图的基于逻辑字符串的视图名,并将其呈现给响应。 |
MultipartResolver(文件上传解析器) | 处理文件上传 |
HandlerExceptionResolver(处理器异常解析器) | 解决处理器内出现的异常 |
掌握了这五种特殊的bean就掌握了SpringMVC,下面开始。
HandlerMapping
映射规则
映射规则 | 说明 |
---|---|
? | 匹配一个字符 |
* | 匹配零个或多个字符 |
/** | 匹配零个或多个路径段,直到路径结束。 |
/{spring} | 匹配路径段并将其捕获为名为spring的变量 |
/{spring:regexp} | 将正则表达式匹配为名为spring的路径变量 |
/{*spring} | 匹配零个或多个路径段,直到路径结束,并将其捕获为名为“spring”的变量。 |
映射原理
每一个请求都会经过DispatcherServlet
前端控制器的处理,在doService
方法中总会调用一个doDispatch
,而这个方法就是路径映射的核心。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//检查是否是文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//确定当前请求的处理程序。
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//确定当前请求的处理程序适配器。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 如果处理程序支持,则处理最后修改的头文件。
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 实际调用处理程序。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
在doDispatch
方法中通过调用getHandler
方法选择处理器方法:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
在该方法中默认有以下处理器映射器:
这些处理器映射器保存了所有的映射规则比如:
- RequestMappingHandlerMapping:保存了每个处理器类的每个处理器方法都能处理什么请求。
- WelcomePageHandlerMapping:保存了欢迎页的映射规则。
当请求到来时该方法会挨个尝试这些处理器映射器,看是否有请求的映射信息,如果有就返回这个请求对应的处理器。
自定义HandlerMapping
Handler
处理器是我们开发中要编写的控制层代码。由它对具体的请求进行处理并返回视图信息。被@Controller
注解标注的类为一个处理器类。在处理器类中被@RequestMapping
注解标注的方法为处理方法。
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface RequestMapping {
String[] value() default {};//请求路径
String[] path() default {};//请求路径
RequestMethod[] method() default {};//请求方式
//限制请求参数
//param 请求参数必须包含param
//!param 请求参数不能包含param
//param=value param必须等于value
//param!=value param不能等于value
String[] params() default {};
String[] headers() default {};//限制请求头
String[] consumes() default {};//限制Content-Type的值
String[] produces() default {};//限制Accept的值
}
它还有以下兄弟注解:
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
处理器方法有一个灵活的签名,可以从一系列受支持的控制器方法参数和返回值中进行选择。
参数
在下文的注解中,大部分是基于字符串的,但如果声明的方法参数类型是非字符串,此时就应该进行类型转换,对于这种情况,类型转换将根据配置的转换器自动应用。默认情况下支持简单类型。可以通过WebDataBinder
或通过在FormattingConversionService
中注册格式化器来定制类型转换。
@RequestParam
@RequestParam注解可以将Servlet请求参数绑定到控制器中的方法参数。
@AliasFor("name")
String value() default "";//参数名
@AliasFor("value")
String name() default "";
boolean required() default true;//是否必须
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
@PathVariable
@PathVariable可以将URL模板变量绑定到方法参数上。
@AliasFor("name")
String value() default "";//参数名
@AliasFor("value")
String name() default "";
boolean required() default true;//是否必须
@RequestHeader
@RequestHeader注解可以将请求头绑定到控制器中的方法参数。
@AliasFor("name")
String value() default "";//参数名
@AliasFor("value")
String name() default "";
boolean required() default true;//是否必须
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
@CookieValue
@CookieValue注解可以将HTTP Cookie的值绑定到控制器中的方法参数。
@AliasFor("name")
String value() default "";//参数名
@AliasFor("value")
String name() default "";
boolean required() default true;//是否必须
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
@SessionAttributes
@SessionAttributes
注解可以将模型中的属性添加到Session域中。
@Target({ElementType.TYPE})
public @interface SessionAttributes {
@AliasFor("names")
String[] value() default {};
@AliasFor("value")
String[] names() default {};
Class<?>[] types() default {};
}
@SessionAttribute
@SessionAttribute
注解可以将Session域中的值绑定到方法参数。
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
@RequestAttribute
@RequestAttribute可以将HTTP Request的值绑定到控制器中的方法参数。
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
@RequestBody
可以使用@RequestBody
注解来读取请求体,该注解通过HttpMessageConverter
将请求体反序列化为一个对象注入到方法参数上。
boolean required() default true;
@ModelAttribute
可以在方法参数上使用@ModelAttribute
注解来访问模型中的属性,它有以下作用:
- 将模型中的属性绑定到方法参数中。
- 将与方法参数同名的请求参数绑定到方法参数。
- 将与方法参数属性同名的请求参数绑定到方法参数属性上。
@Target({ElementType.PARAMETER, ElementType.METHOD})
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean binding() default true;
数据绑定可能导致错误。默认情况下,引发bindeexception
。如果要在控制器方法中检查此类错误,您可以在@ModelAttribute
旁边立即添加一个BindingResult
参数:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
默认情况下,任何不是简单值类型的参数并且没有由任何其它参数解析器解析的参数都将被视为使用了@ModelAttribute
。
Multipart
启用MultipartResolver
后,可以使用该参数接收POST请求的内容。
@Controller
public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(@RequestParam("name") String name,
@RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
byte[] bytes = file.getBytes();
// store the bytes somewhere
return "redirect:uploadSuccess";
}
return "redirect:uploadFailure";
}
}
返回值
返回值可以是一个String类型的视图模型也可以是Void,表示没有响应体等等。
@ModelAttribute
被@ModelAttribute注解注释的方法会在此控制器类的每个方法执行前被执行,主要有以下几种使用情况:
- 用在普通Void方法。
- 用在普通的具有返回值的方法:此时会把方法的返回值添加到模型属性中,属性名默认为返回值类名的首字母小写。
- 用在控制器方法:此时的返回值会被解析为模型中的属性,属性名可以有该注解的参数指定。
@ResponseBody
@ResponseBody
注解可以将方法返回值通过HttpMessageConverter
序列化作为响应体返回。
HandlerAdapter
在doDispatch
方法中确定了一个处理器之后就要使用getHandlerAdapter
方法确定一个处理器适配器:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
在该方法中试用了以下默认处理器适配器:
这些处理器可以适配满足自身条件的处理器,比如第一个适配器支持标注有@RequestMapping
注解的处理器。找到适配器后就将适配器放回。接着就在在doDispatch
方法中调用处理器适配器的Handler方
法执行真正的处理器:
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
该方法的重要工作就是将请求参数正确的传递给处理器,以及将处理器的返回值封装为一个ModelAndView
对象。在这两个过程中参数解析器和返回值处理器起到了中岛的作用。
参数解析器
在上文中提到了处理器方法的各种参数,而这些参数都有专门的参数解析器进行解析。
这些参数解析器会获得所有参数的详细信息来判断是否满足解析条件,满足条件就进行解析赋值。比较特殊的是前文提到的@ModelAttribute
可以将自定义类型作为处理器方法参数,与自定义类型参数对应的ServletModelAttributeMethodProcessor
解析器就负责解析它们,该解析器会利用WebDataBinder
进行绑定。
WebDataBinder
由于HTTP报文都会以String类型存在,所以WebDataBinder
的作用就是利用下面转换器的将这些String类型的报文转换为指定类型封装到自定义方法参数中。
自定义converter
将我们自定义的converter添加到WebDataBinder
就可以实现自定义转换。
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new MyConverter());
}
}
class MyConverter implements Converter<String,Student>{
@Override
public Student convert(String source) {
...
return null;
}
}
返回值处理器
当处理器方法执行完成时处理器适配器就会根据返回值类型选择某个下面的返回值处理器进行处理。
比较特殊的是在使用@ResponseBody
注解的时候会将返回值直接转换为响应报文响响应给浏览器。此时RequestResponseBodyMethodProcessor
就会通过HttpMessageConverter
进行java对象到报文的转换。实际上在使用@RequestBody
注解时也会使用HttpMessageConverter
进行请求报文到java对象的转换。
内容协商
在进行转换之前首先要确定浏览器可以接收什么样的媒体类型报文,然后根据自身能力能生产什么样的媒体类型并且从中选择一个最佳的匹配类型返回,这一过程叫做内容协商。内容协商有两种策略:
- 基于请求头的策略(默认):根据请求头中的
Accept
字段确定客户端可以接收的媒体类型。比如:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
- 基于请求参数的策略:根据浏览器的请求参数确定客户端可以接收的媒体类型。可以通过以下配置开启:
spring:
mvc:
contentnegotiation:
favor-parameter: true
开启之后我们在发送请求的时候添加一个format就可以选择返回的媒体类型:
http://localhost:8080/test?format=json
但是基于请求参数的策略在默认情况下只支持json
和xml
,如果还想支持其它类型,此时可以自定义内容协商策略。
自定义内容协商策略
@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
HashMap<String, MediaType> stringMediaTypeHashMap = new HashMap<>();
stringMediaTypeHashMap.put("json",MediaType.APPLICATION_JSON);
stringMediaTypeHashMap.put("xml",MediaType.APPLICATION_XML);
stringMediaTypeHashMap.put("student",MediaType.parseMediaType("application/student"));
ParameterContentNegotiationStrategy parameterContentNegotiationStrategy = new ParameterContentNegotiationStrategy(stringMediaTypeHashMap);
configurer.strategies(Arrays.asList(parameterContentNegotiationStrategy));
}
}
HttpMessageConverter
内容协商之后根据确定好的媒体类型选择相应的HttpMessageConverter
进行转换,
自定义HttpMessageConverter
@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MyMessageConverter());
}
}
class MyMessageConverter implements HttpMessageConverter<Student>{
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return clazz.isAssignableFrom(Student.class);
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return MediaType.parseMediaTypes("application/student");
}
@Override
public Student read(Class<? extends Student> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
public void write(Student student, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
String std = "student"+student.getName();
OutputStream body = outputMessage.getBody();
body.write(std.getBytes());
}
}
View Resolver
处理器适配器的Handler
方法执行完成后会返回一个ModelAndView
对象,该对象包括模型数据和视图地址。之后在doDispatch
方法内继续调用processDispatchResult
方法并将ModelAndView
对象传入,在该方法内处理该对象并决定页面如何响应。该方法的执行步骤大致如下:
- 根据椅子条件挑选一个合适的视图解析器。
- 根据视图解析器得到View对象。
- 视图对象调用
render
方法进行页面渲染。
常见的视图解析器如下:
视图解析器 | 说明 |
---|---|
UrlBasedViewResolver | 可以将视图名称直接解析为一个URL而不需要做其它映射,视图名可以是资源url本身,也可以由指定的前缀或后缀扩展。 |
InternalResourceViewResolver | UrlBasedViewResolver一个子类 |
BeanNameViewResolver | 将视图名称解析为一个上下文中的bean名称。 |
配置视图解析器就是向Spring中添加bean,可以配置多个视图解析器,它们将以规定的顺序进行解析,如果最终没有匹配的视图,那么视图解析器将返回null。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--视图所在文件目录-->
<property name="prefix" value=""></property>
<!--视图文件后缀名-->
<property name="suffix" value=""></property>
</bean>
转发和重定向
视图名中的特殊redirect:
前缀允许执行重定向,forward
前缀允许执行转发。
@Controller
public class PersonController {
@GetMapping("/index")
public String index(){
return "forward:/error";
}
@RequestMapping(path = "/error")
public String handle(HttpServletRequest request) {
return "error";
}
}
视图控制器
当控制器方法内只进行视图跳转而没有其它操作时,可以使用视图控制器代替:
<mvc:view-controller path="/" view-name="index"/>
静态资源访问
由与我们的控制器内没有实现静态资源的路径映射,所以在客户端访问静态资源时还是交给DispatcherServlet
处理,那么就会造成静态资源访问不了的问题,此时就应该使用Tomcat配置的默认Servlet处理静态资源请求:
<mvc:default-servlet-handler/>
HandlerInterceptor
拦截器主要用于拦截匹配路径的请求。
//该方法在请求处理之前执行,只有此方法返回true才能执行后续的拦截器和控制器。
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
//该方法在处理请求之后,视图返回渲染之前执行
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
//该方法在请求结束之后执行,用于资源清理
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
demo
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandler");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandler");
}
}
配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="cn.superstallion.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
执行原理
getHandler方
法寻找控制器的同时还会寻找适用于此请求的拦截器。- 在控制器执行之前先顺序执行拦截器的
preHandle
方法,如果返回为true则执行下一个拦截器的preHandle
方法,然后执行控制器方法,控制器方法执行完成之后倒序调用所有拦截器的postHandle
方法。 - 然后进行视图渲染,渲染完成之后倒序执行所有已经执行的拦截器的
afterCompletion
方法。 - 如果
preHandle
方法返回为false或前面的步骤有任何异常也会倒序执行所有已经执行的拦截器的afterCompletion
方法。
MultipartResolver
MultipartResolver是一种用于解析包括文件上传在内的多部分请求的策略。当接收到内容类型为multipart/form-data的POST时,解析器将解析内容,并将当前HttpServletRequest包装为MultipartHttpServletRequest,以提供对已解析文件的访问,同时将部分作为请求参数公开。
配置
首先在web.xml中配置multipart-config
:
<multipart-config>
<!--上传文件存储位置-->
<location>C:\Users\Administrator\Downloads\基础\代码\SSM\upload</location>
<!--单个文件的最大值-->
<max-file-size>5242880</max-file-size>
<!--一个请求文件的最大值-->
<max-request-size>20971520</max-request-size>
<!--文件存储阈值-->
<file-size-threshold>0</file-size-threshold>
</multipart-config>
然后在mvc配置文件中添加解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>
HandlerExceptionResolver
如果在请求映射期间发生异常或从请求处理程序抛出异常,前端控制器将委托给处理器异常解析器链来解决异常并提供替代处理。
实现类 | 说明 |
---|---|
DefaultHandlerExceptionResolver | 默认异常处理器 |
SimpleMappingExceptionResolver | 自定义异常处理器 |
@Controller
和@ControllerAdvice
类可以具有@ExceptionHandler
方法来处理控制器方法中的异常,该注解用在@Controller
内,进行处理异常的方法必须和出现异常的方法在同一控制器内。如果用在@ControllerAdvice
内可以作为全局异常处理器。
@ExceptionHandler
@Target({ElementType.METHOD})
Class<? extends Throwable>[] value() default {};//指定要捕获的异常类
@ControllerAdvice
@Target({ElementType.TYPE})
String[] basePackages() default {};//精确处理范围
Class<? extends Annotation>[] annotations() default {};
CORS
@CrossOrigin
使用此注解可解决跨域问题。默认情况下允许所有域名、请求头和请求方法。
@Target({ElementType.TYPE, ElementType.METHOD})
String[] origins() default {};//允许的跨域域名
String[] allowedHeaders() default {};//允许的请求头
RequestMethod[] methods() default {};//允许的请求方法
long maxAge() default -1L;//最大连接时常
全局配置
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600);
// Add more mappings...
}
}
过滤器
protected Filter[] getServletFilters() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
CorsFilter filter = new CorsFilter(source);
return new Filter[]{filter};
}