视图解析:

返回字符串:

controller 方法返回字符串也称为逻辑视图名,通过视图解析器解析为物理视图地址。

物理视图地址 = 视图解析器前缀 + 逻辑视图名 + 视图解析器后缀

1
2
3
4
5
6
7
8
<!--视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/pages/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
</bean>
1
2
3
4
5
@RequestMapping("/saveAccount")
public String saveAccount(User user){
System.out.println(user);
return "success";
}

最终返回的物理视图地址:/WEB-INF/pages/ + success + .jsp = /WEB-INF/pages/success.jsp

返回void?

很少用,默认会查找请求路径结尾的jsp资料

1
2
3
4
@RequestMapping("/void")
public void returnVoid(){
System.out.println("返回值void...");
}

会报404,即便是空值返回也会被视图解析为 void.jsp,当然如果你设置了视图解析器的话

ModelAndView

ModelAndView 是 SpringMVC 为我们提供的一个对象,该对象也可以用作控制器方法的返回值。

两个重要的方法:

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Add an attribute to the model.
* @param attributeName name of the object to add to the model (never {@code null})
* @param attributeValue object to add to the model (can be {@code null})
* @see ModelMap#addAttribute(String, Object)
* @see #getModelMap()
*/
public ModelAndView addObject(String attributeName, @Nullable Object attributeValue) {
getModelMap().addAttribute(attributeName, attributeValue);
return this;
}

1
2
3
4
5
6
7
8
/**
* Set a view name for this ModelAndView, to be resolved by the
* DispatcherServlet via a ViewResolver. Will override any
* pre-existing view name or View.
*/
public void setViewName(@Nullable String viewName) {
this.view = viewName;
}

返回值可以直接通过el表达式获取

controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RequestMapping(value = "/model/view",method = RequestMethod.GET)
public ModelAndView modelAndView(ModelAndView modelAndView){
System.out.println("测试 modelAndView...");
User user = new User();
user.setName("tom");
user.setAge(22);
//保存数据
modelAndView.addObject("clazz","大数据");
modelAndView.addObject("user",user);
//设置视图名称
modelAndView.setViewName("success");
//返回ModelAndView对象
return modelAndView;
}

jsp:

1
2
3
4
5
6
7
8
<body>
<h2>sccess</h2>

class: ${clazz}
username: ${user.name}
age: ${user.age}

</body>

json返回

主要使用@ResponseBody注解

该注解用于将 Controller 的方法返回的对象,通过 HttpMessageConverter 接口转换为指定格式的 数据如:json、xml 等,默认转为json格式,再通过 Response 响应给客户端 。使用json格式的数据进行交互,也是目前前后端分离开发最主要的方式。

Springmvc 默认用 MappingJacksonHttpMessageConverter 对 json 数据进行转换,需要加入 jackson 的包(如果已经添加过,不要重复添加)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 版本管理 -->
<jackson-version>2.11.0</jackson-version>


<!-- 依赖jar -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson-version}</version>
</dependency>

用法:

1
2
3
4
5
6
7
8
9
10
11
12
@RequestMapping(value = "/testResponseBody",method = RequestMethod.POST)
public @ResponseBody User testResponseBody(){
System.out.println("使用 @ResponseBody响应json数据 ...");

User user = new User();
user.setId(1001);
user.setMoney(100.1F);
user.setName("tom");
user.setAge(22);

return user;
}

实际上就是在返回值前面添加约束注解,但是因为要返回值嘛,所以必须要限制访问方式为post请求

拦截器

简介

Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对请求进行预处理和后处理。用户可以自己定义一些拦截器来实现特定的功能。谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。

aejOXQ

说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,都是基于AOP编程思想的体现,都能实现权限检查、日志记录等,但是也有区别,例如:

  • 使用范围不一样
    • 过滤器是 servlet规范中的一部分,只能用于web工程中。
    • 拦截器是 SpringMVC框架自己的,只有使用了 SpringMVC框架的工程才能用。
  • 规范不同:
    • Filter实在servlet规范中定义的,是servlet容器支持的。
    • 拦截器(interceptor)是在Spring容器内的,是Spring框架支持的。
  • 使用资源不同
    • 拦截器是Spring的组件,因此能够使用Spring里的任何资源、对象,而Filter不能
  • 深度不同
    • Filter只在servlet前后起作用,
    • 拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用更具有弹性,因此在Spring架构中优先使用拦截器

如何自定义拦截器?

  1. 编写一个普通类实现 HandlerInterceptor 接口
  2. 配置拦截器
  3. 测试运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class HelloInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {

System.out.println("preHandle...controller之前执行");

return true;
}

@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {

System.out.println("postHandle...jsp之前执行");
}

@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {

System.out.println("afterCompletion...jsp 之后执行");
}

}

继承抽象类不会报错,但是要重写他的三个方法分别是上面的三种

返回true表示允许请求通过

prehandle表示在请求到来之前

posthandle表示在controller执行之后,jsp之前

afterhandle表示在jsp(视图)之后执行

为什么需要在请求到来之前还有个拦截器呢,这个怎么用?答:因为在jsp页面中可以 用 <%%> 这个里面直接写java代码,执行相应的逻辑

创建新项目找不到模板了,maven东西太多了:org.apache.maven.archetypes:maven-archetype-webapp

在springmvc.xml中

1
2
3
4
5
6
7
8
9
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="helloInterceptor"
class="com.ls.intereptor.interectorooo">
</bean>
</mvc:interceptor>
</mvc:interceptors>

/** 这个是过滤所有的controller的意思

什么时候用到?

aejL6g

prehandle

错误拦截示例

1
2
3
4
5
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
request.getRequestDispatcher("WEB-INF/error.jsp").forward(request,response);
return false;
}
  • 返回值特点
    • 该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,可以返回到其他页面,后续的Interceptor 和Controller 都不会再执行;
    • 当返回值为true 时,如果还有别的interceptor实力的话,就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
  • 作用
    • 可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。典型的应用场景:登录拦截、权限校验

posthandle

1
2
3
4
5
6
7
8
9
10
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {

modelAndView.addObject("token","CZXY-2020-07-03");

System.out.println("postHandle...");
}
  • 执行时机
    • preHandle 方法的返回值为true 时才能被调用,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,
    • postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行
  • 作用
    • 可以在这个方法中对Controller 处理之后的ModelAndView 对象和request、response进行操作。

afterCompletion

1
2
3
4
5
6
7
8
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {

System.out.println("afterCompletion...controller之后执行");
}
  • 执行时机
    • 当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。
    • 该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。
  • 作用
    • 这个方法的主要作用是用于进行资源清理、记录日志信息等工作。

拦截器指定配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!--<mvc:mapping>用于配置拦截器作用的路径,该路径在其属性path 中定义。-->
<!--path 的属性值“/**” 表示拦截所有路径,-->
<!--“/hello” 表示拦截所有以 “/hello” 结尾的路径-->
<mvc:mapping path="/**"/>
<!-- 用于指定排除的 url-->
<mvc:exclude-mapping path="/login"/>
<bean id="helloInterceptor1" class="com.ls.mvc.interceptor.HelloInterceptor"></bean>
</mvc:interceptor>

<mvc:interceptor>
<!--指定具体的拦截器,而不是所有-->
<mvc:mapping path="/user/**"/>
<bean id="helloInterceptor2" class="com.ls.mvc.interceptor.HelloInterceptor2"></bean>
</mvc:interceptor>

<mvc:interceptor>
<mvc:mapping path="/user/list"/>
<bean id="helloInterceptor3" class="com.ls.mvc.interceptor.HelloInterceptor3"></bean>
</mvc:interceptor>
</mvc:interceptors>

我们不想用所有拦截器一起生效,只想要我们使用的生效,可以看上面。

不需要拦截的静态资源?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<mvc:resources location="/js/" mapping="/**/*.js"/>
<mvc:resources location="/css/" mapping="/**/*.css"/>
<mvc:resources location="/assets/" mapping="/assets/**"/>
<mvc:resources location="/images/" mapping="/images/**" cache-period="360000"/>

<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/**/fonts/*"/>
<mvc:exclude-mapping path="/**/*.css"/>
<mvc:exclude-mapping path="/**/*.js"/>
<mvc:exclude-mapping path="/**/*.png"/>
<mvc:exclude-mapping path="/**/*.gif"/>
<mvc:exclude-mapping path="/**/*.jpg"/>
<mvc:exclude-mapping path="/**/*.jpeg"/>

<bean class="com.czxy.mvc.interceptor.HelloInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>

在springmvc.xml中加上静态资源放行配置,某个拦截器执行的时候

/**代表第一层所有目录

<mvc:exclude-mapping path=”/user/toadd”/> 这个标签是一个过滤器的放行标签,参数里面填写完其他的请求,其他的请求也会被放行

放行不一定在配置中设置,也可以在程序里面写

多个拦截器执行顺序?

aejjmj

简单来说就是,prehandle按照配置顺序来,也就是 A B C,在执行完controller之后,posthandle 与 aftercompletion过滤规则都是,C B A