一兽肤、Spring MVC起步
1.1 跟蹤Spring MVC的請(qǐng)求
在請(qǐng)求離開瀏覽器時(shí)(步驟
1
),會(huì)帶有用戶所請(qǐng)求內(nèi)容的信息另假,至少會(huì)包含請(qǐng)求的URL
像屋,但是還可能帶有其他信息。
請(qǐng)求的第一站是前端控制器
DispatcherServlet
边篮。SpringMVC
所有的請(qǐng)求都會(huì)通過這個(gè)前端控制器己莺,前端控制器是常用的Web
應(yīng)用程序模式,在這里是一個(gè)單實(shí)例的Servlet
將請(qǐng)求委托給應(yīng)用程序的其他組件來執(zhí)行實(shí)際的處理戈轿。DispatcherServlet
的任務(wù)是將請(qǐng)求轉(zhuǎn)發(fā)給控制器(Controller
)凌受。為了達(dá)到此目的,DispatcherServlet
需要查詢一個(gè)或多個(gè)處理器映射器(handler mapping
)(步驟2
)來確定請(qǐng)求所需要的控制器是哪一個(gè)(因?yàn)榭刂破鲿?huì)有很多)思杯,處理器映射器會(huì)根據(jù)請(qǐng)求所攜帶的URL
信息進(jìn)行決策胜蛉。一旦選擇了合適的控制器,
DispatcherServlet
會(huì)將請(qǐng)求發(fā)送給選中的控制器(步驟3
)色乾。到了控制器誊册,請(qǐng)求會(huì)卸下其負(fù)載(用戶 提交的信息)并耐心等待控制器處理這些信息。控制器在完成邏輯處理后暖璧,通常會(huì)產(chǎn)生一些信息案怯,這些信息需要返回給用戶并在瀏覽器上顯示。這些信息被稱為模型(
model
)澎办。不過嘲碱,僅僅給用戶返回原始的信息是不夠的——這些信息需要以用戶友好的方式進(jìn)行格式化,一般會(huì)是HTML
浮驳。所以悍汛,信息需要發(fā)送給一個(gè)視圖(view
)捞魁,通常是JSP
至会。控制器做的最后一件事就是將模型數(shù)據(jù)打包,并且標(biāo)識(shí)出用于渲染輸出的視圖名谱俭。然后發(fā)送回
DispatcherServlet
(步驟4
)奉件。而返回給
DispatcherServlet
的視圖名并不直接表示某個(gè)特定的JSP
,可能并不是JSP
昆著。相反县貌,它僅僅傳遞的是一個(gè)邏輯名稱,這個(gè)名稱用來查找產(chǎn)生結(jié)果的真正視圖凑懂。這就需要使用視圖解析器對(duì)邏輯視圖名進(jìn)行解析(步驟5
)煤痕。解析完之后就會(huì)找到相關(guān)視圖對(duì)數(shù)據(jù)進(jìn)行渲染(步驟
6
),在這里交付模型數(shù)據(jù),之后對(duì)用戶響應(yīng)(步驟7
)摆碉。
1.2 搭建Spring MVC
1.2.1 配置DispatcherServlet
按照傳統(tǒng)方式塘匣,像DispatcherServlet
這樣的Servlet
會(huì)配置在web.xml
文件中。但是借助Servlet3.1
和Spring3.1
的功能增強(qiáng)巷帝,這里使用Java
將DispatcherServlet
配置在Servlet
容器中忌卤。
package spittr.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import spittr.web.WebConfig;
public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
說明:
這里我們創(chuàng)建的應(yīng)用的名稱為
Spittr
。要理解上述代碼是如何工作的楞泼,可能只需要知道擴(kuò)展AbstractAnnotationConfigDispatcherServletInitializer
的任意類都會(huì)自動(dòng)地配置DispatcherServlet
和Spring
應(yīng)用上下文驰徊,Spring
的應(yīng)用上下文位于應(yīng)用程序的Servlet
上下文之中。AbstractAnnotationConfigDispatcherServletInitializer
剖析:
在Servlet3.0
環(huán)境中堕阔,容器會(huì)在類路徑中查找實(shí)現(xiàn)了javax.servlet.ServletContainerInitializer
接口的類棍厂,如果能發(fā)現(xiàn)的話,就用它來配置Servlet
容器印蔬。Spring
提供了這個(gè)接口的實(shí)現(xiàn)勋桶,名為SpringServletContainerInitializer
,這個(gè)類反過來會(huì)查找WebApplicationInitializer
的類并將配置的任務(wù)交給它們來完成侥猬。Spring3.2
引入了一個(gè)便利的WebApplicationInitializer
基礎(chǔ)實(shí)現(xiàn)例驹,即AbstractAnnotationConfigDispatcherServletInitializer
。因?yàn)槲覀兊?code>SpitterWebInitializer擴(kuò)展了AbstractAnnotationConfigDispatcherServletInitializer
(同時(shí)也就實(shí)現(xiàn)了WebApplicationInitializer
)退唠,因此部署到Servlet3.0
容器中的時(shí)候鹃锈,容器會(huì)自動(dòng)發(fā)現(xiàn)它,并用它來配置Servlet
上下文瞧预。這里的第一個(gè)方法
getServletMappings()
屎债,它會(huì)將一個(gè)或多個(gè)路徑映射到DispatcherServlet
上。本例中垢油,它映射的是“/”
盆驹,這表示它會(huì)是應(yīng)用的默認(rèn)Servlet
。會(huì)處理進(jìn)入應(yīng)用的所有請(qǐng)求滩愁。其他兩個(gè)方法在后面說明躯喇。注意:這里使用的是
Java
方式,和web.xml
方式不同在于硝枉,應(yīng)用啟動(dòng)時(shí)廉丽,容器會(huì)在類路徑下查找ServletContainerInitializer
的實(shí)現(xiàn)(SpringServletContainerInitializer
),此實(shí)現(xiàn)又查找WebApplicationInitializer
的實(shí)現(xiàn)(AbstractAnnotationConfigDispatcherServletInitializer
)妻味,這個(gè)類會(huì)創(chuàng)建DispatcherServlet
和ContextLoaderListener
正压,而這里應(yīng)用的配置類SpitterWebInitializer
繼承了AbstractAnnotationConfigDispatcherServletInitializer
。
1.2.2 兩個(gè)應(yīng)用上下文之間的故事
當(dāng)DispatcherServlet
啟動(dòng)的時(shí)候责球,會(huì)創(chuàng)建Spring
應(yīng)用上下文焦履,并加載配置文件或配置類中所聲明的bean
拓劝。在上述代碼中的getServletConfigClasses()
方法中,我們要求DispatcherServlet
加載應(yīng)用上下文時(shí)嘉裤,使用定義在WebConfig
配置類中的bean
(相關(guān)代碼在后面給出)凿将。
但是在Spring Web
應(yīng)用中,通常還會(huì)有另外一個(gè)應(yīng)用上下文价脾。這個(gè)上下文是由ContextLoaderListener
創(chuàng)建的牧抵。一般情況是,我們希望DispatcherServlet
加載包含Web
組件的bean
侨把,如控制器犀变、視圖解析器以及處理器映射器,而ContextLoaderListener
要加載應(yīng)用中其他的bean
秋柄。這些bean
通常是驅(qū)動(dòng)應(yīng)用后端的中間層和數(shù)據(jù)層組件(如IoC
容器)获枝。
實(shí)際上,AbstractAnnotationConfigDispatcherServletInitializer
會(huì)同時(shí)創(chuàng)建DispatcherServlet和ContextLoaderListener
骇笔。getServletConfigClasses()
方法返回的帶有@Configuration
注解的類將會(huì)用來定義DispatcherServlet
應(yīng)用上下文中的bean
省店。getRootConfgiClasses()
方法返回的帶有@Configuration
注解的類將會(huì)用來配置ContextLoaderListener
創(chuàng)建應(yīng)用上下文中的bean
。
當(dāng)然我們也可以使用傳統(tǒng)的web.xml
配置笨触,但是其實(shí)沒有必要辖试,而這種配置方式下的應(yīng)用必須部署到支持Servlet3.0
的服務(wù)器中才能正常工作苍在。
1.2.3 啟用Spring MVC
有多種方式配置DispatcherServlet
,于是,啟用Spring MVC
組件的方式也不僅一種收班≡笕可以使用<mvc:annotation-driven>
啟用注解驅(qū)動(dòng)的Spring MVC
(在后面說明)邀摆,這里使用Java
進(jìn)行配置怠肋。
package spittr.web;
import org.springframework.context.annotation.*;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc//啟用Spring MVC
@ComponentScan("spittr.web")//啟用組件掃描
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public ViewResolver viewResolver() {
//配置JSP視圖解析器
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
// 配置靜態(tài)資源的處理
configurer.enable();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 在后面進(jìn)行說明
super.addResourceHandlers(registry);
}
}
說明:
這里啟用了組件掃描,后面帶有
@Controller
注解的控制器會(huì)稱為掃描時(shí)的候選bean
串慰。因此偏塞,我們不需要在配置類中顯式的聲明任何控制器。接下來邦鲫,我們添加了一個(gè)
ViewResolver bean
灸叼。更具體來講InternalResourceViewResolver
。在后面會(huì)更為詳細(xì)的討論視圖解析器掂碱。在查找的時(shí)候怜姿,它會(huì)在視圖名稱上加一個(gè)特定的前綴和后綴(例如慎冤,名為home
的視圖將會(huì)解析為/WEB-INF/views/home.jsp
)疼燥。最后,
WebConfig
類還擴(kuò)展了WebMvcConfigurerAdapter
并重寫了其configureDefaultServletHandling()
方法蚁堤。通過調(diào)用enable()
方法醉者,我們要求DispatcherServlet
將對(duì)靜態(tài)資源的請(qǐng)求轉(zhuǎn)發(fā)到Servlet
容器中默認(rèn)的Servlet
上但狭,而不是使用DispatcherServlet
本身來處理此類請(qǐng)求。下面給出
RootConfig
:
package spittr.config;
import java.util.regex.Pattern;
import org.springframework.context.annotation.*;
import org.springframework.core.type.filter.RegexPatternTypeFilter;
import spittr.config.RootConfig.WebPackage;
@Configuration
@ComponentScan(basePackages={"spittr"},
excludeFilters={
@Filter(type=FilterType.ANNOTATION, value=EnableWebMvc.class)
})
public class RootConfig {
}
說明:這里我們使用了自動(dòng)掃描配置撬即,在后面可以使用非Web
組件來完善RootConfig
立磁。這里的因?yàn)榍懊?code>@EnableWebMvc已經(jīng)被加載了,這里使用@excludeFilters
將其排除掉剥槐。
其實(shí)ContextLoaderListener
會(huì)根據(jù)根配置文件RootConfig
會(huì)加載相關(guān)bean
唱歧,而上述配置中將EnableWebMvc
類排除了,其實(shí)還應(yīng)該將spittr.web
包中的類排除(修改后的代碼后面給出)粒竖,因?yàn)槟莻€(gè)包中的類是由DispatcherServlet
來加載的颅崩,這樣兩者就不會(huì)產(chǎn)生重復(fù)的bean
了,如果產(chǎn)生了重復(fù)蕊苗,則優(yōu)先使用DispatcherServlet
返回的bean
沿后,而ContextLoaderListener
產(chǎn)生的bean
無法被調(diào)用,稱為內(nèi)存泄漏朽砰。
@Configuration
@Import(DataConfig.class)
@ComponentScan(basePackages={"spittr"},
excludeFilters={
@Filter(type=FilterType.CUSTOM, value=WebPackage.class)
})
public class RootConfig {
public static class WebPackage extends RegexPatternTypeFilter {
public WebPackage() {
super(Pattern.compile("spittr\\.web"));
}
}
}
1.3 Spittr簡(jiǎn)介
這里Spittr
表示一個(gè)類似微博的應(yīng)用尖滚,其中Spitter
用于表示應(yīng)用的用戶,而Spittle
表示用戶發(fā)布的簡(jiǎn)短狀態(tài)更新瞧柔。
二漆弄、編寫基本的控制器
在Spring MVC
中,控制器只是方法上添加了@RequestMapping
注解的類造锅,這個(gè)注解聲明了它們所要處理的請(qǐng)求置逻。這里控制器盡可能簡(jiǎn)單,假設(shè)控制器類要處理對(duì)“/”
的請(qǐng)求备绽,并渲染應(yīng)用的首頁(yè)券坞。
package spittr.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(){
return "home";
}
}
說明:
很顯然,這個(gè)類帶有
@Controller
注解肺素,表示這個(gè)類是一個(gè)控制器恨锚。@Controller
是一個(gè)構(gòu)造型(stereotype
)的注解,基于@Component
注解倍靡。在這里猴伶,它的目的就是輔助實(shí)現(xiàn)組件掃描。因?yàn)檫@個(gè)類帶有@Controller
注解塌西,因此組件掃描的時(shí)候會(huì)自動(dòng)找到此類他挎,并將其聲明為一個(gè)bean
。HomeController
有一個(gè)方法home()
方法捡需,帶有@RequestMapping
注解办桨。它的value
屬性指定了這個(gè)方法所要處理的請(qǐng)求路徑,method
屬性細(xì)化了它所處理的HTTP
方法站辉。在本例中呢撞,當(dāng)收到對(duì)“/”
的HTTP GET
請(qǐng)求時(shí)损姜,就會(huì)調(diào)用此方法處理。這里
home()
方法返回的是一個(gè)視圖的邏輯名殊霞,之后DispatcherServlet
會(huì)調(diào)用視圖解析器對(duì)其進(jìn)行解析摧阅,找到真正的視圖(/WEB-INF/views/home.jsp
)。
2.1 測(cè)試控制器
以前我們經(jīng)常使用單元測(cè)試對(duì)某個(gè)方法進(jìn)行測(cè)試绷蹲,但是對(duì)于這種Web
應(yīng)用來說棒卷,測(cè)試總是要啟動(dòng)應(yīng)用和服務(wù)器,這是較為麻煩的祝钢。從Spring3.2
開始娇跟,可以按照控制器的方式來測(cè)試控制器,而不僅僅將控制器作為一個(gè)POJO
來測(cè)試太颤。Spring
現(xiàn)在包含了一種mock Spring MVC
并針對(duì)控制器執(zhí)行HTTP
請(qǐng)求的機(jī)制苞俘。這樣就不需要啟動(dòng)應(yīng)用和服務(wù)器了。
package spittr.web;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
public class HomeControllerTest {
@Test
public void testHomePage() throws Exception{
HomeController controller = new HomeController();
//初始化MockMvc對(duì)象
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.view().name("home"));
}
}
說明:這里首先使用真實(shí)控制器對(duì)象初始化MockMvc
對(duì)象龄章,然后使用perform()
方法發(fā)起GET
請(qǐng)求吃谣,然后使用view()
方法檢查返回的結(jié)果是否是“name”
(也就是檢查返回的邏輯視圖名是否是“name”
)。
2.2 定義類級(jí)別的請(qǐng)求處理
之前我們是將請(qǐng)求定義在方法級(jí)別上(在方法上使用@RequestMapping
注解)做裙,下面將請(qǐng)求定義在類級(jí)別上岗憋。
package spittr.web;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/")
public class HomeController {
@RequestMapping(method = RequestMethod.GET)
public String home(Model model) {
return "home";
}
}
說明:
這里在控制器中,路徑現(xiàn)在被轉(zhuǎn)移到類級(jí)別的
@RequestMapping
上锚贱,而HTTP
方法依然映射在方法級(jí)別上仔戈,此時(shí)這個(gè)注解會(huì)應(yīng)用到控制器的所有處理器方法上。當(dāng)然我們可以同時(shí)在方法上使用@RequestMapping
注解拧廊,此時(shí)监徘,方法級(jí)別上的注解就是對(duì)類上注解的一種補(bǔ)充,這里兩個(gè)注解合并之后(沒有方法級(jí)別上的注解)標(biāo)明home()
方法將會(huì)處理對(duì)“/”
路徑的GET
請(qǐng)求吧碾。這里注意凰盔,
@RequestMapping
的value
屬性能夠接收一個(gè)String
類型的數(shù)組。
@Controller
@RequestMapping({"/", "/homepage"})
public class HomeController {
...
}
此時(shí)倦春,控制器的home()
方法能夠映射到對(duì)“/”
和“/homepage”
的GET
請(qǐng)求户敬。
2.3 傳遞模型數(shù)據(jù)到視圖中
大多數(shù)控制器可能并不像上面那樣簡(jiǎn)單,在Spittr
應(yīng)用中睁本,我們需要一個(gè)頁(yè)面展現(xiàn)最近提交的Spittle
列表尿庐。因此,我們需要一個(gè)新的方法來處理這個(gè)頁(yè)面呢堰。
為了避免對(duì)數(shù)據(jù)庫(kù)進(jìn)行訪問抄瑟,這里定義一個(gè)數(shù)據(jù)訪問的Repository
接口,稍后實(shí)現(xiàn)它暮胧。
package spittr.data;
import java.util.List;
import spittr.Spittle;
public interface SpittleRepository {
List<Spittle> findSpittles(long max, int count);
}
說明:findSpittles()
方法接受兩個(gè)參數(shù)锐借。其中max
參數(shù)代表所返回的Spittle
中,Spittle ID
屬性的最大值往衷,而count
參數(shù)表明要返回多少個(gè)Spittle
對(duì)象钞翔。為了獲得最新的20
個(gè)Spittle
對(duì)象,可以這樣使用:
List<Spittle> recent = spittleRepository.findSpittles(Long.MAX_VALUE, 20);
下面給出Spittle
對(duì)象:
package spittr;
import java.util.Date;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Spittle {
private final Long id;
private final String message;
private final Date time;
private Double latitude;//經(jīng)度
private Double longitude;//緯度
public Spittle(String message, Date time) {
this(null, message, time, null, null);
}
public Spittle(Long id, String message, Date time, Double longitude, Double latitude) {
this.id = id;
this.message = message;
this.time = time;
this.longitude = longitude;
this.latitude = latitude;
}
public long getId() {
return id;
}
public String getMessage() {
return message;
}
public Date getTime() {
return time;
}
public Double getLongitude() {
return longitude;
}
public Double getLatitude() {
return latitude;
}
@Override
public boolean equals(Object that) {
return EqualsBuilder.reflectionEquals(this, that, "id", "time");
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this, "id", "time");
}
}
說明:這里唯一要注意的是使用了Apache Common Lang
包實(shí)現(xiàn)equals()
和hashCode()
方法席舍。下面對(duì)新的控制器進(jìn)行測(cè)試布轿。
@Test
public void houldShowRecentSpittles() throws Exception {
List<Spittle> expectedSpittles = createSpittleList(20);
SpittleRepository mockRepository = Mockito.mock(SpittleRepository.class);
Mockito.when(mockRepository.findSpittles(Long.MAX_VALUE, 20))
.thenReturn(expectedSpittles);
SpittleController controller = new SpittleController(mockRepository);
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setSingleView(new InternalResourceView("/WEB-INF/views/spittles.jsp"))
.build();
mockMvc.perform(MockMvcRequestBuilders.get("/spittles"))
.andExpect(MockMvcResultMatchers.view().name("spittles"))
.andExpect(MockMvcResultMatchers.model().attributeExists("spittleList"))
.andExpect(MockMvcResultMatchers.model().attribute("spittleList",
Matchers.hasItems(expectedSpittles.toArray())));
}
說明:這里需要導(dǎo)入mockito-all-2.0.2-beta.jar
和hamcrest-all-1.3.jar
兩個(gè)包。這里先是使用SpittleRepository
接口創(chuàng)建了一個(gè)mock
實(shí)例来颤,之后的when(XXX).thenReturn(XXX)
其實(shí)是一種配置汰扭,即調(diào)用某個(gè)方法應(yīng)該返回什么值。然后使用mock
對(duì)象構(gòu)造一個(gè)控制器福铅,而這里在MockMvc
構(gòu)造器上調(diào)用setSingleView()
萝毛。這樣的話,mock
框架就不用解析控制器中的試圖名了滑黔。在很多場(chǎng)景中笆包,其實(shí)沒有必要這樣做。但是對(duì)于這個(gè)控制器方法略荡,試圖名與請(qǐng)求路徑非常相似庵佣,這樣如果按照默認(rèn)的視圖解析規(guī)則,MockMvc
就會(huì)發(fā)生失敗汛兜,因?yàn)闊o法區(qū)分視圖路徑和控制器路徑巴粪。之后使用perform()
方法發(fā)起GET
請(qǐng)求,同時(shí)對(duì)視圖和模型都設(shè)置了一些條件粥谬,即首先斷言試圖名為“spittles”
肛根,同時(shí)斷言模型中有key
為“spittleList”
的對(duì)象同時(shí)對(duì)象中包含相關(guān)預(yù)期內(nèi)容。在運(yùn)行測(cè)試之前還需要給出相關(guān)控制器:
package spittr.web;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import spittr.web.Spittle;
import spittr.web.SpittleRepository;
@Controller
@RequestMapping("/spittles")
public class SpittleController {
private static final String MAX_LONG_AS_STRING = "9223372036854775807";
private SpittleRepository spittleRepository;
@Autowired
public SpittleController(SpittleRepository spittleRepository) {
this.spittleRepository = spittleRepository;
}
@RequestMapping(method=RequestMethod.GET)
public String spittles(Model model) {
model.addAttribute(spittleRepository.findSpittles(Long.MAX_VALUE, 20));
return "spittles";
}
}
說明:
這里在構(gòu)造器中注入
spittleRepository
漏策。需要注意的是晶通,在spittles()
方法中定義了一個(gè)Model
作為參數(shù)。這樣哟玷,此方法就能將Repository
中獲取到的Spittle
列表填充到模型中狮辽。Model
實(shí)際上就是一個(gè)Map
,它會(huì)傳遞給視圖巢寡,這樣數(shù)據(jù)就能渲染到客戶端了喉脖。當(dāng)調(diào)用addAttribute()
方法并且不指定key
的時(shí)候,那么key
會(huì)根據(jù)值的對(duì)象類型推斷抑月。在本例中树叽,因?yàn)樗且粋€(gè)List<Spittle>
,因此谦絮,key
推斷為spittleList
题诵。最后返回視圖名spittles
洁仗。當(dāng)然在使用
addAttribute()
方法的時(shí)候也可以指定key
:
@RequestMapping(method=RequestMethod.GET)
public String spittles(Model model) {
model.addAttribute("spittleList", spittleRepository.findSpittles(Long.MAX_VALUE, 20));
return "spittles";
}
- 如果你希望使用非
Spring
類型的話,那么可以使用Map
來代替Model
:
@RequestMapping(method=RequestMethod.GET)
public String spittles(Map model) {
model.put("spittleList", spittleRepository.findSpittles(Long.MAX_VALUE, 20));
return "spittles";
}
- 還有另一種方式來編寫
spittles()
方法:
@RequestMapping(method=RequestMethod.GET)
public List<Spittle> spittles(
return spittleRepository.findSpittles(Long.MAX_VALUE, 20);
}
當(dāng)處理器方法像這樣返回對(duì)象或集合時(shí)性锭,這個(gè)返回值會(huì)放到模型中赠潦,模型key
會(huì)根據(jù)其類型推斷得出(此處為spittleList
),而邏輯視圖名會(huì)根據(jù)請(qǐng)求路徑推斷出草冈,因?yàn)檫@個(gè)方法處理針對(duì)“/spittles”
她奥,于是邏輯視圖名為spittles
。下面給出/WEB-INF/views/spittles.jsp
:
<c:forEach items="${spittleList}" var="spittle" >
<li id="spittle_<c:out value="spittle.id"/>">
<div class="spittleMessage"><c:out value="${spittle.message}" /></div>
<div>
<span class="spittleTime"><c:out value="${spittle.time}" /></span>
<span class="spittleLocation">(<c:out value="${spittle.latitude}" />,
<c:out value="${spittle.longitude}" />)</span>
</div>
</li>
此處使用了一些JSTL
表達(dá)式怎棱。