Spring MVC 框架
springMVC概念
Spring MVC 是由spring 框架提供的一个web 层框架的解决方法。至于MVC 是什么我在前边文章也有说明,MVC 分别指的是model(模型:三层架构中的服务层,持久层,基本的Bean 实体),View 视图层,也就是我们的前端展示给用户的界面。controller指的是我们的web 层,负责接收请求与处理响应。Spring MVC 本质还是我们熟悉servlet,spring MVC 通过对原生的servlet 进行了在封装,解决我们每一个请求都需要一个servlet 的问题,极大减少了我们重复代码的冗余。它还实现了我们请求参数的封装,减少了我们手动编写封装请求的冗余问题。spring MVC集成请求分发,参数绑定,视图解析,编码过滤等常用的组件,极大提高了我们的开发效率。
![image-20200823174617632 image-20200823174617632](https://i-blog.csdnimg.cn/blog_migrate/2d252d667c32df889e322ec5094bd759.png)
springMVC执行流程分析(面试重点)
![image-20200823175314923](https://i-blog.csdnimg.cn/blog_migrate/290495b8242457267b27be74a9a0df6b.png)
1.当请求通过浏览器来到spring mvc时,请求首先来到 spring MVC的一个核心servlet 组件 DispatchServlet(请求转发)。
2.DisPatcherServlet 组件用于把请求进行转发。
3.转发的请求到达HandlerMapping 处理映射器,通过HandMapping 处理映射器把请求映射到指定的方法中。
4.然后在 我们提供的 controller层的方法进行处理,处理完的请求进行返回。
5.然后通过spring MVC 提供的 View resolver 视图解析器,把响应映射到指定的View 视图,如果在处理请求的方法上配置了@ResponseBody,不会在进行视图解析,而是直接向浏览器响应一段 json 或者 字符串。
6.在View 中把spring 提供的 model 对象中的数据进行渲染。
spring mvc 组件
spring mvc 为我们提供了三大核心组件 dispatcherServlet, handMapping,view resolver。
dispatcherServlet: 本质上是spring mvc 预先写好的一个 servlet, 这个 servlet 会把所有的请求拦下,然后进行请求转发
HandMapping: 处理映射器,负责接收由 disPatherServlet 转发的请求,然后把请求映射到指定的 controller 中的方法去进行处。
HandMappingAdapter: 处理器适配器,采用适配器的设计模式,负责具体执行 controller 中的方法,然后返回 model(spring MVC 封装的一个request 域,传递数据)和view 字符串。
View resolver: 视图解析器,经过处理完controller 处理完的请求,如果没有特别指定返回的json(@ResPonseBody,@RestController)会默认被视图解析器进行处理,把返回的view字符串映射到指定的前端页面视图,然后视图(模板引擎技术 jsp ,thremleaf,freemarker)会把 model 域中的数据渲染到页面。
关于spring MVC 组件在 web.xml 配置(tomacat 读取加载)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 加载Spring容器配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 设置Spring容器加载所有的配置文件的路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-*.xml</param-value>
</context-param>
<!-- 配置SpringMVC核心控制器 -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始配置化文件,前面contextConfigLocation看情况二选一 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-mvc.xml</param-value>
</init-param>
<!-- 启动加载一次 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--为DispatcherServlet建立映射 通过配置请求的DispatchServlet 在 / 路径接收所有请求然后进行请求的转发到 controller 匹配具体请求-->
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<!-- 此处可以可以配置成*.do,对应struts的后缀习惯 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--spring MVC 内部集成的默认servlet 放行静态资源 spring 拥有静态资源拦截器,需要配置放行规则-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.gif</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.woff</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.woff2</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.*</url-pattern>
</servlet-mapping>
<!-- 防止Spring内存溢出监听器 -->
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<!-- 解决工程编码过滤器 过滤所有地址 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置欢迎页面
<welcome-file-list>
<welcome-file>html/index.html</welcome-file>
</welcome-file-list>
-->
</web-app>
spring MVC 常用注解(使用 spring MVC 需要依赖 spring 框架)
@Controller 出现在类上,表明为一个 controller 层,会把对象加入 spring 的IOC 容器。
@RequestMapping: 可以出现在方法或类上,出现在类上表明请求URL的一级路径,出现在方法上代表此方法接收的请求, 默认支持 value(请求路径),method(请求方式),params (请求参数设置限定)
@RequestMapping(value = "/findUserInfoList",method = RequestMethod.GET) @RequestMapping(value="/removeAccount",params= {"accountName","money>100"})
和它相同功能的注解还有 @GetMapping ,@PostMapping,@PutMapping,@DeleteMaping....
@RequestParm:用于把请求参数给控制器中的形参赋值,常出现在前后端参数名不一致的情况下
@RequestMapping("/useRequestParam") public String useRequestParam(@RequestParam("name")String username, @RequestParam(value="age",required=false)Integer age,User user)
spring MVC 默认会自动实现参数绑定,参数绑定可以到集合,基本类型,string,和我们自定义的实体,当然这样就要求我们前端传递参数名要与后端一致,Java实体呢,要求前端参数名与实体类字段对应,这样才能封装到实体类。
@RequestBody:用于获取请求体内容 key=value&key=value,get 方法不支持,不常用到。
@PathVaribale:出现在参数上,用来构建 restful 风格的api。
![]()
![]()
@RequestMapping(value="/testRestPUT/{id}",method=RequestMethod.PUT) public String testRestfulURLPUT(@PathVariable("id")Integer id,User user) @RequestMapping(value="/testRestPOST",method=RequestMethod.POST) public String testRestfulURLPOST(User user) @RequestMapping(value="/testRestDELETE/{id}",method=RequestMethod.DELETE) public String testRestfulURLDELETE(@PathVariable("id")Integer id) @RequestMapping(value="/testRestGET/{id}",method=RequestMethod.GET) public String testRestfulURLGET(@PathVariable("id")Integer id)
@CookieValue: 用于获取cookie 中的数据到形参
@SessionAttribute:用于向 session 中传递数据,一般不使用
@RequestMapping("/useCookieValue") public String useCookieValue(@CookieValue(value="JSESSIONID",required=false) @RequestMapping("/springmvc") @SessionAttributes(value ={"username","password"},types={Integer.class}) public class SessionAttributeController
@ModelAttribute:可以用于修饰方法和参数,出现在方法上表明此方法可以在在控制器方法(请求执行的方法)前执行,出现在方法上,常用来进行执行更新操作的数据库参数(关键数据不传递到前端,在数据库端补全)补全。出现在方法上用来进行参数的绑定。通常我们会把这两结合起来使用。
易混淆的点 Model 对象,Model 对象相当于Request 域,用来进行数据传递。@ModelAttribute 用来进行数据补全参数绑定
---------------------无返回值 map 集合参数补全绑定实例---------------------------- @ModelAttribute public void showModel(String username,Map<String,User> map) { //模拟去数据库查询 User user = findUserByName(username); System.out.println("执行了 showModel 方法"+user); map.put("abc",user); } /** * 模拟修改用户方法 * @param user * @return */ @RequestMapping("/updateUser") public String testModelAttribute(@ModelAttribute("abc")User user) { System.out.println("控制器中处理请求的方法:修改用户:"+user); return "success"; } -------------------有返回值 参数补全绑定实例----------------------------------- @ModelAttribute public User showModel(String username,Map<String,User> map) { //模拟去数据库查询 User user = findUserByName(username); System.out.println("执行了 showModel 方法"+user); return user; } /** * 模拟修改用户方法 * @param user * @return */ @RequestMapping("/updateUser") public String testModelAttribute(@ModelAttribute("user")User user) { // 这里的user 是经过上面进行参数补全的user System.out.println("控制器中处理请求的方法:修改用户:"+user); return "success"; } ----------------Model 对象演示 ----------------------------------------- @RequestMapping("/springmvc") @SessionAttributes(value ={"username","password"},types={Integer.class}) public class SessionAttributeController { /** * 把数据存入 SessionAttribute * @param model * @return * Model 是 spring 提供的一个接口,该接口有一个实现类 ExtendedModelMap * 该类继承了 ModelMap,而 ModelMap 就是 LinkedHashMap 子类 */ @RequestMapping("/testPut") public String testPut(Model model){ model.addAttribute("username", "泰斯特"); model.addAttribute("password","123456"); model.addAttribute("age", 31); //跳转之前将数据保存到 username、password 和 age 中,因为注解@SessionAttribute 中有 这几个参数 return "success"; }
@ResponseBody: 出现在方法,表明此方法不去走视图解析器,响应Json 数据的字符串
@RestController: 相当于 @ResponseBody +@Controller ,把类加入IOC ,并且返回的结果不走视图解析器
ModelAndView 是 SpringMVC 为我们提供的一个对象用来数据传递和设置返回视图
string 类型用于响应视图或者响应json字符串 void 获取原生API 操作 ModelAndView 参数,用来数据传递和设置返回视图 ----------- 响应视图-------------------- controller 方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。 //指定逻辑视图名,经过视图解析器解析为 jsp 物理路径:/WEB-INF/pages/success.jsp @RequestMapping("/testReturnString") public String testReturnString() { System.out.println("AccountController 的 testReturnString 方法执行了。。。。"); return "success"; } ---------- void 类型 采用servlet API 操作------------------- @RequestMapping("/testReturnVoid") public void testReturnVoid(HttpServletRequest request,HttpServletResponse response) throws Exception { } -----------响应 json 字符--------------- @RequestMapping("/testReturnString") @ResponseBody public String testReturnString() { System.out.println("AccountController 的 testReturnString 方法执行了。。。。"); return "{'status':'success'}"; } --------- ModelAndView 对象 ------------------------------- @RequestMapping("/testReturnModelAndView") public ModelAndView testReturnModelAndView() { ModelAndView mv = new ModelAndView(); mv.addObject("username", "张三"); mv.setViewName("success"); return mv; }
@CrossOrigin(origins = "*", maxAge = 3600):跨域注解
Spring mvc 下的请求与重定向
----- 请求转发------------------ @RequestMapping("/testForward") public String testForward() { System.out.println("AccountController 的 testForward 方法执行了。。。。"); return "forward:/WEB-INF/pages/success.jsp"; } 如果用了 formward:则路径必须写成实际视图 url,不能写逻辑视图。 它相当于“request.getRequestDispatcher("url").forward(request,response)”。使用请求转发,既可以转发到 jsp,也可以转发到其他的控制器方法 ------重定向 ------------- @RequestMapping("/testRedirect") public String testRedirect() { System.out.println("AccountController 的 testRedirect 方法执行了。。。。"); return "redirect:testReturnModelAndView"; }它相当于“response.sendRedirect(url)”。需要注意的是,如果是重定向到 jsp 页面,则 jsp 页面不 能写在 WEB-INF 目录中,否则无法找到。
关于spring MVC异常处理的补充
异常被分为运行期异常和预期异常,对于可捕获的预期异常,在三层开发,我们通常不再 dao 层和 service 层直接捕获处理,我们通常直接在这两层向上抛出,统一交由 controller 层捕获处理。spring mvc 支持定义异常在上层处理。
![image-20200823213229555](https://i-blog.csdnimg.cn/blog_migrate/0869fcab0d693d1fa9bff6b6b4739a99.png)
/**
自定义异常处理器
主要是实现HandlerExceptionResolver 接口,实现 resolveException 异常解析方法,返回错误界面,CustomException 为我们自定义 extends Exception 的子类,在这里我们我们仅了解,spring MVC 支持自定义异常即可,自定义异常需要自己实现异常处理器HandlerExceptionResolver中的解析异常resolveException的方法。
注意自定义的异常类我们需要把它加入到 IOC 容器,可以通过注解和配置两种方式。
<bean id="handlerExceptionResolver"
class="com.itheima.exception.CustomExceptionResolver"/>
*/
@Commponent("handlerExceptionResolver")
public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
ex.printStackTrace();
CustomException customException = null;
//如果抛出的是系统自定义异常则直接转换
if(ex instanceof CustomException){
customException = (CustomException)ex; }else{
//如果抛出的不是系统自定义异常则重新构造一个系统错误异常。
customException = new CustomException("系统错误,请与系统管理 员联系!");
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", customException.getMessage());
modelAndView.setViewName("error");
return modelAndView; } }
Spring mvc 还支持文件上传,具体就不再赘述,网上例子有很多。
补充说明: 上面我们介绍了spring MVC 的注解方式,如果使用原生的 ssm 项目,我们需要去在 spring MVC 的配置中配置 注解支持,或者采用上文 spring 说的配置类方式,如果采用 spring Boot ,我们只需要导入 web 模块的 start,其他的都帮我们配置好了。
<!--xml方式 开启 mvc 注解支持 (@Controller 属于spring 的注解) 这里我省略了约束--> <!-- 注解扫描包 --> <context:component-scan base-package="com.hyg.im" /> <!-- 开启注解 --> <mvc:annotation-driven />
过滤器与拦截器区别与联系(面试重点)
过滤器和拦截器都可以用来拦截处理请求,这算是他两相同的地方,其实本质上这两个属于不同的东西。
过滤器 filter:
1. 过滤器依赖于 servlet 容器,属于 Java web 原生API 提供的过滤拦截请求的接口 2. 过滤器在 servlet 初始化时便进行了加载,加载在Servlet 容器中,常用来过滤器中修改字符编码,用户权限,修改 HttpServletRequest 中的一些参数:过滤非法字符。 3. 过滤器无法像拦截器一样,可以通过 spring 注入 bean 对象,进行复杂业务处理。 4. 过滤器主要是对 servlet 请求拦截,支持静态资源拦截处理。 5. 过滤器过滤拦截在前
拦截器 Interceptor:
1. 拦截器依赖于 web 框架,由 web MVC 框架提供实现。在spring 中拦截器基于Spring MVC 框架,基于反射和动态代理,是AOP的典型应用。 2. 拦截器是基于web 框架加载调用,初始化在 IOC 容器中。 3. 可以通过spring 的依赖注入,完成复杂逻辑处理。 4. 拦截器是对 controller 中请求进行拦截处理,对直接请求静态资源的请求不能有效拦截处理 5. 拦截器发会作用在 过滤器执行在 filterChain.doFilter(request,response)之后,在 dispatcherServlet 分发完请求后。
![image-20200823222416663](/Users/stack/Library/Application%20Support/typora-user-images/image-20200823222416663.png)
/**
spring mvc 实现拦截器需要实现HandlerInterceptor
*/
public class LoginIntercetor implements HandlerInterceptor {
/**
* 在进入请求前 controller 拦截 返回值决定是否进入请求
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println(" 尚未执行请求 拦截 还未返回结果 ");
HttpSession session = httpServletRequest.getSession();
Object username = session.getAttribute("username");
// 登录成功
if(username !=null) {return true;}
return false;
}
/**
return ModelAndView之前进行,可以操控Controller 的ModelAndView内容。也就是说在 controller 方法执行完返回之前执行
*/
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println(" 执行完请求 拦截 还未返回结果 ");
}
/**
在过滤器返回给前端前一步执行,也就是在chain.doFilter(request, response)之后执行
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println(" return filter 之后 拦截 ");
}
}
---------- spring MVC 配置文件 拦截器配置 多个组成拦截器链 ----
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/index"/>
<bean class="com.stack.Interceptor.LoginIntercetor"/>
</mvc:interceptor>
</mvc:interceptors>
![image-20200823221536787 image-20200823221536787](https://i-blog.csdnimg.cn/blog_migrate/b67d63ef4f7354b9605c62c5225dcca1.png)
多个拦截器同过滤器一样,可以组成拦截器链,按照拦截器在配置文件中的配置位置先后顺序执行。
使用springMVC带来的好处
上面写了这么多,spring MVC 的好处和易用性也都表现的十分明显。
- 提供了模块的内聚,通过 dispatcherServlet 和 handMapping 让不再一个请求一个 servlet
- 参数绑定的便捷,我们不再需要重复进行请求参数与Java 对象绑定
- 视图解析器和Model 对视图响应和数据传递更容易使用
- 拦截器对过滤器的补充与完善
对原生 servlet API 封装,使我们 web 开发更加高效
> 在搭建ssm 或 springMVC 等设计的web 服务 tomcat 的项目 >要把 需要依赖的 jar 包放到 WEB-INF 的目录下的 lib 目录中只有 >存放在 此位置的第三方依赖才能正常被 tomcat 加载识别到。 ### idea调试Web项目 > idea 调试Java web 项目时,控制台会有三段日志信息可以查看,出现 >错误时要查看这三部分日志定位错误