Springboot学习笔记(八)——SpringMVC(一)

Spring MVC一开始就定位于一个比较松散的耦合,展示给用户的视图(view)、控制器返回的数据模型(model)、定位视图的视图解析器(ViewResolver)和处理器适配器(HandlerAdapter)等内容都是独立的。换句话说,通过springMVC很容易就把后台的数据转换为各种类型的数据,以满足移动互联网数据多样化的要求。例如,springMVC是基于spring基础框架派生出来的web框架,所以可以很方便的整合到spring框架中。

SpringMVC框架的设计

SpringMVC框架设计图
在这里插入图片描述
其中带有数字的说明,是MVC框架运行的流程。处理请求先到达控制器(Controller),控制器的作用是进行请求分发,这样它会根据请求的内容去访问模型层(Model);数据主要是从数据库和NoSQL中来,而且对于数据库而言还存在事务机制。为了适应这种变化,将模型层再分为两层,即业务层(Service)和数据访问层(Dao);当控制器获取到由模型层返回的数据后,就将数据渲染到视图中,就能展现给用户了。

SpringMVC流程

springmvc全流程
在这里插入图片描述在web服务器启动的过程中,如果再springboot机制下启动springmvc,他就开始初始化一些重要的组件,如DispatcherServlet、HandlerAdapter的实现类RequestMappingHandlerAdapter等组件对象。关于这些组件的初始化,可以看DispatcherServlet.properties,他定义的对象都是在springmvc开始时就初始化,并且存放在springIOC容器中,源代码如下:

DispatcherServlet.properties中的代码:
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
//国际化解析器
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
//主题解析器
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
//HandlerMapping实例
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
//处理器适配器
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
//处理器异常解析器
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
//策略视图名称转换器,当你没有返回视图逻辑名称的时候,通过它可以生成默认的视图名称
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
//视图解析器
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
//Falsh管理器
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

测试代码:

package com.springboot.chapter09.dao;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.springboot.chapter09.pojo.User;
//Dao层
@Mapper		//自动把注解的接口生成代理类
public interface UserDao {
	User getUser(Integer id);
	//@Param用来传递多个参数,与sql中的字段名对应
	List<User> findUsers(@Param("userName") String userName,@Param("note")String note);
}

@Mapper是spring的注解,一般用在接口上,会自动把@Mapper注解的接口生成代理类,相当于xxxMapper.xml;@Param作用在Dao层的注解,用于传递参数,从而与SQL中的字段相对应,一般在2~5个参数时使用;@MapperScan用在springboot的启动类中,配置一个或多个包路径,自动的扫描这些路径下的类并生成代理类;@Repository是mybatis的注解,一般用于Dao层接口的实现类上

//Service层
@Service
//@Transactional
public class UserServiceImpl implements UserService {
	@Autowired
	private UserDao userDao;
	@Override
	public User getUser(Integer id) {
		// TODO Auto-generated method stub
		return this.userDao.getUser(id);
	}
	@Override
	public List<User> findUsers(String userName, String note) {
		// TODO Auto-generated method stub
		return this.userDao.findUsers(userName, note);
	}
}
//Controller层
@Controller
@RequestMapping("/user")
public class UserController {
	@Autowired
	private UserService userService;
	
	@RequestMapping("/get")
	public ModelAndView getUser (Integer id) {
		//访问模型层得到数据
		User user = userService.getUser(id);
		//模型和视图
		ModelAndView model = new ModelAndView();
		//定义模型视图
		model.setViewName("user/details");
		//加入数据模型
		model.addObject("user", user);
		//返回模型和视图
		return model;
	}
}

@Controller表明这是一个控制器,然后@RequestMapping代表请求路径和控制器(或其方法)的映射关系,他会在web服务器启动springMVC时,就被扫描到HandlerMapping的机制中存储,之后在用户发起请求被DispatcherServlet拦截后,通过URI和其他条件,通过HandlerMapper机制就能找到对应的控制器(或其方法)进行响应,只是通过HandlerMapping返回的是一个HandlerExecutionChain对象,这个对象的源代码如下所示:

public class HandlerExecutionChain {
	//日志
	private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
	//处理器
	private final Object handler;
	//拦截器数组
	@Nullable
	private HandlerInterceptor[] interceptors;
	//拦截器列表
	@Nullable
	private List<HandlerInterceptor> interceptorList;
	//拦截器当前下标
	private int interceptorIndex = -1;
	......
}

从源码中可以看出,HandlerExecutionChain对象包含一个处理器(Handler)。这里的处理器是对控制器(Controller)的包装,因为控制的方法可能存在参数,那么处理器就可以读入HTTP和上下文的相关参数然后再传递给控制方法。而在控制器执行完毕后返回后,处理器又可以通过配置信息对控制器的返回结果进行处理。处理器包含了控制器方法的逻辑,此外还有处理器的拦截器(interceptor),这样就能够通过拦截处理器进一步地增强处理器的功能。
得到了处理器(handler),还要去运行,但是我们有普通的HTTP请求,也有按beanname的请求,甚至是websocket的请求,所以他还需要一个适配器去运行HandlerExecutionChain对象包含的处理器,这就是HandlerAdapter接口定义的实现类。在DispatcherServlet.properties的代码中,可以看到在SpringMVC中最常用的HandlerAdapter的实现类,这便是HttpRequestHandlerAdapter。通过请求的类型,DispatcherServlet就会找到他来执行web请求的HandlerExecutionChai对象包含的内容,这样就能够执行我们的处理器(handler)。
在处理器调用控制器时,它首先会通过模型层得到数据,再放入数据数据模型中,最后将返回模型和视图(ModelAndView)对象,得到控制器返回的视图名称,就到了视图解析器(ViewResolver),去解析视图逻辑名称了。
在DispatcherServlet.properties的代码中可以看到视图解析器(ViewResolver)的自动初始化,为了定制InternalResourceViewResolver初始化,可以在配置文件application.properties中进行配置,如下:

spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

通过修改这样的配置,就能在SpringBoot的机制下定制InternalResourceViewResolver这个视图解析器的初始化,也就是在返回视图名称之后,他会以前缀(prefix)和后缀(suffix)以及视图名称组成全路径定位视图。
视图解析器定位到视图后,视图的作用是将数据模型(model)渲染,这样就能够响应用户的请求。这一步就是视图将数据模型渲染出来,用来展示给用户看。

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div align="center">
		<table border="1">
			<tr>
				<td>标签</td>
				<td></td>
			</tr>
			<tr>
				<td>用户编号</td>
				<td><c:out value="${user.id}"></c:out></td>
			</tr>
			<tr>
				<td>用户名称</td>
				<td><c:out value="${user.userName}"></c:out></td>
			</tr>
			<tr>
				<td>用户备注</td>
				<td><c:out value="${user.note}"></c:out></td>
			</tr>
		</table>
	</div>
</body>
</html>

在这里插入图片描述有时候,我们可能需要的是json数据集,目前前后台分离的趋势,使用JSON已经是主流的方式,正如我们之前使用的@RequestBody标明的方法一样,在后面springMVC会把数据转换为JSON数据集,但是这里先不用@RequestBody,而是先用MappingJackson2JsonView转换出JSON,如下代码:

@RequestMapping("/json")
	public ModelAndView getUserForJSON(Integer id) {
		//访问模型层得到数据
		User user = userService.getUser(id);
		//模型和视图
		ModelAndView model = new ModelAndView();
		//生成json视图
		MappingJackson2JsonView jsonview = new MappingJackson2JsonView();
		model.setView(jsonview);
		//加入模型
		model.addObject("user", user);
		return model;
	}

可以看到,在控制器的方法中模型和视图(ModelAndView)中捆绑了JSON视图(MappingJackson2JsonView)和数据模型(User对象),然后返回,结果也会转变为JSON,只是需要注意的是这步与使用jsp作为视图是不一样的。在UserController类中给视图设置了名称。它会根据视图解析器(InternalResourceViewResolver)的解析找到jsp,然后渲染数据到视图中,从而展示最后的结果,而这里的JSON视图是没有视图解析器的定位视图的,因为他不是一个逻辑视图,只是需要将数据模型(这里是User对象)转换为JSON而已。
因为MappingJackson2JsonView是一个非逻辑视图,他并不需要视图解析器进行定位,他的作用只是将数据模型渲染为JSON数据集来响应请求。

定制SpringMVC的初始化

在Servlet3.0规范中,web.xml再也不是一个必须的配置文件。为了适应这个规范,springMVC从3.1版本开始也进行了支持,也就是我们不在需要通过任何的xml去配置springMVC的运行环境,正如springboot的宗旨,消除xml的繁杂配置。为了支持对于springMVC的配置,spring提供了接口WebMvcConfigurer,这是一个基于java8的接口,所以其大部分方法都是default类型的,但是他们都是空实现,这样只需要实现这个接口,重写需要自定义的方法即可。在springboot中,自定义是通过配置类WebMvcAutoConfiguration定义的,他有一个静态的内部类WebMvcAutoCongfigurationAdapter,通过它springboot就自动配置了springMVC的初始化,他们之间的关系如下图所示:
在这里插入图片描述在WebMvcAutoConfigurationAdapter类中,他会读入Spring配置Spring MVC的属性来初始化对应组件,这样便能够在一定程度上实现自定义。

SpringMVC实例

SpringMVC开发的核心是控制器的开发,控制器的开发又分为以下几个步骤,首先是定义请求分发,让springMVC能够产生HandlerMapping,其次是接收请求获取参数,再次是处理业务逻辑获取数据模型,最后是绑定视图和数据模型。视图将数据模型渲染则是视图定义的问题,不属于控制器开发的步骤。
下面演示一个用户列表查询的界面,使用ajax异步请求,进入页面后直接查询。

  1. 开发控制器
package com.springboot.chapter09.controller;
/**import**/
@Controller
@RequestMapping("/user")
public class UserController {
	@Autowired
	private UserService userService;
	......
	@RequestMapping("/table")
	public ModelAndView table() {
		List<User> userList = userService.findUsers(null, null);
		ModelAndView model = new ModelAndView();
		model.setViewName("user/table");
		model.addObject("userList", userList);
		return model;
	}
	@RequestMapping("/list")
	@ResponseBody
	public List<User> list(@RequestParam(value = "userName",required = false)String userName,
			@RequestParam(value = "note",required = false)String note){
		return userService.findUsers(userName, note);
	}
}

开发控制器首先是指定请求分发,这个任务是由注解@RequestMapping去完成的,这个注解可以标注类或方法。当一个类被标注的时候,所有关于他的请求,都需要在@RequestMapping定义的URL下。这个注解还可以标注方法,当方法被标注后,也可以定义部分URL,这样就能让请求的URL找到对应的路径。配置了扫描路径后,Spring MVC扫描机制就可以将其扫描,并且装载为HandlerMapping,以备后面使用。这里的控制器存在两个方法。table方法的任务是进入页面时,首先查询所有的用户,这是一个没有条件的查询,当他查询出所有的用户数据后,创建模型和视图(ModelAndView),接着指定视图名称为“user/table”,然后将查询的用户列表捆绑到模型和视图中,最后返回模型和视图。

  1. 视图和视图渲染
    这里使用了EasyUI作为页面端页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" type="text/css" href="../../easyui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="../../easyui/themes/icon.css">
<link rel="stylesheet" type="text/css" href="../../easyui/demo/demo.css">
<script type="text/javascript" src="../../easyui/jquery.min.js"></script>
<script type="text/javascript" src="../../easyui/jquery.easyui.min.js"></script>
<script type="text/javascript">
	// 定义事件方法
	function onSearch() {
		// 指定请求路径
		var opts = $("#dg").datagrid("options");
		opts.url = "./list";
		// 获取查询参数
		var userName = $("#userName").val();
		var note = $("#note").val();
		// 组织参数
		var params = {};
		if (userName != null && userName.trim() != '') {
			params.userName = userName;
		}
		if (note != null && note.trim() != '') {
			params.note = note;
		}
		// 重新载入表格数据
		$("#dg").datagrid('load', params);
	}
</script>
</head>
<body>
	<div style="margin: 20px 0;"></div>
	<div class="easyui-layout" style="width: 100%; height: 350px;">
		<div data-options="region:'north'" style="height: 50px">
			<form id="searchForm" method="post">
				<table>
					<tr>
						<td>用户名称:</td>
						<td><input id="userName" name="userName"
							class="easyui-textbox" data-options="prompt:'输入用户名称...'"
							style="width: 100%; height: 32px"></td>
						<td>备注</td>
						<td><input id="note" name="note" class="easyui-textbox"
							data-options="prompt:'输入备注...'" style="width: 100%; height: 32px">
						</td>
						<td><a href="#" class="easyui-linkbutton"
							data-options="iconCls:'icon-search'" style="width: 80px"
							onclick="onSearch()">查询</a></td>
					</tr>
				</table>
			</form>
		</div>
		<div data-options="region:'center',title:'用户列表',iconCls:'icon-ok'">
			<table id="dg" class="easyui-datagrid" data-options="border:false,singleSelect:true,
fit:true,fitColumns:true">
				<thead>
					<tr>
						<th data-options="field:'id'" width="80">编号</th>
						<th data-options="field:'userName'" width="100">用户名称</th>
						<th data-options="field:'note'" width="80">备注</th>
					</tr>
				</thead>
				<tbody>
					<!--使用forEache渲染数据模型-->
					<c:forEach items="${userList}" var="user">
						<tr>
							<td>${user.id}</td>
							<td>${user.userName}</td>
							<td>${user.note}</td>
						</tr>
					</c:forEach>
				</tbody>
			</table>
		</div>
	</div>
</body>
</html>

这里使用了EasyUI和他的控件DataGrid(数据网格),通过JSTL的foreach标签进行循环将控制信息返回用户列表渲染到这张JSP中,所以在刚刚进入页面的时候,就可以展示用户列表。采用先取数据后渲染的方式,所以刚刚进入页面的时候并不会出现ajax的异步请求,这样有助于提高UI(用户接口)体验。
在这里插入图片描述在页面中还定义了两个文本框,用来输入用户名和备注,然后通过查询按钮进行查询,这里查询按钮的点击事件为onSearch,这样就能够找到onSearch函数来执行查询,在这个函数中,首先定义DataGrid请求的URL,他指向了UserController 类的list方法,然后通过jQuery去获取两个文本框的参数值,再通过DataGrid的load方法,传递参数去后端查询,得到数据后重新载入DataGrid的数据,这样DataGrid就能够得到查询的数据了。
再看会UserController 类的list方法,首先他标注了@RequestBody,这样SpringMVC就知道最终需要把返回的结果转换为JSON数据,然后是获取参数,这里使用了注解@RequestParam,通过指定参数名称使得HTTP请求的参数和方法的参数进行绑定,只是这个注解的默认的规则是参数不能为空。为了克服这根问题,代码将其属性required设置为false即可。其意义就是允许参数为空。
在这里插入图片描述从图中可以看出,单击查询按钮后,就会执行ajax请求,把数据取回来,用来显示在DataGrid控件中。这样就可以在第一次进入一个新的页面时,就可以无刷新地显示数据,而在查询等操作中使用ajax,有效地提高了用户体验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值