SpringMVC详解
文章目录
一种设计模式,Spring MVC
MVC设计不仅限于 Java Web应用 ,还包括许多应用,比如前端,PHP,.NET 等语言。之所以这么做根本原因在于解耦个模块。
MVC:是Model、View和Controller的缩写,分别代表web应用程序的三种职责。
模型:用于存储数据以及处理用户请求的业务逻辑(实体层)
视图:向控制器提交数据,显示模型中的数据
控制器:根据视图提出请求判断将请求和数据交给那个模型处理,将处理的相关结果交给那个视图显示
Spring MVC 结构最清晰的MVC实现
1、清晰的角色划分
2、灵活的配置功能
3、提供了大量的控制器接口 和 实现类
4、真正的做到与View层的实现无关(JSP)
5、国际化的支持
6、面向接口编程
Srping 提供了Web应用开发的一整套流程,不仅仅是MVC,他们之间可以很方便的整合
请求与相应的过程:用户访问controller,controller查询模型,携带数据访问视图返回给用户
创建Spring MVC,模式方式一
1、添加依赖
2、创建类 继承extends AbstractController 实现方法:handleRequestInternal
3、配置springMVC核心配置文件 application-servlet.xml
4、配置web.xml 》》》 配置Springmvc 核心控制器 ( 看下图 )加载 application-servlet.xml配置
增强工具提示插件
导入依赖
tomcat
- 在java报下创建一个控制器包,里面创建一个UserController.java,继承了 AbstractController,这是一个抽象类,继承了Controller,因此UserController就变成了一个控制器;
- 实现方法:handleRequestInternal;
- 添加控制器modelandview将信息携带到网页名为main的页面下,此请求会返回到核心控制器中,核心控制器再根据spring的配置文件对路径进行拼接,进而访问/WEB-INF/jsp/main.jsp
public class UserController extends AbstractController {
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
System.out.println("测试:这里是UserContrller");
ModelAndView modelAndView=new ModelAndView();
//业务层处理好后,将数据携带着去核心控制器 与“main”结合配置文件拼接网页路径,形成页面的详细路径再跳转到页面中
modelAndView.setViewName("main");
return modelAndView;//返回给核心控制器,核心控制器设置了页面路由。modelAndView需要知道访问什么页面
}
}
- 为了让controller跳转页面。要设置一个spring配置文件:application-servlet.xml
将java下的controller转换为bean 便于spring管理该控制器,只要用户访问index.html,就进入UserController控制器中
视图网页路径配置:对转向页面的路径解析 例: /WEB-INF/jsp/main.jsp
name属性表示可以被请求的名称,只要用户在地址栏访问index.html,就进入UserController控制器中
- web.xml中设置核心控制器来加载spring的配置文件application.xml(配置文件的定义都进入到核心控制器类属性中),url-pattern的配置表示任何请求都经过核心控制器;load-startup表示加载顺序,tomcat启动了是否要立即加载的意思
springMVC的模式总结: 所有的请求都是经过核心控制器,核心控制器再进行分配 - 当tomcat启动时,加载web/xml,web.xml中的核心控制器加载application-servlet.xml,那么核心控制器通过application-servlet.xml配置文件寻找我们在项目下定义的控制器Usercontroller上,等待用户的浏览器请求…
- 当用户访问浏览器时,就会访问地址栏根据配置文件对应的我们定义的控制器例如UserContrller,执行UserContrller的内容,UserContrller接着调用模型处理业务模块,UserContrller的返回结果由modelandview来处理,modelandview携带数据和路径找(先找核心控制器)配置文件application-servlet.xml的试图路径对应,配置文件application-servlet.xml中的viewResolver处理视图映射,model再在核心控制器中将模型数据传给当前视图(jsp)进行展示,这些信息再返回到浏览器页面中
1、客户端的请求提交到DispatcherServlet 由DispatcherServlet控制器寻找一个或者多个HandlerMapping,HandlerMapping找到处理请求的Controller
2、DispatcherServlet 将请求提交到Controller
3、Controller调用业务逻辑成处理后返回ModelAndView(携带页面名称和返回值)。
4、DispatcherServlet 寻找一个或者多个ViewResolver视图解析器,找到ModelAndView指定的视图
5、视图负责将结果显示到客户端
上图包含了4个Spring MVC的接口DispatcherServlet 、 HandlerMapping 、 Controller 、 ViewResolver
Spring MVC所有的请求都将经过DispatcherServlet 来统一分发,在DispatcherServlet 将请求分给Controller
之前需要借助Spring MVC提供的HandlerMapping 定位到具体的Controller
HandlerMapping 接口负责完成客户端请求到Controller映射, 核心控制器先找HandlerMapping类,HandlerMapping实现RequestHandlerMapping。它会处理RequestMapping这个注解,启动tomcat的时候,扫描包扫到这个controller,将controller的方法注册到映射表中,等待用户的请求
Controller 接口将处理用户的请求,这个和 Java Servlet角色一致, 一旦Controller处理完用户的请求,将返回ModelAndView对象给DispatcherServlet 前端控制器,ModelAndView中包含了模型(Model)和视图(View)
从宏观的角度考虑,DispatcherServlet 是整个web应用的控制器;
从微观考虑:Controller是单个HTTP请求处理过程中的控制器,而ModelAndView是HTTP请求过程中返回的模型与视图。
ViewResolver 接口(视图解析器)在web应用中负责查找view对象,从而将结果渲染给客户
此模式缺点
每有一个contrller,都要在application.xml中配置一个bean
创建Spring MVC,模式方式二
优化:加入扫描,为controller添加注解@RequestMapping,与模式一不同的是:我们不需要在application.xml中创建一个controller的bean,并添加网页信息。
思想:spring会遍历这些Bean,依次判断是否是处理器,并检测其HandlerMethod ,核心控制器先找HandlerMapping类,HandlerMapping实现RequestHandlerMapping。它会处理RequestMapping这个注解,启动tomcat的时候,扫描包扫到这个controller,将controller的方法注册到映射表中,等待用户的请求
遍历Handler中的所有方法,找出其中被@RequestMapping注解标记的方法。
获取方法method上的@RequestMapping实例。
检查方法所属的类有没有@RequestMapping注解
将类层次的RequestMapping和方法级别的RequestMapping结合 (createRequestMappingInfo)
当请求到达时,去urlMap中需找匹配的url,以及获取对应mapping实例,然后去handlerMethods中获取匹配HandlerMethod实例。
将RequestMappingInfo实例以及处理器方法注册到缓存中。
- application-servlet.xml头部导包
- application.xml中添加扫描注解与注解驱动,有了注解驱动,我们标注的注解才会起作用
<mvc:annotation-driven/>
Spring就知道了我们启用了注解驱动,然后Spring通过 <context:component-scan base-package="com.xk"/>扫描,会自动的为我们找到@Compnent @Controller @Service @Repository等注解标记的组件到工厂中,来为我们处理请求
-
首先UserContrller头部加入标注controller,
@RequestMapping
为UserContrller起个名字为user,就表示了UserContrller是一个控制器。这一步替代了继承AbstractController -
自定义方法加入RequestMapping注解,并添加get方法访问index.html的信息,当用户get方法地址栏输入index.html就会访问到这个方法上。相当于一个application.xml中创建一个controller的bean,bean内设置url和contrller。更加简便。
-
new ModelandView("main")
是使用构造函数,这个构造函数的参数也是对返回的页面名称viewname
赋值。
- 测试,请求的是contrller中的方法,因此地址栏要输入user/index.html,user表示contrller
跳转页面的方式一:modelandview
跳转页面的方式二:String字符串跳转
将返回值类型设置为String类型,返回值为页面名称 :return "页面名称 "
入参的方式一: 如果要携带值去页面,使用model来进行携带
入参的方式二: 如果要携带值去页面,使用map集合来进行携带
-
页面接收参数:页面上EL表达式无法使用时加入一个定义即可
-
测试
页面的值如何传递到后台
- 写一个跳转到login.jsp页面的方法,当用户输入login.html,就跳转到login.jsp页面中
- 复制main.jsp的头部,创建一个login.jsp页面,当用户提交信息时访问userLogin这个方法
少量参数值的传递
- 用户输入完信息后点击提交访问userLogin方法,接收用户输入的信息参数,需要添加注解,此方法显示登录成功后跳转到main.jsp的方法
多参数的传递:通过对象接收页面参数
当用户填完登录信息并且信息过多时,不断对每一个参数增加注解就显得很麻烦,可以选择在pojo层创建一个User类,User类的属性要与jsp中用户提交信息的name属性名称一致
-
写一个将用户提交的信息访问userLogin时存入User对象中,因此此处jsp的name属性与User类的属性名称要一致才可以存入,再获取用户提交的信息的方法
-
resources下配置log4j的配置文件
-
测试
springMVC的局部异常处理
当程序发生问题时,通过局部异常处理来监控并进行页面的跳转
- 控制器中写一个发生局部异常跳转error页面的方法,注解中设置为运行异常,表示处理运行异常,程序只要触发这个异常就执行handlerException()方法;用户登录访问的方法中模拟一个异常来进行测试:此处写的“错误”这个msg,会在handlerException()方法中的getMessage()来获取并展示到页面上。
- 创建一个error.jsp页面,当程序引发错误,就需要提示的一个页面
- 测试:
弊端:局部异常处理只能在当前的java文件中使用,这个java文件之外的其余文件无法监控到异常。为了让所有的控制器在发生异常时都可以被监控到,因此需要在application.xml中配置全局异常处理。
springMVC的全局异常处理
专门有一个类来负责全局异常处理,需要在application.xml中配置:捕获当前异常名称默认的名字e(这个e也要显示到jsp页面上,因此名称与jsp页面保持一致)、异常发生时跳转的默认页面、 为了将不同的异常分配到不同的错误页面中还需要配置指定特殊的异常要跳转的页面
注释掉控制器的局部异常处理后在用户登录提交进入控制器对应的方法处模拟一个错误进行测试
Spring MVC拦截器
用户请求时会进入第1个、第2个、。。拦截器,都不拦截,再进入servlet,servlet再进入到第n个。。。第二个、第一个拦截器,再访问到页面
-
创建一个拦截器的包,继承一个拦截器,并重写方法
-
通过logger输出来查看结果,配置三个拦截器(重写3个方法):在业务处理器 处理请求之前被调用、在业务处理器 执行完成后 生成视图之前执行、 在DispatcherServlet完全处理完请求后被调用,用于清理资源
/**
* 继承 HandlerInterceptorAdapter
* 实现拦截器
*/
public class UserInterceptor extends HandlerInterceptorAdapter {
private Logger logger = Logger.getLogger(UserInterceptor.class.getName());
/**
* 在业务处理器 处理请求之前被调用
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*
* 如果返回结果为 false 从当前拦截器往会执行
* 如果返回结果为true 执行下一个拦截器
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("1、preHandle");
logger.info("请求的方式:"+request.getMethod());
System.out.println(request.getRequestURI());
System.out.println(request.getContextPath());
return true;
}
/**
* 在业务处理器 执行完成后 生成视图之前执行
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("2、postHandle");
}
/**
* 在DispatcherServlet完全处理完请求后被调用,用于清理资源
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("3、afterCompletion");
}
- SpringMVC配置文件application.xml中配置拦截器,可以配置不拦截的资源
<!--拦截器的配置:命名空间mvc:interceptors-->
<mvc:interceptors>
<mvc:interceptor>
<!-- 需要拦截的地址: /** 所有文件夹 以及子文件夹
/* 所有文件夹 不包含子文件夹
/是WEB项目的根目录
-->
<mvc:mapping path="/**"/>
<!--需要排除的拦截地址:例如自己定义的拦截器文件、登录页面 /user/login.html表示当用户输入此url不拦截-->
<mvc:exclude-mapping path="/user/login.html"/>
<!--这个类是我们定义的Interceptor-->
<bean class="com.xk.interceptor.UserInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
<!-- 不拦截 静态资源css、js-->
<!--<mvc:resources mapping="" location=""></mvc:resources>-->
- 拦截器配置方式二:在WEB.xml 中也可以配置不需要拦截的资源(注意web.xml头部)
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!-- 配置 不需要拦截的资源-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/static/js/*</url-pattern>
<url-pattern>/static/css/*</url-pattern>
<url-pattern>/static/images/*</url-pattern>
<url-pattern>/static/fonts/*</url-pattern>
</servlet-mapping>
- 测试
查看后台:
报错总结
-
[2020-12-17 11:17:08,304] Artifact SpringMVC:war exploded: Error during artifact deployment. See server log for details.
web.xml配置文件写错,注意标签的对应 -
Request method POST not supported
jsp页面以post方式提交的信息访问到控制器方法中,方法的对应也应该是post,这里错误的写为get导致的 -
org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.ScQ.pojo.User]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.ScQ.pojo.User.()
原因:实体类没有无参构造方法
文件上传
思想:用户在地址栏输入访问上传文件的网页,跳转到jsp页面进行文件上传,用户点击提交按钮时访问contrller的上传文件方法,方法将文件上传成功后跳转到上传成功的页面。注意此时地址栏为访问contrller的上传文件方法的地址。
配置三步骤:配置文件设置、jsp页面、controller后台。
- 配置文件设置
- web-inf下创建文件夹jsp
- 包下创建一个类用于文件上传,扫描要扫描到此处
文件上传方式一:io流一边读一边写
-
要求; 文件上传必须以post方式接收
-
方法参数:CommonsMultipartFile file
-
获取用户上传的文件的基本信息:文件名称、文件类型、文件大小、文件类型名称
-
使用io包工具类获取旧文件文件后缀名、设置新的文件名称,用于文件重命名防止文件名重复,
-
定义文件目录用于保存新上传的文件:例如复制项目的路径,此路径不一定存在,需要检查一下,如果不存在要创建
-
使用输入输出流开始文件上传,使用输出流保存上传的文件
-
通过输入流获取用户上传的文件file 最后关闭输入输出流
-
写一个上传文件的页面fileUpload.jsp和成功页面success.jsp
-
fileUpload.jsp中:提交路径(到contrller(user)的哪个方法上)
${pageContext.request.contextPath}/user/fileUpload2.html
、提交方法POST、提交的为数据表单 ,注意name属性名与提交到contrller对应方法上参数名一致才能对应,enctype="multipart/form-data"
表示:设置 这个表单 是数据表单用于文件上传,因为普通表单无法上传 -
fileUploadContrller写一个跳转方法,当用户访问这个地址,先跳转到 fileUpload.jsp中,开始上传文件,当用户点击提交后跳转到 fileUploadContrller里面上传文件的方法中,方法将文件上传到指定路径成功后跳转到成功页面。
-
- fileUploadContrller里面上传成功就跳转到成功页面success.jsp
- fileUploadContrller里面上传成功就跳转到成功页面success.jsp
-
web.xml中加载配置文件
文件上传方式二:
代码如下:
@Controller
@RequestMapping("fileUpload")
public class FileUploadController {
@RequestMapping(value = "fileUpload.html",method = RequestMethod.GET)
public String goFileUpload(){
//当用户访问fileUpload.html,先跳转到 fileUpload.jsp中
return "fileUpload";
}
@RequestMapping(value ="fileUpload1",method = RequestMethod.POST)
public String FileUpload(@RequestParam(value = "file", required = false)MultipartFile file){
//当用户f点击提交时跳转到此方法下上传文件,文件上传成功后天转到success.jsp中
//获取文件类型
System.out.println("-------"+file.getOriginalFilename());
System.out.println("-------"+file.getContentType());
System.out.println("-------"+file.getSize());
System.out.println("-------"+file.getName());
/* 用过UUID 生成文件名称*/
String fileName = UUID.randomUUID().toString().replace("-","");
/*获取文件后缀名*/
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
String filePpath = "D:\\Java\\ScQ_news\\SpringMVC\\img";
File file1 = new File(filePpath);
if(!file1.exists()){
file1.mkdirs();
}
//文件上传方式一:
/* try {
OutputStream os = new FileOutputStream(filePpath+File.separator+"."+fileName+extension);
InputStream is = file.getInputStream();
int temp;
while ((temp = is.read() )!= (-1)){
os.write(temp);
}
os.flush();
os.close();
is.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}*/
//文件上传方式二:
try {
file.transferTo(file1);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
}
测试:
本文地址:https://blog.csdn.net/qq_44241861/article/details/111088395