Java进阶笔记(四):SpringMVC相关

springMVC是什么:spring提供的用于简化web开发的框架。

--------------------------------

view:视图(html)
Controller:只负责接收请求、转发请求
service:处理业务逻辑
Dao:操作数据库

经典三层(代码架构):
表现层(view、controller)
业务层(service)
Dao层(dao)

MVC模式(代码组织方式/形式):
Model模型(数据模型[pojo、vo、po]+业务模型[业务逻辑]):
View视图(jsp、html):
Controller控制器(servlet):

pojo分为:
vo:value Object,向前台传递数据的
po:process Object,向数据库持久化数据

【SpringMVC是表现层的框架,view和controller之间靠数据模型model交互数据。】

SpringMVC属于web模块,是spring框架的一部分,是后续才出现的。
SpringMVC通过一套注解,让一个简单的java类成为处理请求的控制器,而无需实现任何接口;同时它还支持RESTful编程风格的请求。
SpringMVC是为了解决表现层问题的web框架,主要职责是处理前端HTTP请求。


原生servlet模式:
一个客户端中有多个servlet。

SpringMVC模式:
一个客户端中只有一个DispatcherServlet(前端控制器),这个servlet对应多个Controller。

================================

SpringMVC搭建流程:
1.使用idea创建maven->maven-archetype-webapp项目
2.在main文件夹下创建java文件夹和resources文件夹,并配置好这两个文件夹的类型。(Mark Directory as,java文件夹选Sources Root,resources文件夹选Resources Root)
3.在pom.xml中引入spring webmvc的依赖
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.1.12.RELEASE</version>
</dependency>
4.在web.xml中配置servlet标签,class是DispatcherServlet
<servlet>
  <servlet-name>springmvc</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>springmvc</servlet-name>
  <url-pattern>/<url-pattern>
  <!--
  方式一:带后缀,比如*.action *.do *.aaa
  方式二:/ 不会拦截.jsp (这里用这个)
  方式三:/* 拦截所有,包括.jsp
  -->
</servlet-mapping>

5.创建一个java类,使用@Controller注解标注类;使用@RequestMapping注解设置接收请求的url;在方法中使用ModelAndView对象设置要跳转到的前端页面。(ModelAndView对象有addObject("UserName","abc")方法可以设置key与value,前端使用${UserName}获取;setViewName("/WEB-INF/jsp/success.jsp")方法设置要跳转到的页面。)

6.在Resources文件夹下创建一个springmvc.xml,在其中配置以下信息:
<!-- 开启Controller扫描 -->
<context:component-scan base-package="com.xxx.test.controller" />
<!-- 配置springmvc的视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="/WEB-INF/jsp/" />
  <property name="suffix" value=".jsp" />
</bean>

7.然后回到Controller.java,就可以省略要跳转的页面url的前后缀为:modelAndView.setViewName("success");

8.再回到springmvc.xml,可以配置处理器映射器和处理器适配器(如果不配置,会使用默认的)
<!-- 自动选择最合适的处理器映射器,处理器适配器 -->
<mvc:annotation-driven/>

9.回到web.xml,使用init-param标签将springmvc.xml引入进去,写在Servlet标签中:
<servlet>
  <servlet-name>springmvc</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc.xml</param-value>
  </init-param>
</servlet>

--------------------------------

springmvc搭建流程总结:
1.配置DispatcherServlet前端控制器
2.开发处理具体业务逻辑的Handler(@Controller、@RequestMapping)
3.xml配置文件配置controller扫描,配置springmvc三大件
4.将xml文件路径告诉springmvc(DispatcherServlet)

================================

前端控制器(DispatcherServlet,接收所有url请求,选择Controller,选择返回的页面或直接返回消息):
负责接收用户请求,分发、获取处理结果后返回给用户。

springmvc核心三大件:
处理器映射器(HandlerMapping,类似map,装Controller)、处理器适配器(HandlerAdapter,找到具体执行的Handler并执行,返回ModelAndView)、视图解析器(ViewResolver,将逻辑视图地址转为物理视图地址,返回view对象)。

Handler:在@Controller标注的类中,每个标注@RequestMapping的方法,都是一个Handler。

SpringMVC九大组件:
HandlerMapping(处理器映射器):类似map,用于存储handler,寻找目标handler。
HandlerAdapter(处理器适配器):用于适配不同的handler执行。(因为@RequestMapping标识的方法接收参数时,可以是bean,可以是String等,需要适配)
ViewResolver(视图解析器):用于将String类型的视图名和Locale解析为View类型的视图。
HandlerExceptionResolver:处理Handler产生的异常情况。
RequestToViewNameTranslator:从请求中获取ViewName。
LocaleResolver:用于国际化处理。
ThemeResolver:用于解析主题。
MultipartResolver:用于上传请求。
FlashMapManager:用于重定向时的参数传递。

url-pattern配置问题:
<url-pattern>/</url-pattern>
不会拦截.jsp,但是会拦截.html以及其它静态资源。
静态资源配置,解决方法1:
在springmvc.xml中,配置一个标签:
<mvc:default-servlet-handler />
<!-- 原理:添加这个标签后,会在springMVC上下文中定义一个DefaultServletHttpRequestHandler对象,会对url请求过滤筛查,如果发现是一个静态资源请求,那么就会把请求转由web应用服务器(tomcat)默认的DefaultServlet来处理,如果不是静态资源请求,那么继续由springMVC框架处理 -->
注意:
需要把静态资源放在webapp下(但不在WEB-INF下)

静态资源配置,解决方法2(springMVC框架自己处理静态资源):
在springmvc.xml中,配置:
<!-- mapping中的值表示,url以resources开头的,则去location的值那里找。【classpath的指resources文件夹】 -->
<!-- location可以用逗号分隔,例如:"/,classpath:/" -->
<mvc:resources location="classpath:/" mapping="/resources/**" />
<mvc:resources location="/WEB-INF/js/" mapping="/js/**" />

--------------------------------

@RequestMapping标识的方法(即handler)接收参数时,可以使用Model、Map、ModelMap,以及直接用参数变量接收;之后可以在Model、Map、ModelMap中放入value,前端jsp使用${key}获取值。
Model、Map、ModelMap这三种类型,运行时的具体类型都是BindingAwareModelMap,相当于用这些Map保存的数据都会放入Request域中。
BindingAwareModelMap继承了ExtendedModelMap,ExtendedModelMap继承了ModelMap,实现了Model接口。

--------------------------------

1.如果要在springMVC中使用servlet原生对象,比如HTTPServletRequest/HttpServletResponse/HttpSession,直接在Handler方法形参中声明使用即可
2.接收简单数据类型参数,直接在handler方法的形参中声明即可,框架会取出参数值然后绑定到对应参数上。【注意参数名称要一致】
如果参数不一致,可以这么写:
@RequestParam("ids")Integer id
*使用时最好使用包装类型(Integer,而不是int),防止请求参数为null时后台报错。
*对于Boolean类型的参数,请求时的参数值只能为true/false/1/0,否则后台会报错。

3.handler如何绑定pojo类型参数:直接形参声明即可,类型就是pojo的类型,形参名无所谓。
*要求传递的参数名需要和pojo的属性名保持一致

4.handler如何绑定包装对象参数(一个pojo嵌套另一个pojo):要求传递的参数按规则(pojo.参数名),例如:
url:http://127.0.0.1:8080/deal?user.id=1&user.name=abc&mid=1

5.handler如何绑定日期类型参数:
可以写一个转换用的类(自定义类型转换器),继承Converter接口;写好后,在springmvc.xml中注册,并与<mvc:annotation-driven/>关联
<mvc:annotation-driven conversion-service="conversion-serviceBean" />

--------------------------------

restful风格请求是什么样的?
restful特性:资源 表现层 状态转移
rest是一个url请求的风格,基于这种风格设计请求的url
没有rest的话,原有的url设计:
url中定义了动作,参数具体锁定到操作的是谁。(http://localhost:8080/user/queryUserById.action?id=1)

有了rest风格之后:
rest认为互联网中的所有东西都是资源,既然是资源,就会有一个唯一的url标识它
http://localhost:8080/user/1  代表的是id为1的用户记录(资源)
锁定资源之后如何操作它呢?常规操作就是增删改查
根据请求方式不同,代表要做不同的操作
get 查询资源
post 增加,新建资源
put 更新
delete 删除资源

rest风格带来的直观体现:传递参数的变化
参数是url的一部分(参数可以在uri中)


对Restful风格请求支持:
注解@PathVariable
@RequestMapping(value="/handle/{id}",mathod = {RequestMethod.GET})
public Model find(@PathVariable("id") Integer id){}

--------------------------------

springmvc解决post请求乱码:
web.xml中配置filter
<filter>
  <filter-name>encoding</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodinngFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>encoding</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

解决GET请求乱码:
需要修改Tomcat下的server.xml的配置。

--------------------------------

jsp中发送put等请求:
直接配置method标签没有用,需要配置隐藏标签,并在web.xml中配置filter标签,拦截到隐藏标签后,发送指定模式的请求。

--------------------------------

Ajax json交互
1.前端到后台:前端发json,后台直接接收为pojo参数,用注解@RequestBody
2.前端到后台:后台返回pojo参数,前端接收为json,用注解@ResponseBody

json数据交互所需jar,3个:
jackson-core
jackson-databind
jackson-annotations


================================

【js中使用$.ajax发送请求时,type为'POST',才会将data中写的参数封装到请求体中;如果type为'GET',则会将data中写的参数封装到url中。】
【js中使用$.ajax发送json请求时,需要写明contentType:'application/json;charset=utf-8',否则服务器可能会报错】

================================

后台添加@ResponseBody后,返回信息则不再走视图解析器的流程(返回ModelAndView等、返回目标页面的那个),而是等同于response直接输出。

================================

@RequestMapping标识的方法(即handler)返回参数时,可以返回ModelAndView,String(网址或者内容)、

================================

过滤器(Filter):作用在servlet之前,过滤请求。
监听器(Listener):实现了javax.servlet.ServletContextListener接口的服务器端组件,它随Web应用的启动而启动,只初始化一次,然后会一直运行监视,随web应用的停止而销毁。
作用一:做一些初始化工作。
作用二:监听web中的特定事件,例如统计在线人数。(session的创建与销毁)
拦截器(Interceptor):【是springMVC、Struts等表现层框架自己的】,不会拦截jsp/html/css/image的访问等,只会拦截访问的控制器方法(Handler)。
可以拦截的地方:
handler执行之前、
在handler执行完毕但未跳转页面之前
在跳转页面之后拦截一次

配置位置:过滤器、监听器在web.xml中配置;而拦截器在springmvc.xml中配置。

拦截器常用位置:在handler执行前拦截,进行权限校验。

拦截器使用方法:
1.新建一个类,实现HandlerInterceptor接口,类中重写三个方法,分别是:
preHandle()//handler方法执行前
postHandle()//handler方法执行后跳转页面前
afterCompletion()//页面跳转后

2.这些方法传入了Object handler参数,可以用这个确定是哪个handler。

3.经常在preHandle()方法中进行权限校验工作,返回值boolean代表是否放行,true表示放行,false表示终止。

4.可以在postHandle()方法中对返回的数据和视图信息进行修改,这个方法接收了ModelAndView参数。

5.可以在afterCompletion()方法中进行异常捕获,这个方法接收了Exception参数,但是一般不用。

6.之后,需要在springmvc.xml中注册这个自定义拦截器:
<!-- 这样会拦截所有的handler -->
<mvc:interceptors>
  <bean class="com.test.xxx.interceptor.MyIntercepter01" />
</mvc:interceptors>

<!-- 也可以配置拦截url的路径,使用exclude配置不拦截的url的路径 -->
<mvc:interceptors>
<mvc:interceptor>
  <mvc:mapping path="/**" />
  <mvc:exclude-mapping path="/demo/**" />
  <bean class="com.test.xxx.interceptor.MyIntercepter01" />
</mvc:interceptor>
</mvc:interceptors>

如果配置了多个自定义拦截器,会按照springmvc.xml中拦截器的配置顺序进行拦截;preHandle()方法是顺序执行,postHandle()与afterCompletion()方法是逆序执行。

================================

SpringMvc如何处理multipart形式的数据,也就是如何处理文件上传:
对原生srevlet处理文件上传的方法进行了封装

1.引入jar包commons-fileupload
2.客户端:
form表单
(1)method=post
(2)enctype=multipart
(3)file组件
3.服务端:
原始方法servlet:解析文件上传流
springmvc:
(1)重命名(给一个唯一的名字)
(2)存储到磁盘(考虑同一个目录文件过多,可以按照日期创建新的文件夹)
(3)把文件路径更新到数据库
4.springmvc中配置文件上传解析器

--------------------------------

文件上传具体代码:
1.客户端:
<fieldset>
<form method="post" enctype="multipart/form-data" action="/upload">
  <input type="file" name="uploadFile" />
  <input type="submit" value="上传" />
</form>
</fieldset>

2.后台:
@RequestMapping(value = "/upload")
public ModelAndView upload(MultipartFile uploadFile, HttpSession session) throws IOException{
  //处理上传文件
  //重命名,获取后缀
  String oriName = uploadFile.getOriginalFilename();
  String ext = oriName.substring(oriName.lastIndexOf(".")+1, oriName.length());
  String newName = UUID.randomUUID().toString() + "." + ext;
  //存储到指定文件夹
  //如果存储到WEB-INF中,就可以通过url直接访问该文件
  //考虑文件过多的情况,可以按照日期生成子文件夹
  //根据项目相对路径获取磁盘绝对路径
  String realPath = session.getServletContext().getRealPath("/uploads");
  String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
  File folder = new File(realPath + "/" + datePath);
  if(!folder.exits()){
    folder.mkdirs();
  }
  //存储文件目录
  uploadFile.transferTo(new File(folder, newName));
  
  //TODO 文件磁盘路径更新到数据库字段
  
  //跳转到成功页面
  ModelAndView modelAndView = new ModelAndView();
  modelAndView.addObject("msg","成功!");
  modelAndView.setViewName("success");
  return modelAndView;

}

3.在springmvc.xml配置文件中配置:
<!-- 多元素解析器,id固定为multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
  <!-- 设置上传文件大小上限,单位是字节,-1代表没有限制,默认为-1 -->
  <property name="maxUploadSize" value="5000000" />
</bean>

================================

SpringMVC异常处理机制:
SpringMVC异常处理器,可以处理handler抛出的异常:
1.在Controller类中创建一个方法,加注解:
//可以指明一个异常类
@ExceptionHandler(ArithmeticException.class)
//下面声明的形参的异常范围性需要大于等于注解中的异常
public void handleException(ArithmeticException exception,HttpServletResponse response){
  //异常处理逻辑
  try{
    response.getWriter().write(exception.getMessage());
  }catch(IOException e){
  }
  
}

2.写在一个Controller中,只会对当前Controller生效

3.可以新建一个类,使用注解【@ControllerAdvice】,就可以捕获所有Controller类中的handle抛出的异常了。
可以在新类中新建一个方法,使用【@ExceptionHandler】注解,捕获一类异常;
然后return ModelAndView,让某类异常统一返回到一个错误页面。

================================

springMVC重定向时参数传递的问题:
转发与重定向的区别:

转发:A->B->C
url不会变,参数也不会丢失,一个请求

重定向:A->B->A->C
url会变,参数会丢失,需要重新携带参数,两个请求

解决代码如下:
@RequestMapping("/handle1")
public String handle1(String name,RedirectAttributes redirectAttributes) {
  //这个方法设置了一个flash类型的属性,该属性会被暂存到session中,在跳转到页面之后,该属性会销毁
  redirectAttributes.addFlashAttribute("name",name);
  return "redirect:handle2";

}

@RequestMapping("/handle2")
public ModelAndView handle2(@ModelAttribute("name")String name) {
  //从model中获取name
  System.out.println(name);
  ...
}  


 

posted @ 2020-10-16 16:43  codeToSuccess  阅读(120)  评论(0编辑  收藏  举报