SpringMVC
1.1、定义
springMVC是基于spring的一个框架,它就是spring的一个模板,专门用来做web开发的。可以理解为是Servlet的一个升级。
web开发的底层是servlet。
1.2、DispatcherServlet
作用:接收用户所有的请求,然后转发给@controller对象(@controller注解创建的对象)去处理。
1.3、实现步骤
-
创建spring-web的maven项目
-
导入依赖
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.14</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> <scope>provided</scope> </dependency>
-
在web.xml文件中注册DispatcherServlet对象
-
DispatcherServlet称为中央调度器,也叫前端控制器,是一个servlet。
-
DispatcherServlet负责接收用户请求,然后调用其他控制器对象,并把处理结果显示给用户。
<!-- 声明springmvc核心对象DispatcherServlet 需要在tomcat启动后创建DispatcherServlet对象。 为什么:因为在创建DispatcherServlet对象时会同时创建springmvc容器对象 然后创建配置文件中所有的bean。 DispatcherServlet在执行init方法时{ WebApplicationContext context =ClassPathXmlApplicationContext("applicationContext.xml"); getServletContext.setAttribute(key, context); } --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 在tomcat启动后,创建Servlet对象 其中的数值(>=0)表示的是,创建对象的顺序,数值越小,创建的时间越早。 --> <!--自定义spring配置文件的位置--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
-
-
创建发起请求的页面 index.jsp
-
创建控制器类
-
在类上加@Controller注解,创建对象,并交给springmvc容器中
-
在类的方法上加@RequestMapping注解。
/** * 在spring容器中创建对象 * MyController也叫后端控制器 */ @Controller public class MyController { /** * 匹配请求的uri地址 * value:需要配置的uri地址,前面通常加/ * @return */ @RequestMapping(value = "/some.do") //相当于Servlet中doGet方法 //ModelAndView中保护了数据和视图路径 public ModelAndView doSome(){ ModelAndView modelAndView= new ModelAndView(); //添加数据,最后框架把数据放到request作用域中 //框架中调用了request.setAttribute(key,value); modelAndView.addObject("msg","欢迎使用springmvc开发"); //指定视图的路径,相当于request.getRequestDispatcher("/show,jsp").forward(request,response) modelAndView.setViewName("/show.jsp"); return modelAndView; } }
-
-
创建一个用于显示结果的jsp
-
创建SpringMVC配置文件(spring配置文件一样
-
声明组件扫描器
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <context:component-scan base-package="com.jfs"/> </beans>
-
声明视图解析器
<!-- 声明视图解析器,设置视图文件的位置 --> <bean id="view" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀:表示的是视图的文件地址位置--> <property name="prefix" value="/WEB-INF/view"/> <!--后缀:表示文件的类型--> <property name="suffix" value=".jsp"/> </bean>
-
1.4、springmvc执行过程源代码分析
-
tomcat启动创建容器过程,通过load-on-start标签,来创建DispatcherServlet对象,DispatcherServlet是一个servlet。
在创建DispatcherServlet对象时,会执行init()方法。
在init方法中有
WebApplicationContext context =ClassPathXmlApplicationContext(“applicationContext.xml”);
getServletContext.setAttribute(key, context);
创建容器对象,同时创建@controller注解的对象。放到容器中。
-
请求处理过程
-
执行servlet中service方法
protected void service(HttpServletRequest request, HttpServletResponse response) ;
protected void doService(HttpServletRequest request, HttpServletResponse response);
protected void doDispatch(HttpServletRequest request, HttpServletResponse response){
在该方法中调用controller中的方法
}
-
1.5、RequestMapping注解的使用
-
@RequestMapping放到类上面,表示公共的部分。
-
指定请求的方式,使用属性method =RequestMethod.get。如果不指定方式,表示没有限制。
1.6、处理方法的参数
- HttpServletRequest
- HttpServletResponse
- HttpSession
- 请求中所携带的请求参数
1.6.1、接收用户提交数据的方式
1.6.1.1、逐个接收用户数据
**要求:**1. 方法的参数名和请求的参数名必须一致,和参数位置无关。
2.方法的参数名和请求的参数名不一样,加注解@RequestParam(“请求参数名”),在处理器形参定义前面。
@RequestParam参数:
- value:用来匹配请求的参数名
- required:表示该参数是否是必要的,默认是true。
处理流程:
-
使用request对象获取参数
String name =request.getparameter("name"); String age =reqest.getParameter("age");
-
springmvc通过调用DispatcherServlet调用相应的方法,调用方法时按照名称,给相应的参数赋值。
框架会把String类型,转换成 int、double、long等类型。
1.6.1.2、使用对象来接收请求参数
**java对象:**处理方法的形参时java对象,请求的参数名和java对象的属性名一样,框架会创建对象,会使用set方法属性赋值
1.7、请求后出现乱码问题
**问题:**在使用get方式请求时,携带的中文参数,不会出现乱码的问题。post方式会出现乱码问题
**解决办法:**使用过滤器解决乱码问题
1.8、处理方法的返回值
1.8.1、返回ModleAndView
**使用场景:**既需要转到其他视图,也需要携带数据的时候,使用较好。
1.8.2、返回String
表示视图,可以是逻辑名称,也可以是完整的视图路径
- 如果是逻辑名称需要配置视图解析器
<bean id="view" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀:表示的是视图的文件地址位置-->
<property name="prefix" value="/WEB-INF/view/"/>
<!--后缀:表示文件的类型-->
<property name="suffix" value=".jsp"/>
</bean>
- 如果返回的是完整的视图路径,则不需要加视图解析器。
1.8.3、返回void
不能表示数据,也不能表示视图。在处理ajax时,可以使用void。通过HttpSevletResponse输出数据,来相应ajax请求。
1.8.4、返回Object
返回对象,需要用到@ResponseBody注解,将转换的json数据放入到响应体中。例如:String、Integer、Map、List、Student等。返回的是数据,和视图无关。相应ajax请求。
实现步骤:
-
加入json的工具库依赖,springmvc默认使用的Jackson。
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.13.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.1</version> </dependency>
-
在springmvc配置文件中加入mvc:annotation-driven/驱动
json =om.writeValueAsString(Student)
驱动的功能是:完成java对象到json、xml、text、二进制等数据格式的转换。
实现HttpMessageConverter接口:消息转换器
在springmvc文件中加入mvc:annotation-driven/后,会自动创建HttpMessageConverter的7个实现类对象,
其中包含MappingJackson2JsonView(使用jackson工具库中的ObjectMapper实现从java对象转换为json格式的字符串)
-
在方法上面加@ResponseBody。
response.setContextType(“application/jason;chartset=utf-8”);
PrintWriter pw =response.getWriter();
pw.print(json);
放在处理器上面,通过HttpServletResponse返回给ajax数据的
1.8.4.1、返回自定义类型
- 框架根据返回值Student类型,依次调用框架中的HTTPMessageConverter接口的实现类对象的canWrite()方法,确定使用哪个类。
- 框架调用实现类的write()方法,把Student类型转换成相应的类型
- 根据@ResponseBody注解,把步骤2中转换的数据输出到浏览器中,相应ajax请求。
1.8.4.2、返回list集合类型
方法上加上@ResponseBody注解,返回给ajax请求的是一个json数据的数组。例如:[{name:“zhangsna”,age:18},{name:“lisi”,age:15}]
1.8.4.3 返回的是一个字符串对象(用于表示数据的)
如果加了@Responsebody注解,就代表返回的是数据,否则代表返回的视图。
实现步骤:
- 框架根据返回值String类型,依次调用框架中的HTTPMessageConverter接口的实现类对象的canWrite()方法,确定使用哪个类。
- 框架调用实现类的write()方法,把String类型按照"text/plain;charset=utf-8"编码方式处理。
- 根据@ResponseBody注解,把步骤2中转换的数据输出到浏览器中,相应ajax请求。
1.9、url-pattern为/时
tomcat配置文件:
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
DefaultServlet作用:
1、处理静态资源,2、处理未映射的其他servlet请求。
当中央调度器url-pattern也为/时。
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 在tomcat启动后,创建Servlet对象
其中的数值(>=0)表示的是,创建对象的顺序,数值越小,创建的时间越早。
-->
<!--自定义spring配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
当中央调度器url-pattern也为/时,它会替代tomcat中的default,导致所有的静态资源都由中央调度器处理。默认情况下中央调度器是没有处理静态资源的控制器,所有像,.jp、.html、.css都会报404错误。
动态资源是可以访问的,因为程序中有控制器对象。
处理静态资源访问不了问题:
-
在springmvc配置文件中加入mvc:default-servlet-handler/,框架会自动创建DefaultServletHttpRequestHandler对象,类似于我们自己创建的Controller对象。
-
处理方式二
<!-- 处理方式二 --> <!-- /img/**:表示处理img/123.jsp、img/test/23.jpg等访问地址 /img/:表示根目录下img文件夹下面所有的文件 --> <mvc:resources mapping="/static/**" location="/static/"/>
1.10、相对地址和绝对地址
1.10.1、绝对地址
绝对地址:带有协议名称的是绝对地址。例如:https://www.baidu.com。
1.10.2、相对地址
相对地址:没有协议开头,例如user/some.do、/user/some.do.
相对地址不能独立使用,必须要一个参考地址。通过参考地址+相对地址才能指定到资源。
加"/":
<a href="/test/some.do">访问dosome链接</a>
<%--点击该地址后,实际访问的地址是http://localhost:8080/test/some.do
加上"/"的作用是访问的是服务器的地址+点击的地址
--%>
不加"/"
<a href="test/some.do">访问dosome链接</a>
<%--点击该地址后,实际访问的地址是http://localhost:8080/springmvc01/test/some.do 不加上"/"的作用是访问的是资源访问路径地址+点击的地址--%>
解决访问地址问题:
<img src="${pageContext.request.contextPath}/img/123.jpeg" alt="静态资源(图片)">
1、在地址前面加上${pageContext.request.contextPath}
2、使用base标签。不过地址前面没有加"/",默认的加上的是这个地址
<head>
<base href="http://localhost:8080/springmvc01/"/>
</head>
<a href="test/some.do">访问dosome链接</a>
<img src="img/123.jpeg" alt="静态资源(图片)">
1.11、Springmvc核心技术
1.11.1、请求重定向和转发
forward:请求转发
redirect:请求重定向
**注意:**请求转发可以访问WEB-INF目录里面的资源,但是请求重定向无法访问WEB-INF里面的资源。
/**
*语法:setViewName("forward:完整的视图名路径");
* 请求转发不和视图解析器相关,需要完整的路径名
* 请求重定向可以访问WEB-INF里面的文件
*/
@RequestMapping("/forward")
public ModelAndView forward(String name,Integer age){
ModelAndView modelAndView = new ModelAndView();
//在request的作用域下,相当于request.setAttribute();
modelAndView.addObject("name",name);
modelAndView.addObject("age",age);
modelAndView.setViewName("forward:/WEB-INF/view/show.jsp");
return modelAndView;
}
/**
* 语法:setViewName("redirect:完整的视图名路径");
* 请求转发不和视图解析器相关,需要完整的路径名
* 框架对重定向的作用:
* 1、框架会把简单的数据类型转换成String类型,作为redirect.jsp请求的参数
* 目的在于两次请求之间传递参数
* 2、在redirect.jsp中可以使用${param.name}来获取请求的参数
* 3、请求重定向不能访问WEB-INF里面的文件
*/
@RequestMapping("redirect")
public ModelAndView redirect(String name, Integer age){
ModelAndView modelAndView = new ModelAndView();
//在request的作用域下,相当于request.setAttribute();
modelAndView.addObject("name",name);
modelAndView.addObject("age",age);
modelAndView.setViewName("redirect:/redirect.jsp");
return modelAndView;
}
1.11.2、异常处理
spring框架采用统一的、全局的异常处理。
把controller中的异常都集中到一个地方,采用的是aop的思想,把异常代码和业务代码分开,解耦合
常用到的注解:@ExceptionHandle @ControllerAdvice
处理步骤:
-
创建maven项目
-
导入依赖
-
新建一个自定义异常类StudentException,再定义它的子类NameException,AgeException。
-
在controller中抛出NameException,AgeException异常。
-
创建一个普通类,作为全局异常处理类
- 在类的上面加上@ControllerAdvice
- 在类的方法上加上@ExceptionHandler
//框架要扫描这个注解所在的包 @ControllerAdvice public class MyExceptionHandler { /** * 方法的形式和controller方法一样 * 当注解中有异常的类型,捕捉这个异常, * 参数:Exception,可以获取异常的信息。 */ @ExceptionHandler(NameException.class) public ModelAndView nameException(Exception e){ ModelAndView modelAndView =new ModelAndView(); modelAndView.addObject("exception",e); modelAndView.setViewName("exception"); return modelAndView; } /** * 当注解后面没有加任何的异常类型,则表示除了上面的异常外,其他异常都走这边 */ @ExceptionHandler public ModelAndView otherException(Exception e){ ModelAndView modelAndView =new ModelAndView(); modelAndView.addObject("exception",e); modelAndView.setViewName("exception"); return modelAndView; }
-
创建处理异常的视图页面
-
创建springmvc配置文件
- 组件扫描器,扫描@Controller注解
- 组件扫描器,扫描@ControllerAdvice注解
- 声明注解驱动
<!--扫描异常处理类所在的包--> <context:component-scan base-package="com.jfs.exceptionHandler"/> <!--声明注解驱动--> <mvc:annotation-driven/>
1.11.3、拦截器
1、拦截器是springmvc的一种,需要实现HandlerInterceptor接口。
2、拦截器和过滤器相似,功能的侧重点不同。过滤器是用来过滤请求参数和设置字符集编码的,拦截器是用来拦截用户请求的
3、拦截器是全局的,可以对多个controller拦截,一个项目中可以有0-n个拦截器,它们一起拦截用户的请求。拦截器常用在用户登录、权限检查、记录日志等方面。
1.11.3.1、使用步骤
1、定义类实现HandlerInterceptor接口。
public class MyInterceptor implements HandlerInterceptor {
/**
* 预处理方法
* 特点:
* 1、在控制器对象方法之前执行。
* 2、在这个方法中可以获取请求信息,验证请求是否符合要求。
* 可以验证用户是否登录,验证用户是否有权限访问该uri地址。
* 如果验证失败,这拦截。验证成功可以访问控制器中的方法。
* @param request
* @param response
* @param handler :被拦截的控制器对象
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("111拦截器preHandle方法");
return true;
}
/**
* 后处理方法。特点:
* 1、在controller方法之后执行的
* 2、能够获取modelAndView,故可以修改视图和数据
* 3、主要对执行结果进行二次处理
* @param request
* @param response
* @param handler :控制器对象
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("111拦截器postHandle方法");
}
/**
* 最后执行的方法。特点:
* 1、在请求执行完成后执行的,在框架中规定当你的视图处理完成后吗,对视图执行forward方法,认为请求完成。
* 2、一般做资源回收工作。
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("111拦截器afterCompletion方法");
}
}
2、在springmvc配置文件中声明拦截器,并指定要拦截的uri地址。
<mvc:interceptors>
<!--声明第一个拦截器-->
<mvc:interceptor>
<!--要拦截的uri地址
**:表示任意字符,文件或者多级目录中的文件
-->
<mvc:mapping path="/student/**"/>
<!--声明拦截器对象-->
<bean class="com.jfs.interceptor.MyInterceptor"/>
</mvc:interceptor>
<!--声明第二个拦截器-->
<mvc:interceptor>
<!--要拦截的uri地址
**:表示任意字符,文件或者多级目录中的文件
-->
<mvc:mapping path="/student/**"/>
<!--声明拦截器对象-->
<bean class="com.jfs.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
1.11.3.2、拦截器执行时间
1、在请求方法之前拦截,也就是在controller类方法执行前拦截
2、在控制器方法执行后也会执行拦截器
3、在请求处理完成后也会执行拦截器
1.11.3.3、多个拦截器执行顺序
两拦截器都是true的情况:
111拦截器preHandle方法
222拦截器preHandle方法
controller中的方法
222拦截器postHandle方法
111拦截器postHandle方法
222拦截器afterCompletion方法
111拦截器afterCompletion方法
111拦截器是true,222拦截器是false:
111拦截器preHandle方法
222拦截器preHandle方法
111拦截器afterCompletion方法
111拦截器是false,无论222是true还是false:
111拦截器preHandle方法
1.11.3.4、拦截器和过滤器的区别
-
过滤器是servlet中的对象,拦截器是框架中的对象
-
过滤器是实现Filter接口,拦截器实现HandlerInterceptor接口
-
过滤器是用来设置request、response参数、属性的,侧重数据的过滤。
拦截器是用来验证登录等的,拦截请求的。
-
过滤器是在拦截器之前执行
-
过滤器是tomcat创建的对象,拦截器是springmvc容器创建的对象
-
过滤器有一个执行时间点,拦截器有三个执行时间点。
-
过滤器可以执行html,js,css等。拦截器侧重拦截controller对象,如果你的请求不能被DispatcherServlet接收,也不会执行拦截器。
1.12、Springmvc的执行流程
-
用户发起请求
-
中央调度器接收请求(DispatcherServlet),把请求转交给处理器映射器
处理器映射器:springmvc框架中的一个对象,框架把实现了HandlerMapping接口的类叫做映射器(多个)。
处理器映射器的作用:从springmvc容器中获取处理器对象。框架把找到的对象放到一个叫作处理器执行链类(HandlerExecutionChain)保存。
HandlerExecutionChain类中保存着:1、处理器对象 2、项目中所有的拦截器
中央调度器执行的方法:
//获取处理器映射器 mappedHandler = getHandler(processedRequest);
-
中央调度器把2中获得HandlerExecutionChain中的处理器对象交给处理器适配器(多个)
处理器适配器:springmvc框架中的对象,实现HandlerAdapter接口
处理器适配器的作用:执行处理器的方法,得到返回值ModelAndView。
中央调度器执行的方法:
//获取处理器适配器 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //执行处理器方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
-
中央调度器把在3中获取的ModelAndView交给视图解析器对象。
视图解析器:springmvc中的对象,需要实现ViewResolver接口(可以有多个)
视图解析器的作用:组成视图的完整路径,使用前缀后缀,并创建view对象。
view:是一个接口,表示视图的。在框架中jsp、html不是String表示,而是使用view和他的实现类表示视图。
InternalResourceView:视图类,表示jsp文件。视图会创建该类的对象。这个对象的里面有个/WEB-INF/view/show.jsp。
- 中央调度器获取到4步骤中view,调用View类中的方法。把Model数据放到request中。对视图对象执行forward。请求结束。