SpringMVC
一 SpringMVC概述
- SpringMVC是基于Spring的框架,实际上就是Spring的一个模块,专门用于做javaweb开发的。可以将其理解为Servlet的一个升级。它的底层就是Servlet,是在servlet的基础上加入一些功能,方便进行web开发。
- SpringMVC实际上就是一个Spring容器。Spring能够使用ioc管理对象,能够使用@Component、@Repository、@Service、@controller、<bean>等管理对象。而SpringMVC容器中存放的是控制器对象,他将控制器对象存在其中能够将该对象作为控制器使用。
- 注意,这个控制器对象能够接收用户的请求,返回显示的结果,也就是说,他可以当做一个Servlet来使用,但是要注意的是他并不是Servlet,而是一个普通对象(因为他并没有继承HttpServlet类并且在web.xml中注册为Servlet),只不过SpringMVC赋予了该对象servlet的功能。
- SpringMVC中有一个对象是Servlet,这个对象是DispatcherServlet,即中央调度器。他负责接收用户的所有请求,用户把请求给了DispatcherServlet之后,SpringMVC会通过DispatcherServlet将请求转发给Controller对象,最后由Controller对象中的逻辑来处理和响应请求。
二 使用实例来介绍SpringMVC的处理方式
- 使用maven创建web项目
- 在pom文件中添加servlet和springMVC依赖,顺便添加编译插件(maven-compiler-plugin)
- 在web.xml中注册中央调度器,并配置springmvc配置文件的位置
load-up-startup有什么作用呢?可以这样理解- spring容器的容器对象applicationContext实现了BeanFactory,在该对象第一次创建时会同时创建容器内部保存的bean对象。
- dispatcherServlet继承于HttpServlet,在初次访问时会对其进行初始化并创建DispatcherServlet对象。在DispatcherServlet的init方法中,会创建springmvc的容器对象webApplicationContext,并将其放入servletContext工程域对象中
- 因此使用load-up-startup标签表示在工程启动时就创建DispatcherServlet对象,这样就能够同时创建springMVC容器对象并且将其中所有的bean对象一同创建
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1<load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml<param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
- 创建SpringMVC配置文件springmvc.xml
- 创建处理器类
在Controller包中创建处理器类,添加@Controller注解。在处理器方法上添加@RequestMapping注解,表示对value属性所指定的URI进行处理和响应。
如果有多个请求均需要使用该处理器类,那么@RequestMapping注解的value属性可以添加一个数组
ModelAndView类中的addObject()方法用于向Model中添加数据,Model底层为一个HashMap。Model中的数据会储存在request域中,springMVC默认采用转发的方式跳转到视图,本次请求结束后,Model中的数据会被销毁
@Controller
public class MyController{
@RequestMapping("/some.do")
public ModelAndView doSome(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message","你好吗");
modelAndView.setViewName("/show.jsp");
return modelAndView;
}
}
- 声明组件扫描器
在springmvc.xml中声明组件扫描器 :<context:component-scan base-package="com.jarvis.controller">
- 创建目标jsp页面
- 在springmvc.xml中注册视图解析器
主要是方便处理器方法回传页面。在modelAndView对象使用setViewName("show")
即可
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/">
<property name="suffix" value=".jsp">
</bean>
**使用SpringMVC框架进行web请求的顺序为:**前端页面发请求给Tomcat服务器,Tomcat服务器接收到请求后将请求处理后传递给中央调度器。中央调度器根据请求的地址调用对应的@RequestMapping处理器方法,并在中央调度器的doDispatcher()中处理请求,并根据ModelAndView中的数据和页面地址响应请求。
三 SpringMVC注解式开发
@RequestMapping定义请求规则
- 该注解的value用来定义所匹配请求的URI,一般会以斜杠“/”开始。当该注解定义在处理器方法上时,表示该方法所对应的请求URI。而当该注解定义在处理器类上时,表示该处理器类中所有方法共同的请求URI前置部分,起到一定的过滤作用
@Controller
@RequestMapping("/test")
public class MyController{
@RequestMapping("/some.do")
public ModelAndView doSome(HttpServletRequest req,HttpServletResponse resp) throws Exception{
modelAndView.addObject("message","你好吗");
modelAndView.setViewName("show");
return modelAndView;
}
}
- @RequestMapping注解还包含method属性,表示对应请求的请求方式。只有请求方式匹配才能对请求进行处理。如
@RequestMapping(value="/some.do" method=RequestMethod.POST)
,当不指定该method属性时,无论是POST还是GET请求方式,该处理器都可以对其进行处理响应。
处理器方法的参数处理
处理器方法可以包含四类参数,这些参数会在系统调用时由系统自动赋值,所以可以在创建处理器方法时直接使用。这四类参数分别为:HttpServletRequest、HttpServletResponse、HttpSession、请求中所携带的请求参数
- 进行逐个参数接收,只需要保证请求参数名和该处理方法的参数名相同即可
@Controller
@RequestMapping("/test")
public class MyController{
@RequestMapping(value="/register.do")
public ModelAndView register(String name , Integer age){
//doSomething
return modelAndView;
}
}
- 使用@RequestParam注解校正请求参数名
该注解中的value属性即为请求参数的参数名。
required参数表示请求中是否必须存在此参数,默认为true,即必须存在该参数
@Controller
@RequestMapping("/test")
public class MyController{
@RequestMapping(value="/register.do")
public ModelAndView register(@RequestParam("userName") String name ,
@RequestParam("userAge") Integer age){
//doSomething
return modelAndView;
}
}
- 对象参数接收
将处理器方法的参数定义为一个对象,只要保证请求参数与该对象中的参数同名即可
@Controller
@RequestMapping("/test")
public class MyController{
@RequestMapping(value="/register.do")
public ModelAndView register(Student student){
//doSomething
return modelAndView;
}
}
处理器方法的返回值
使用@Controller标注的处理器中的处理方法,其返回值有常用的四种类型,分别为:ModelAndView、String、无返回值void和自定义类型对象
-
返回ModelAndView
当处理器方法对请求的处理为跳转到其他资源并且在该资源之间传递数据时,可以使用ModelAndView作为返回值,因为它既可以传递数据也可以传递资源页面。
但是当请求只需要响应返回数据,而并不需要进行跳转(如AJAX);或者是仅仅执行跳转操作时(如重定向),这时候使用ModelAndView会总有一部分没有被利用到 -
返回String
处理器方法返回的String可以指定逻辑视图名,藉由视图解析器可以将其转化为物理视图地址
@Controller
@RequestMapping("/test")
public class MyController{
@RequestMapping(value="/register.do")
public String register(String name , Integer age){
//doSomething
return "show";//根据视图解析器会返回到/WEB-INF/jsp/show.jsp页面
}
}
- 返回void
当处理器接收到AJAX等不需要跳转仅仅接收数据之类的请求时,可以使用void作为返回值。只需要在处理器方法中使用Gson等jar包将所需要的对象转化为json字符串,通过响应流回传给客户端即可。
@Controller
@RequestMapping("/test")
public class MyController{
@RequestMapping(value="/register.do")
public void register(Student student,HttpServletResponse resp){
//doSomething
String student = Gson.toJson(student);
PrintWriter writer = resp.getWriter();
writer.print(student);
writer.flush();
writer.close();
}
}
- 返回自定义类的对象
处理器方法也可以返回自定义类的对象,这样返回的对象是作为直接在客户端页面显示的数据出现的。返回自定义类的对象时,需要使用@ResponseBody注解,将转换后的JSON数据放入到响应体中。- 由于数据转化需要将对象转化为JSON,在SpringMVC中是使用Jackson完成的,因此需要导入jackson相关依赖
- 同时,将对象数据转化为Json格式,需要由消息转换器HttpMessageConverter完成。而转换器的开启需要由/mvc:annotation-driven/完成。SpringMVC使用消息转换器实现请求数据和对象,处理器方法返回对象和响应输出之间的自动转换。当Spring容器进行初始化的过程中,在声明注解驱动完成后创建注解驱动时,默认创建了七个HttpMessageConverter对象,也就是说声明注解驱动是为了让容器创建HttpMessageConverter对象
@Controller
@RequestMapping("/test")
public class MyController{
@RequestMapping("/student.do")
@ResponseBody
public Student doStudentJson(){
Student student = new Student();
student.setName("张三");
student.setAge(15);
retuen student;
}
}
- 返回List
@Controller
@RequestMapping("/test")
public class MyController{
@RequestMapping(value="/array.do")
public List<Student> doArray(){
//doSomething
List<Student> list = new ArrayList<Student>();
list.add(new Student(1));
list.add(new Student(2));
return list;
}
}
- 返回字符串对象(并非是返回资源地址)
若要返回非中文字符串对象,在处理器方法前面加上@ResponseBody并且将返回值设置为String即可。但是如果返回的字符串中带有中文字符,则需要在@RequestMapping的produces属性中指定字符集。
@Controller
@RequestMapping("/test")
public class MyController{
@RequestMapping(value="/string.do" , produces="text/plain;charset=utf-8")
@ResponseBody
public String doString(){
return "SpringMVC";
}
}
关于 <url-pattern>
- DispatcherServlet的<url-pattern>配置详解
- 在没有特殊要求的情况下,SpringMVC的中央调度器常使用后缀匹配方式,如*.do、*.action等方式
- 当然也可以使用斜杠/,因为DispatcherServlet会将向静态资源的获取请求当做时一个普通的Controller请求。中央调度器会调用处理器映射器为其查找相应的处理器。但是当使用斜杠时,静态资源可能会无法正常显示。这是因为Tomcat内部的DefaultServlet能够处理静态资源的访问,还可以处理所有未被映射的请求, DefaultServlet的映射本身就是斜杠。当Servlet映射为斜杠时,就表示静态资源和未映射的请求都交给Servlet处理。因此如果将DispatcherServlet的映射设置为斜杠,就会代替DefaultServlet来处理这些请求。但默认情况下中央调度器没有相应的映射器处理这些请求
对静态资源的访问
虽然中央调度器没有声明对应的处理器,但这并不代表SpringMVC不可以访问静态资源,经过一些配置也是可以解决的
- 在springmvc.xml中声明标签 <mvc:default-servlet-handler>
声明后会在SpringMVC容器中创建一个DefaultServletHttpRequestHandler对象。会在请求进入中央调度器时进行过滤,将静态请求交给WEB应用服务器默认的Servlet处理。但是这种方法需要WEB应用服务器具有处理静态资源的Servlet。 - 使用<mvc:resources>
在Spring中定义了专门用于处理访问静态资源的请求的处理器对象ResourceHttpRequestHandler。只需要在springmvc.xml中添加标签即可——<mvc:resources mapping="" location="">
.
mapping用于表示访问静态资源的uri地址,可以使用通配符**
location用于表示静态资源在项目中的位置
如<mvc:resources mapping="images/**" location="/images/">
- 但是需要注意的是,无论是那种配置,都会与@RequestMapping注解产生冲突,也就是动态资源与静态资源之间会产生一些冲突。所以需要添加上注解驱动来解决这些冲突
<annotation-driven>
- 在实际开发时,通常会将静态资源统一存放在某个目录下进行配置
<mvc:resources mapping="static/**" location="/static/">
<annotation-driven></annotation-driven>
四 SSM整合开发
- 在pom文件中添加依赖。
- 配置web.xml文件
- 注册ContextLoaderListener监听器。注册创建该监听器,用于创建Spring容器及将创建好的Spring容器对象放入到ServletContext作用域中
- 注册字符集过滤器,用于解决请求参数中携带中文时产生的乱码问题。
- 配置中央调度器。Spring容器用于管理配置文件中的Bean,SpringMVC容器负责处理器对象。
<!-- 注册Spring监听器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
<listener>
<!-- 注册字符集过滤器 -->
<filter>
<filter-name>characterEncodingFilter</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>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置中央调度器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
- 创建库表、entity、dao、service、controller、前端页面等
- spring.xml
- 引入外部数据库链接配置文件
- 配置dataSource对象
- 配置事务管理器对象
- 注册sqlSessionFactory对象
- 注册动态代理对象MapperScannerConfigurer
- 开启组件扫描,用以扫描service包
- 开启事务管理
- 开启动态代理
<!-- spring容器的配置文件applicationContext.xml-->
<context:property-placeholder location="classpath:resource/jdbc.properties">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 配置事务管理器对象 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.sqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:resource/mybatis.xml"/>
</bean>
<!-- 注册动态代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" >
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.jarvis.dao" />
</bean>
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.jarvis.service">
<!-- 开启事务管理 -->
<tx:annotation-driven transactionManager="transactionManager"></tx:annotation-driven>
<!-- 开启动态代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- spring.xml
- 开启组件扫描,扫描controller包
- 开启注解驱动
- 解决静态资源和动态资源的冲突
- 使得框架可以将对象数据处理为json数据以响应ajax请求
- 配置静态资源访问标签
- 配置视图解析器
<!-- springmvc.xml配置文件 -->
<context:component-scan base-package="com.jarvis.controllers">
<mvc:annotation-driven />
<mvc:resource mapping="static/**" location="/static/">
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view">
<property name="suffix" value=".jsp">
</bean>
- mybatis.xml文件
- 给mapper映射文件中用到的bean对象配置别名
- 指定sql映射文件
<configuration>
<typeAliases>
<package name="com.jarvis.bean">
</typeAliases>
<mappers>
<package name="com.jarvis.dao">
</mappers>
</configuration>
- 创建dao接口及mapper映射文件
- 创建service层并添加@Service注解
- 创建处理器对象和处理器方法
- 创建前端页面
五 SpringMVC核心技术
请求重定向和请求转发
foward和redirect都是spring中的关键字,这两者在使用时都不与视图解析器一同工作,也即不受到视图解析器的限制。SpringMVC框架把JavaWeb中servlet里的请求转发和请求重定向进行了封装操作,现在可以使用简单的方式实现请求转发和请求重定向操作了
- forward请求转发
在servlet里需要使用request.getRequestDispatcher("xxx.jsp").forward(request,response);
来实现。
在处理器方法返回ModelAndView时,只需要在setViewName()指定的视图前添加forward即可。当处理器方法返回String时,只需要在视图路径前加入forward:
即可。
@Controller
@RequestMapping("/test")
public class MyController{
@RequestMapping("/1.do")
public ModelAndView doForward1(){
ModelAndView mv = new ModelAndView();
mv.setViewName("forward:/other.jsp");
return mv;
}
@RequestMapping("/2.do")
public String doForward2(){
return "forward:/other.jsp"
}
}
- redirect请求重定向
在servlet中需要使用response.sendRedirect("xx.jsp");
的方式来实现。
在处理器方法返回的视图字符串前添加redirect:
就可以实现重定向跳转。
需要注意的是,在servlet中重定向为两次请求,因此不能共享request域中的请求数据。但是SpringMVC框架会将Model中的简单类型数据转为String,作为重定向二次请求的参数。
@Controller
@RequestMapping("/test")
public class MyController{
@RequestMapping("/1.do")
public ModelAndView doRedirect1(){
ModelAndView mv = new ModelAndView();
mv.setViewName("redirect:/other.jsp");
return mv;
}
@RequestMapping("/2.do")
public String doRedirect2(){
return "redirect:/other.jsp"
}
}
异常处理
SpringMVC采用统一全局的异常处理,将Controller层中所有的异常处理集中在一处解决。这采用了aop的设计思想,即将业务逻辑和异常处理代码分离,使用到了两个注解@ExceptionHandler
和@ControllerAdvice
- 异常处理的步骤
- 创建自定义异常类MyException,及其多个子类NameException…等,作为项目的异常处理类
- 创建一个普通类作为全局异常类,添加注解@ControllerAdivice
- 在全局异常类中添加异常处理方法,添加注解@ExceptionHandler
- 在Controller层中抛出异常,如NameException、AgeException…等
- 创建处理异常的页面告知用户出现异常
- 在Spring.xml文件中注册组件扫描器(用来扫描controller对象以及全局异常类对象)以及注解驱动
//创建自定义异常类如NameException
//在处理器方法中抛出异常
@RequestMapping("/12.do")
public String methodA() throws UserException{
if(a)
throw new NameException("name");
if(b)
throw new AgeException("age");
return "11";
}
//定义全局异常处理类,交给容器管理
@ControllerAdvice
public class GlobalExceptionHandler{
//表示用于处理该类型异常的方法,没有value值的方法最多出现一个
@ExceptionHandler(UserException.class)
public ModelAndView method(){
mv.addObject("出现了XXX异常。");
mv.setViewName("exception");
return mv;
}
}
配置springmvc.xml文件
<!-- 指定controller所在的包 -->
<context:component-scan base-package="com.jarvis.controller">
<!-- 指定@ControllerAdvice所在的包 -->
<context:component-scan base-package="com.jarvis.exception">
<mvc:annotation-driven/>
拦截器
拦截器是SpringMVC中的一种对象,需要实现HandlerInterceptor接口。
拦截器与过滤器类似,功能方向侧重点不同。过滤器主要用于过滤请求参数、设置编码字符集等操作。拦截器是拦截用户请求,做请求判断处理的。
拦截器是全局的,可对多个Controller做拦截,常用于用户登录、权限管理、记录日志等情况中。
- 拦截器的使用步骤
- 定义类实现HandlerInterceptor接口
- 在springmvc.xml文件中声明拦截器
- 使用时间
- 在请求处理前,即在Controller方法执行之前
- 在处理器方法之后
- 在请求处理完成之后
- 实现拦截器
- 创建普通类实现HandlerInterceptor接口,并实现接口中的三个方法
- preHandler(req,resp,handler)
- postHandler(req,resp,handler,modelAndView)
- afterHandler(req,resp,handler,exception)
- springmvc.xml的配置
- 开启组件扫描Controller
- 声明拦截器,并制定拦截的uri地址
- 创建普通类实现HandlerInterceptor接口,并实现接口中的三个方法
- 拦截器的三个方法
- 预处理方法:handler表示被拦截的控制器对象。在控制器方法执行前执行,用户的请求首先会到达此方法,可以获取请求信息以验证是否符合访问要求。验证失败则return false截断请求。
- 后处理方法:在控制器方法执行后执行,能够获取到控制器方法的返回值ModelAndView,以影响输出结果。可以对原来方法的执行结果做二次修正。
- 最后处理方法:exception为异常类对象。在请求处理完成之后执行,主要用于资源回收等一系列的操作。
- 声明拦截器
path指的是拦截请求的uri地址,可以使用通配符**,如path="/user/**"
bean对象用于指定拦截器对象,对不同的请求执行不同的拦截处理。
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="">
<bean class="">
</mvc:interceptor>
</mvc:interceptors>
-
多个拦截器的使用
执行拦截器链,无论有多少个拦截器嵌套,只要有一个为false,目标controller肯定无法访问执行。多个Interceptor可以分别处理不同功能。相互协作成为链式结构 -
Interceptor和filter的区别
- filter是servlet中的对象,是规范中规定的对象;而拦截器是框架中的对象
- filter实现Filter接口;拦截器实现HandlerInterceptor接口
- filter用于设置request、response中的参数,侧重于对数据的过滤
拦截器用于验证请求,并且可以截断请求 - 过滤器在拦截器之前执行
- 过滤器由Tomcat创建、拦截器由SpringMVC框架创建
- filter只有一个执行时间点即doFilter()
拦截器有三个执行时间点 - filter侧重过滤servlet请求
拦截器是拦截普通类方法的执行