MVC(Model-View-Controller)模式是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。MVC可对程序的后期维护和扩展提供方便,也为程序某些部分的重用提供了方便。
MVC 设计模式并不是Java Web应用的专属,几乎现在所有 B/S 结构的软件都采用了MVC 设计模式:在早期的 Java Web 开发中,主要是JSP+Java Bean模式,如图所示。服务器端只有JSP页面,所有操作都在JSP页面中,严重的耦合度增加了开发的难度,对后期的维护和扩展极其不利。
首先,Spring MVC框架是围绕着Dispatche Servlet工作的,这个是其核心美,其实它本质是一个Servlet,因此它可以拦截HTTP 发送过来的请求,在Servlet初始化时,Spring MVC 会根据配置,获取配置信息,从而得到统一资源标识符(URL,Uniform Resource Identifier)和处理器(Handler)之间的映射关系(Ha adlerMapping),为了使用更加灵活,且能增强功能,Spring MVC还会给处理器加入拦截器,所以还可以在处理器执行前后加入自己的代码,这样就构成了一个处理器的执行链(Handl er Execution Chain),并且根据上下文初始化视图解析器等内容,当处理器返回的时候就可可以通过视图解析器定位视图,然后将数据模型渲染到视图中,以响应用户的请求。
Spring MVC的核心在于其流程,这是使用 Spring MVC框架的基础,Spring MVCH一种基Servlet 的技术,它提供了核心控制器 DispatcherServlet 和相关的组件,并制定了松散的结构,以适应各种灵活的需求。其流程图如图所示。
具体的步骤如下:
以上就是一个 Spring MVC完整的流程,它是一个松散的结构,所以可以满足各类请求的需要,为此它也实现了大部分请求所需的类库,拥有较为丰富的类库供使用,所以流程中大部分组件并不需要用户去实现。
1、创建web项目并导入jar包
2、 在index.jsp页面中创建一个a标签,执行一个地址请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
首页
欢迎来到项目首页
请求测试
zhuye.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
项目主页
SpringMVC的项目主页!
3、在web.xml中配置springMVC的核心控制器DispatcherServlet,同时设置它创建的时候加载
dispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springmvc.xml
1
dispatcherServlet
/
关于web.xml中的一些配置解释如下。
①contextConfigLocation:用于指定配置文件的位置。如果有多个配置文件,以逗号
隔开;如果没有指定配置文件,Spring监听器会自动查找/WEB-INF/路径下applicationContext.xml配置文件。
②ContextLoaderListener:它是Spring提供的监听器,实现了 ServleiCon接口。由它来查找 Spring 的配置文件,完成对 Spring 的初始化。
③ DispatcherServlet:它是Spring的核心控制器,其初始化属性配置用于指定spring的配置文件位置。如果没有进行初始化配置,其默认的配置文件在/WEB-INF 路径下,名称为配置的servlet-name连上-servlet.xml。例如servlet-name为dispatcher,则配置文件名称为dispatcher-servlet.xml。
④ servlet-mapping:它是 servlet拦截配置,servlet-name 需要和上面配置的servict中的servlet-name 一致。
4、在src目录下创建核心配置文件springmvc.xml,扫描控制器的包、配置视图解析器、开启注解驱动支持(默认配置了 HandlerMapping映射器 和 HandlerAdapter 适配器)
关于springmvc.xml 中的一些配置解释如下。
①
②
InternalResourceViewResolver:定义视图解析器,解析器中定义了前缀和后缀,这样视图就知道去 Web 工程的/WEB-INF view 文件夹中找到对应的 JSP 文件作为视图响应用户请求。
④
@Controller
public class TestController{
@RequestMapping("/test")
public String test(){
//处理test请求
System.out.println("TestController......test");
//做出响应,直接返回视图的名字,视图解析会在指定目录中找到指定后缀的 该视图页面响应给前端页面
return "zhuye";
}
}
测试结果
在Spring MVC 应用程序中,RequestDispatcher 这个 Servlet 负责将进入的 HTTP 请求路由到控制器的处理方法。
在对Spring MVC 进行配置的时候,需要指定请求与处理方法之间的映射关系。要配置Web请求的映射,就需要用到@RequestMapping 注解。
控制器的开发
控制器开发是Spring MVC的核心内容,主要分为以下三个步骤。
(1) 获取请求参数。
(2)处理业务逻辑。
(3)绑定模型和视图。
使用 ServletAPI 对象作为方法参数
SpringMVC 还支持使用原始 ServletAPI 对象作为控制器方法的参数。
@RequestMapping("/login")
//可以使用原生态的servlet API 进行处理
public void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("UserController......login");
String username = request.getParameter("username");
System.out.println(username);
response.sendRedirect("zhuye.jsp");
}
普通参数的绑定
使用要求
集合类型,有两种方式:
第一种:
第二种:
基本类型参数:包括基本类型和 String 类型
请求地址提供请求参数:username、password。
@RequestMapping("/login")
public String login(String username,String password){
System.out.println("UserController......login");
System.out.println(username);
System.out.println(password);
return "zhuye";
}
POJO 类型参数:实体类
在jsp页面中添加一个表单,并创建对应的实体类User(name与参数名称一致)
@RequestMapping("/login")
public void login(User user){
//定义实体类,类的属性和请求参数的name属性一致,就会自动获取数据并封装到实体类中
System.out.println("UserController......login");
System.out.println(user);
}
可以在控制器的参数中通过@ReqeustParam指定URL传递参数名称
@RequestParam 可以手动指定请求参数和参数的数据绑定 required 表示请求是必须传递该参数
关于@RequcstParam中的属性配置解释如下。
required:值类型为布尔值,默认值为true,表示不允许参数为空,如果要允许为空,则设置为false。
defaultValue:当参数为空时候的默认值。
注意:注解@RequestParam可以对自定义简单类型的参数进行绑定,即如果使用@RequestParam,就无须设置controller方法的形参名称与request传入的参数名称一致。而不使用@RequestParam注解时,就要求controller方法的形参名称与request传入的参数名称一致,这样才能绑定成功。
@RequestMapping("/login")
public String login(@RequestParam(value = "username",required = true)String name,String password){
//@RequestParam 可以手动指定请求参数 和 参数的数据绑定 required 表示请求是必须传递该参数
System.out.println("UserController......login");
System.out.println(name);
System.out.println(password);
return "zhuye";
}
如果要求绑定的参数一定不能为空,可以使用@RequestParam注解中的required属性来指定该形参是否必须传入,required属性为“true”指定参数必须传入。
@RequestMapping("/del")
public String del(@RequestParam(required = true)Integer id){
System.out.println("UserController......del");
System.out.println(id);
return "zhuye";
}
ReuqestBody 主要是处理json串格式的请求参数,要求使用方指定header content-type:application/json
RequestBody 通常要求调用方使用post请求@RequestBody 只支持post请求,把数据变成key=value...结构的数据
@RequestMapping("/save")
public String save(@RequestBody String data) {
//@RequestBody 只支持post请求,把数据变成key=value...结构的数据
System.out.println("UserController......save");
System.out.println(data);
return "zhuye";
}
save.jsp 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
到 web.xml 中去配置 Filter 的拦截路径
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
utf-8
encodingFilter
/*
请求转发 :浏览器发送了一次请求,在服务器内部转发了一次;请求了一次,页面地址不会变化;
request.getRequestDispatcher("zhuye.jsp").forward(request,response);
重定向 :浏览器发送了一次请求,服务器让浏览器再去请求一次;请求了两次,页面地址会变化;
response.sendRedirect("zhuye.jsp");
springMVC的响应方式:请求转发
SpringMVC中可以使用两种方式进行重定向和转发:
servlet的请求转发(forward)和重定向(sendRedirect)
使用ModelAndView对象进行转发和重定向
在Controller中,可以使用ModelAndView对象进行转发和重定向。使用ModelAndView对象进行转发时,可以将数据添加到ModelAndView对象中,然后将其返回给DispatcherServlet
DispatcherServlet会将其转发到指定的页面。使用ModelAndView对象进行重定向时,需要设置重定向的URL,然后将其返回给DispatcherServlet,DispatcherServlet会将其重定向到指定的URL。
@RequestMapping("/login1")
public ModelAndView login1(User user){
ModelAndView mv = new ModelAndView();
System.out.println("UserController......login");
System.out.println(user);
//存储数据,默认存入request作用域
mv.addObject("data","后端响应的数据");
//指定响应的视图,执行的是请求转发
mv.setViewName("zhuye");
//请求转发和重定向使用关键词后不会再经过视图解析器,所以需要明确 位置/页面名.后缀
//mv.setViewName("forward:/zhuye.jsp");
//mv.setViewName("redirect:/zhuye.jsp");
return mv;
}
}
GoodsController类
@Controller
public class GoodsController {
@RequestMapping("/findAll")
public ModelAndView findAll(){
ModelAndView mv = new ModelAndView();
//查询商品信息
List goodsList = new ArrayList<>();
Goods g1 = new Goods();
g1.setGid(1);
g1.setGname("泡面");
g1.setPrice(4.5);
Goods g2 = new Goods();
g2.setGid(2);
g2.setGname("可乐");
g2.setPrice(3.5);
Goods g3 = new Goods();
g3.setGid(3);
g3.setGname("火腿肠");
g3.setPrice(6.5);
goodsList.add(g1);
goodsList.add(g2);
goodsList.add(g3);
mv.addObject("goodsList",goodsList);
mv.setViewName("zhuye");
return mv;
}
}
@Controller
public class UserController {
@RequestMapping("/login")
public ModelAndView login(User user){
ModelAndView mv = new ModelAndView();
System.out.println(user);
if(user.getUsername().equals("admin") && user.getPassword().equals("666")){
//我们重定向到商品查询
mv.setViewName("redirect:/findAll");//重定向请求 /findAll 这个地址
}else{
mv.addObject("error","用户名或密码错误!");
mv.setViewName("error");
}
return mv;
}
}
页面插入图片
在springmvc.xml配置文件中配置
mapping:指定静态资源的映射
location:指定静态资源的文件目录
Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。用户还可以自定义一些拦截器来实现特定的功能。
在谈到拦截器,我们还要提到拦截器链(Interceptor Chain)这个词,它是将拦截器按照定的顺序联结成一条链。在访问被拦截的方法或者字段时,拦截器链中的拦截器就会按其定义的顺序被调用;
拦截器的定义
Spring MVC拦截器的实现一般有两利方式:第一种方式是要定义的Interceptor类要实现 Spring 的 HandlerInterceptor 接口; 第二种方式是继承实现了HandlerInterceptor接口的类,比如Spring已经提供的实现了HandlerInterceptor接口的抽象类HandlerInterceptorAdapter
接口中定义了三个方法,其含义解释如下。
注:拦截器中的前置方法返回值决定了后续的流程是否执行,同时需要注意的是后置方法postHandle是在handler方法(Controller)成功执行之后、dispatcher渲染视图之前执行。因此如果handler 方法抛出异常,则 postHandle也是不会执行的。但 afterCompletion 只要前置方法返回true,后面无论是否抛出异常,都会在视图渲染结束后执行。
从输出结果可以看出,当其中一个前置方法返回false,那么后面的前置方法、控制器方法、后置方法都不会执行,只有对应返回true的拦截器的完成方法(afterCompletion)才会逆序执行。
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor......preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor......postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor......afterCompletion");
}
}
判断用户是否登录(首先登录时需要把用户信息存入session,后续只要判断session中是否存在用户信息),如果没有登录则拦截。
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor......preHandle(前置通知)");
//登录请求不拦截,其它请求查看session域
String servletPath = request.getServletPath();
if(servletPath.equals("/login")){
return true;
}else{
//获得session域中的user对象
User user = (User) request.getSession().getAttribute("user");
if(user!=null){
return true;
}else{
return false;
}
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor......postHandle(后置通知)");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor......afterCompletion(最终通知)");
}
}
拦截