初识SpringMVC
Servlet的缺点
1、通常情况下,一个Servlet类只负责处理一个请求,若项目中有成百上千个请求需要处理,就需要有成百上千个Servlet类,这样会使得项目中Servlet类的个数暴增;
2、在Servlet3.0版本之前,每一个Servlet都需要在web.xml文件中至少做八行配置信息,配置内容多且繁琐。当Servlet特别多时,web.xml配置量太多,不利于团队开发;
3、当通过客户端提交参数到服务器,通过Servlet进行接收时,无论数据本身是什么格式,在Servlet中一律按照字符串进行接收,后期需要进行类型转换,复杂类型还需要特殊处理,特别麻烦!
String value = request.getParameter(String name);
4、servlet具有容器依赖性,必须放在服务器中运行,不利于单元测试;
SpringMVC简介
Springmvc是spring框架的一个模块,spring和springmvc无需中间整合层整合
Springmvc是一个基于mvc的web框架
SpringMVC五大组件
- 前端控制器(DispatcherServlet)
- 处理器映射器(HandlerMapping)
- 处理器适配器(HandlerAdapter)
- 视图解析器(ViewReslover)
- 视图渲染(View)
SpringMVC执行原理
提示:DispatcherServlet的作用:接收请求,调用其它组件处理请求,响应结果,相当于转发器、中央处理器,是整个流程控制的中心
-
用户发送请求 至 前端控制器(DispatcherServlet);
-
前端控制器(DispatcherServlet)收到请求后调用处理器映射器(HandlerMapping),处理器映射器(HandlerMapping)找到具体的Controller(可以根据xml配置、注解进行查找),并将Controller返回给DispatcherServlet;
-
前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)。处理器适配器经过适配调用具体的Controller;(Controller–> service --> Dao --> 数据库)
Controller执行完成后返回ModelAndView,处理器适配器(HandlerAdapter)将controller执行的结果(ModelAndView)返回给前端控制器(DispatcherServlet);
提示:Model(模型数据,即Controller处理的结果,Map) View(逻辑视图名,即负责展示结果的JSP页面的名字)
-
.前端控制器(DispatcherServlet)将执行的结果(ModelAndView)传给视图解析器(ViewReslover)
视图解析器(ViewReslover)根据View(逻辑视图名)解析后返回具体JSP页面 -
前端控制器(DispatcherServlet)根据Model对View进行渲染(即将模型数据填充至视图中);(默认会存到request域中)
前端控制器(DispatcherServlet)将填充了数据的网页响应给用户。
其中整个过程中需要开发人员编写的部分有Controller、Service、Dao、View;
SpringMVC执行详解
- 在SpringMVC框架启动之初,程序会对
处理器映射器(HandlerMapping)的进行实例化,其中处理器映射器(HandlerMapping)中会有一个map的结构类型.其中map中的key存储的是你要拦截请求路径,value存储的是
你要具体要执行的请求方法. 所以在SpringMVC框架启动之初,
处理器映射器(HandlerMapping)会把所有请求的注解,和他标识的相关方法进行统一的记录. - 如果有用户发来请求,首先会被前端控制器(DispatcherServlet所拦截,前端控制器(DispatcherServlet)收到请求后调用处理器映射器(HandlerMapping),通过相关路径匹配,找到具体的Controller(可以根据xml配),并将Controller返回给DispatcherServlet;
- 前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)。处理器适配器根据你标识的注解,调用相对应的handler处理器,由这个handler处理器来做具体的调用.Controller;(Controller–>service --> Dao --> 数据库) 最后将controller执行的结果(ModelAndView)对象返回给前端控制器(DispatcherServlet);
- 前端控制器(DispatcherServlet)将执行的结果(ModelAndView)传给视图解析器(ViewReslover)
视图解析器(ViewReslover)根据View(逻辑视图名)解析后返回具体JSP页面 - 前端控制器(DispatcherServlet)根据Model对View进行渲染(即将模型数据填充至视图中); 前端控制器(DispatcherServlet)将填充了数据的网页响应给用户。
SpringMVC流程
SpringMVC拦截器
工作原理
Spring MVC中的拦截器基于回调机制,可以在目标方法执行之前,先进行业务检测,满足条件则放行,不满足条件则进行拦截,拦截器原理分析如下图所示:
SpringMVC拦截器工作原理
HandlerInterceptor
案例
HandlerInterceptor 拦截器
@Component //spring容器管理对象
public class UserInterceptor implements HandlerInterceptor {
@Autowired
private JedisCluster jedisCluster;
//Spring版本升级 4 必须实现所有的方法 spring 5 只需要重写指定的方法即可.
/**
* 需求: 拦截/cart开头的所有的请求进行拦截.,并且校验用户是否登录.....
* 拦截器选择: preHandler
* 如何判断用户是否登录: 1.检查cookie信息 2.检查Redis中是否有记录.
* true : 请求应该放行
* false: 请求应该拦截 则配合重定向的语法实现页面跳转到登录页面 使得程序流转起来
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.判断用户是否登录 检查cookie是否有值
String ticket = CookieUtil.getCookieValue(request,"JT_TICKET");
//2.校验ticket
if(!StringUtils.isEmpty(ticket)){
//3.判断redis中是否有值.
if(jedisCluster.exists(ticket)){
//4.动态获取json信息
String userJSON = jedisCluster.get(ticket);
User user = ObjectMapperUtil.toObj(userJSON,User.class);
request.setAttribute("JT_USER",user);
return true;
}
}
response.sendRedirect("/user/login.html");
return false;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//销毁数据
request.removeAttribute("JT_USER");
}
}
WebMvcConfigurer— springmvc 配置类
@Configuration
public class MvcConfigurer implements WebMvcConfigurer{
//添加拦截器
@Autowired
private UserInterceptor userInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userInterceptor).addPathPatterns("/cart/**","/order/**");
}
}
各种拦截器Filer
SpringMVC基于Servlet进行封装的框架
关于SpringMVC参数提交问题说明
1 简单参数传参问题
1.页面url标识
2.Controller中的方法
@RequestMapping("/xxx")
public void xxx(String name,int age){
}
2 使用对象方法接收参数
1.页面url标识
2.Controller中的方法
@RequestMapping("/xxx")
public void xxx(User user){
}
public class User{ //pojo类
private Integer name;
private String age;
}
3 使用对象的引用为参数赋值
难点: 属性的重名提交问题…
解决思路: 可以采用对象引用的方式为属性赋值.
<input name="name" value="二郎神" />
<input name="age" value="3000" />
<input name="dog.name" value="哮天犬" />
<input name="dog.age" value="8000" />
Controller中的方法
@RequestMapping("/xxx")
public void xxx(User user){
}
public class Dog{
private String name;
private Integer age;
}
public class User{
private String name;
private Integer age;
private Dog dog;
}
不同注解的含义
- @RequestMapping(“/xxx”) 页面url路径
- @GetMapping(“/xxx”) 页面get请求 才会拦截
- @PostMapping(“/xxx”) 页面post请求 才会拦截
Spring MVC 接收参数的几个注解
-
@RequestParam 键值对参数,只要前端参数name属性,和后端接收名字的一致,新版本Springboot项目中.注解可省略.
-
@PathVariable 请求路径参数 ,运用restfun风格{module} 注解不可省略
-
@RequestBody 完整接收post请求协议的内容,转成pojo集合
springmvc响应数据
Model的使用
当请求发起访问Controller中的方法时,可以通过参数声明,在方法内使用Model。
@RequestMapping("/doorList")
public String doorList(Model model){
}
Model对象实际上是一个Map集合,例如:往model中添加一个属性
model.addAttribute(String name, Object value);
其中,addAttribute方法会将属性保存到request域中,再通过转发将属性数据带到相应的JSP中,通过${}取出并显示。
案例,往Model中添加属性
@RequestMapping("/testModel")
public String testModel(Model model){
/* 往Model添加属性 */
model.addAttribute("name", "小雨");
model.addAttribute("age", 20);
return "home";
}
<body>
<h1>hello springmvc~~~</h1>
${ name } <br/>
${ age }
</body>
实际代码
controller
@RequestMapping("/{itemid}")
public String findItemById(@PathVariable Long itemid, Model model){
Item item= dubboItemService.findItemById( itemid);
ItemDesc itemDesc= dubboItemService.findItemDescById(itemid);
model.addAttribute("item", item);
model.addAttribute("itemDesc",itemDesc);
return "item";
}
item.jsp
转发(forward)和重定向(redirect)
实现转发(forward)
在request对象的学习中,通过request对象可以实现请求转发(即资源的跳转)。同样的,springmvc也提供了请求转发的方式,具体实现如下:
@RequestMapping("testForward")
public String testForward(){
System.out.println("测试请求转发(forward)...");
return "forward:hello";
}
- 转发是一次请求,一次响应;
- 转发后地址栏地址没有发生变化(还是访问testForward的地址);
- 转发前后的request和response对象也是同一个。
实现重定向(redirect)
在response对象的学习中,通过response对象可以实现请求重定向(即资源的跳转)。
同样的,springmvc也提供了请求重定向的方式,具体实现如下:
@RequestMapping("testRedirect")
public String testRedirect(){
System.out.println("测试请求重定向(redirect)...");
return "redirect:hello";
}
- 重定向是两次请求,两次响应;
- 重定向后地址栏地址发生了变化(变为转发后的地址);
- 并且在重定向前后,request和response对象不是同一个。
RestFul结构
1 请求说明
- Get请求 http://localhost:8080/user/restFul?id=1&name=张三
- RestFul请求: http://localhost:8080/user/restFul/1/张三
2 RestFul语法
-
URL中请求的参数使用/分割
用户写的: http://localhost:8080/user/restFul/1/张三 -
服务器端参数接收时,变量使用{xx}进行包裹,并且位置固定.
-
利用@PathVariable注解,动态获取路径中的数据,要求名称必须匹配
/**
* RestFul结构 动态接收参数
* url: http://localhost:8080/user/restFul/1/张三
* 返回值: 返回User对象
* 难点: 需要从URL的路径中获取参数!!!
* 语法:
* 1.服务器端参数接收时,变量使用{xx}进行包裹,并且位置固定.
* 2.利用@PathVariable注解,动态获取路径中的数据,要求名称必须匹配
*/
@RequestMapping("/restFul/{id}/{name}")
public User restFul(@PathVariable Integer id,
@PathVariable String name){
User user = new User();
user.setId(id);
user.setName(name);
return user;
}
3 RestFul结构对象传参
/**
* URL: http://localhost:8080/user/restFul2/1/王五/18
* RestFul对象接收:
* 如果对象的属性与{key}相同,则可以使用对象接收.
* 用途: restFul结构 一般用于更新操作
* @param user
* @return
*/
@RequestMapping("/restFul2/{id}/{name}/{age}")
public User restFul2(User user){
return user;
}
4 请求类型与业务的关系
规则: 随着业务深入,请求类型与业务渐渐的绑定.
- 新增: post请求类型 @PostMapping(“”)
- 删除: delete请求类型 @DeleteMapping(“”)
- 修改: put请求类型 @PutMapping(“”)
- 查询: get请求类型 @GetMapping(“”)