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);
...
}