CMS小项目04---SpringMVC视图_数据及视图映射方式
临时插入一个小插曲,说说SpringMVC的一些小应用。
对于框架类型的东西,不敢谈的太深,容易把自己陷进去,这里就只总结一些应用及表面的东西。
往实际点说,我认为SpringMVC最好用的地方就是视图解析和注解。至于更深层的,自认为还不能在博客上归纳。
这两个特点几乎让我们摆脱了繁重的配置以及底层功能的实现让我们可以更加关注业务逻辑。
1.MVC架构
说起视图,那么必须的了解到MVC架构。
这是网上随处可见的图,根据这个图简单的梳理一下,自认为没有网上的大神理解透彻,所以不深入。
用我的视角,MVC有三个部分
名称 | 对应 | 功能 |
---|---|---|
控制器 | controller或者action | 接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。 |
模型 | model和service | 数据模型,提供要展示的数据及执行业务逻辑 |
视图 | JSP或者其他的展示 | 负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。 |
所以,controller会告诉框架,我们要展示的视图是什么,而同时,会将模型的数据传递给视图,控制器就是一个调度员。
而我们主要集中在SpringMVC对视图的处理。
最常见的应该是这一段代码:
<!-- 定义跳转的文件的前后缀 ,视图模式配置-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 这里的配置我的理解是自动给后面action的方法return的字符串加上前缀和后缀,变成一个 可用的url地址 -->
<property name="prefix" value="/WEB-INF/static/cms/" />
<property name="suffix" value=".jsp" />
</bean>
一开始对于SpringMVC的理解就只有这一段代码,而在项目中,我一开始也只记住了这一段代码。
简单点说,就是讲controller设置的视图名称,映射到正确的视图上面。
这一篇只针对视图,那么只需要理清楚两个地方。
- 数据映射
- 视图指定
2.数据映射与视图指定
数据映射说明白点,就是模型里面的数据,我们怎么给视图。视图指定就是说映射到哪个视图。都很简单,没必要分开讲。
通常我们常用四个对象来完成这个过程:
- ModelAndView
- Model
- ModelMap
-
Map
注解方式:
- @SessionAttribute(这个就不举例了,感觉用的也不多)
- @ModelAttribute
这些我在一位博主的博文里面看到了不错的梳理
链接
我只是贴上一些实际的用法:
package com.cms.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.mysql.fabric.Response;
/**
*@author:gang
*@version:
**/
@Controller
@RequestMapping("/test")
public class MVCTest {
private Map<String, String> map;
private List<String> list;
private String [] array ={"1","2","3"};
private String name = new String("110");
@RequestMapping("/modelandview")
public ModelAndView modelAndViewTest(HttpServletRequest request){
ModelAndView modelAndView = new ModelAndView();
//设置对比参数
//记住,要初始化,不然会包NullpointException
map = new HashMap<String, String>();
HashMap<String, String> map2 = new HashMap<String, String>();
map2.put("mapkey1", "001");
map2.put("mapkey3", "002");
map.put("mapkey", "mapvalue1_001");
map.put("mapkey1", "mapvalue1_002");
list = new LinkedList<String>();
list.add("list1_1");
list.add("list1_2");
array[0]="1_1";
name="modelandview";
//添加数据
modelAndView.addObject(map);
modelAndView.addObject(map2);
modelAndView.addObject("map", map);
modelAndView.addObject("list", list);
modelAndView.addObject("array", array);
modelAndView.addObject("name", name);
HttpSession session = request.getSession();
session.setAttribute("name2", name2);
System.out.println(modelAndView.isEmpty());
modelAndView.setViewName("mvctest");
System.out.println(modelAndView.getViewName());
//返回视图
return modelAndView;
}
//
@RequestMapping("/modelmap")
public String modelMap(ModelMap modelMap){
//设置对比参数
map.put("mapkey", "mapvalue2_001");
map.put("mapkey1", "mapvalue2_002");
list.add("list2_1");
list.add("list2_2");
array[0]="2_1";
name="modelMAP";
//添加数据模型
modelMap.put("map", map);
modelMap.put("name", name);
modelMap.addAttribute("list", list);
modelMap.put("array", array);
//映射视图名
return "mvctest";
}
//
@RequestMapping("/model")
public String modelTest(Model model){
//设置对比参数
map.put("mapkey", "mapvalue3_001");
map.put("mapkey1", "mapvalue3_002");
list.add("list3_1");
list.add("list3_2");
array[0]="3_1";
name="model";
//添加数据模型
model.addAttribute("name", name);
model.addAttribute("list", list);
model.addAttribute("array",array);
model.addAttribute(map);
//映射视图名
return "mvctest";
}
@RequestMapping("/map")
public String mapTest(Map<String, Object> map){
map.put("mapkey", "mapvalue3_001");
map.put("mapkey1", "mapvalue3_002");
list.add("list3_1");
list.add("list3_2");
name="map";
//添加数据模型
map.put("name", name);
map.put("map", map);
map.put("list", list);
map.put("array", Arrays.asList("map4_1", "map4_2", "map4_3"));
//映射视图名
return "mvctest";
}
//注解的使用
@ModelAttribute(value="modelAttribute")
public String modelAttribute(){
return "modelAttribute success";
}
@ModelAttribute
public void modelAttribute(ModelMap model){
model.addAttribute("modelAttribute02", "modelAttribute02");
}
}
以及对应的结果显示
以及注解的
3.实现方式探讨
这一篇我们主要是为了去探讨更深层的一点,起因是这一段代码:
//没有名字,他是怎么映射的呢
modelAndView.addObject(map);
所以,我们用Debug一步步跟踪源码来看看
注意,debug时tomcat的开启会明显变慢,需要将Tomcat的初始化时间调节一下,不然会报错
下面正式开始:
//看到这一个方法,是不是很神奇,居然是将数据保存在modelmap中,
//也侧面解释了modelAndView.addAllObjects(modelMap)方法的执行方式
public ModelAndView addObject(Object attributeValue) {
getModelMap().addAttribute(attributeValue);
return this;
}
//包括另外一个addObject
public ModelAndView addObject(String attributeName, Object attributeValue) {
getModelMap().addAttribute(attributeName, attributeValue);
return this;
}
继续:
那么接着看,后面是怎么执行的呢
也是分成有name和无name两个方面
//无name
public ModelMap addAttribute(Object attributeValue) {
Assert.notNull(attributeValue, "Model object must not be null");
//为空的集合就返回这个ModelMap
if (attributeValue instanceof Collection && ((Collection<?>) attributeValue).isEmpty()) {
return this;
}
return addAttribute(Conventions.getVariableName(attributeValue), attributeValue);
}
//有name
public ModelMap addAttribute(String attributeName, Object attributeValue) {
Assert.notNull(attributeName, "Model attribute name must not be null");
put(attributeName, attributeValue);
return this;
}
可以发现,
首先用Assert工具类进行判断,这个工具类我们会在附件中贴上来,Asserts是Spring的断言工具类,通常用于数据合法性检查,可以将复杂的if判断语句进行简化,例如下面的例子
Assert.notNull(attributeValue, "Model object must not be null");
Assert.notNull(attributeName, "Model attribute name must not be null");
我们继续,先进行了Assert判断,在判断是否为集合,并且判断是否为空,然后进行了put操作,实际上还是将对象放到了map对象之中
对于有名字的就不多说了,这里就只分析不带名字的是怎么将名字映射到对象上的
可以看到,有一个这样的语句Conventions.getVariableName(attributeValue),
return addAttribute(Conventions.getVariableName(attributeValue), attributeValue);
很明显,这是一个起别名的类,看一下具体的源码
public static String getVariableName(Object value) {
//判断数据合法性
Assert.notNull(value, "Value must not be null");
//说明一个Class
Class<?> valueClass;
boolean pluralize = false;
//判断是什么类型
if (value.getClass().isArray()) {
valueClass = value.getClass().getComponentType();
pluralize = true;
}
else if (value instanceof Collection) {
Collection<?> collection = (Collection<?>) value;
if (collection.isEmpty()) {
throw new IllegalArgumentException("Cannot generate variable name for an empty Collection");
}
Object valueToCheck = peekAhead(collection);
//反射,根据值确定这个Class
valueClass = getClassForValue(valueToCheck);
pluralize = true;
}
else {
valueClass = getClassForValue(value);
}
//根据属性获取name
String name = ClassUtils.getShortNameAsProperty(valueClass);
return (pluralize ? pluralize(name) : name);
}
//到了这一步,基本上我们的推测就出来了,这个属性的名字和类型应该是一样的,那么,,,我们debug看看
没错,是被映射为了类型名
那么我们试试当有两个map的时候呢
map2.put("mapkey1", "001");
map2.put("mapkey3", "002");
map.put("mapkey", "mapvalue1_001");
map.put("mapkey1", "mapvalue1_002");
modelAndView.addObject(map);
modelAndView.addObject(map2);
并没有前面的数据。
所以说,hashmap是一个对象,在里面可以取出所有包含的值,多个map会被后者替换
4.总结
视图的实际应用基本上就这些了,对于其内部的详细流程,及数据最终会被放在哪,我下次再详细的分析。
5.附件:Assert
抽象类,每个方法的具体实现大概都可以猜出来
package org.springframework.util;
import java.util.Collection;
import java.util.Map;
public abstract class Assert {
public static void isTrue(boolean expression, String message) {
if (!expression) {
throw new IllegalArgumentException(message);
}
}
public static void isTrue(boolean expression) {
isTrue(expression, "[Assertion failed] - this expression must be true");
}
public static void isNull(Object object, String message) {
if (object != null) {
throw new IllegalArgumentException(message);
}
}
public static void isNull(Object object) {
isNull(object, "[Assertion failed] - the object argument must be null");
}
public static void notNull(Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
public static void notNull(Object object) {
notNull(object, "[Assertion failed] - this argument is required; it must not be null");
}
public static void hasLength(String text, String message) {
if (!StringUtils.hasLength(text)) {
throw new IllegalArgumentException(message);
}
}
public static void hasLength(String text) {
hasLength(text,
"[Assertion failed] - this String argument must have length; it must not be null or empty");
}
public static void hasText(String text, String message) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException(message);
}
}
public static void hasText(String text) {
hasText(text,
"[Assertion failed] - this String argument must have text; it must not be null, empty, or blank");
}
public static void doesNotContain(String textToSearch, String substring, String message) {
if (StringUtils.hasLength(textToSearch) && StringUtils.hasLength(substring) &&
textToSearch.contains(substring)) {
throw new IllegalArgumentException(message);
}
}
public static void doesNotContain(String textToSearch, String substring) {
doesNotContain(textToSearch, substring,
"[Assertion failed] - this String argument must not contain the substring [" + substring + "]");
}
public static void notEmpty(Object[] array, String message) {
if (ObjectUtils.isEmpty(array)) {
throw new IllegalArgumentException(message);
}
}
public static void notEmpty(Object[] array) {
notEmpty(array, "[Assertion failed] - this array must not be empty: it must contain at least 1 element");
}
public static void noNullElements(Object[] array, String message) {
if (array != null) {
for (Object element : array) {
if (element == null) {
throw new IllegalArgumentException(message);
}
}
}
}
public static void noNullElements(Object[] array) {
noNullElements(array, "[Assertion failed] - this array must not contain any null elements");
}
public static void notEmpty(Collection<?> collection, String message) {
if (CollectionUtils.isEmpty(collection)) {
throw new IllegalArgumentException(message);
}
}
public static void notEmpty(Collection<?> collection) {
notEmpty(collection,
"[Assertion failed] - this collection must not be empty: it must contain at least 1 element");
}
public static void notEmpty(Map<?, ?> map, String message) {
if (CollectionUtils.isEmpty(map)) {
throw new IllegalArgumentException(message);
}
}
public static void notEmpty(Map<?, ?> map) {
notEmpty(map, "[Assertion failed] - this map must not be empty; it must contain at least one entry");
}
public static void isInstanceOf(Class<?> clazz, Object obj) {
isInstanceOf(clazz, obj, "");
}
public static void isInstanceOf(Class<?> type, Object obj, String message) {
notNull(type, "Type to check against must not be null");
if (!type.isInstance(obj)) {
throw new IllegalArgumentException(
(StringUtils.hasLength(message) ? message + " " : "") +
"Object of class [" + (obj != null ? obj.getClass().getName() : "null") +
"] must be an instance of " + type);
}
}
public static void isAssignable(Class<?> superType, Class<?> subType) {
isAssignable(superType, subType, "");
}
public static void isAssignable(Class<?> superType, Class<?> subType, String message) {
notNull(superType, "Type to check against must not be null");
if (subType == null || !superType.isAssignableFrom(subType)) {
throw new IllegalArgumentException(message + subType + " is not assignable to " + superType);
}
}
public static void state(boolean expression, String message) {
if (!expression) {
throw new IllegalStateException(message);
}
}
public static void state(boolean expression) {
state(expression, "[Assertion failed] - this state invariant must be true");
}
}