Spring MVC(Model-View-Controller)css
當你看到本博文時,我猜你可能正面臨着我已探索過的問題。html
同其餘博主同樣,我先按照書上詳細的介紹一下 Spring MVC,也是爲了本身能理解的更深入一些。前端
1、跟蹤 Spring MVC 的請求java
每當咱們碰到各類問題時,咱們老是會向瀏覽器求助。git
正如你在百度裏查找資料同樣,搜索一下問題,點擊一個連接,再查看裏面的內容。web
一、在請求離開瀏覽器時,總會帶有你發出請求的信息,好比你可能剛剛在搜 Spring MVC,這就是信息。正則表達式
二、請求的第一站是前端控制器,在 Spring MVC 中,它是 DispatcherServlet。spring
它的做用是把請求發送到合適的 Spring MVC 控制器。apache
由於有各類各樣的請求須要控制器去處理,因此前端控制器是把請求發送到一個合適的控制器中。數組
三、用戶發送了一個請求,固然想看的是結果了。因此控制器處理完請求後將會返回一些信息以便能讓用戶看見。
這些但願被用戶看見的信息稱爲模型(Model)。
固然用戶確定是想看的舒服一點,因此這些信息須要發送到視圖(View)時顯示,一般是 JSP。
四、控制器的事情還沒完,它的實際返回值是一個視圖名,將視圖名和模型數據傳遞給前端控制器(DispatcherServlet)。
五、既然知道由哪一個視圖顯示用戶但願看到的結果,那麼請求的最後一站也就是視圖的實現了。
2、搭建 Spring MVC
既然原理已經講清楚了,那麼接下來看個例子才能更好理解!
咱們第一步也就是來配置上面所說到的東西,按照傳統方式,一般是使用 web.xml 來配置的。
可是做者說的使用 JavaConfig 更好一些。那咱們能夠對比一下這兩種方式。
一、配置 DispatcherServlet
package spittr.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class SpittrWebAppInitializer 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[] { "/" }; } }
正如書本提到的,擴展 AbstractAnnotationConfigDispatcherServletInitializer 類的任意類---
都會自動配置 DispatcherServlet 和 Spring 應用上下文。
至於怎麼配置能夠看到下面重寫的三個函數。
函數 getServletMappings 會處理全部的請求,也就是用戶發出的請求都交由 SpringMVC 來處理。
函數 getRootConfigClasses 會根據 RootConfig 類來配置 ContextLoaderListener 建立的應用上下文的 bean。
函數 getServletConfigClasses 會根據 WebConfig 類來配置 DispatcherServlet 應用上下文的 bean。
另外配置類都要帶有 @Configuration 註解,爲了能讓組件掃描知道這是配置類。
二、接下來看下這兩個類的內容
package spittr.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @EnableWebMvc @ComponentScan("spittr.web") public class WebConfig { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); resolver.setExposeContextBeansAsAttributes(true); return resolver; } }
該類的 Configuration 須要導入 org.springframework.context.annotation.Configuration;
@EnableWebMvc 說明須要啓用 SpringMVC,
須要導入 org.springframework.web.servlet.config.annotation.EnableWebMvc;
@ComponentScan 註解說明啓用組件掃描,掃描的包爲 spittr.web,
須要導入 org.springframework.context.annotation.ComponentScan;
書中說的處理靜態資源可能不太好,須要繼承 WebMvcConfigureAdapter 並重寫 configureDefaultServletHanding 方法。
目前我還不太清楚哪裏很差,因此這裏把它刪掉。
在這裏能夠看到有個視圖解析器,它的做用是給控制器返回的視圖名加個前綴和後綴,以便知道視圖文件在哪裏。
在該函數前面加了個@bean 註解,是爲了將起加入到 spring 容器中去,否則該視圖解析器不會生效。
三、接下來看下 RootConfig 的內容
package spittr.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @Configuration @ComponentScan(basePackages = { "spittr" }, excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class) }) public class RootConfig { }
在這裏我詳細的說一下@ComponentScan的做用:
在配置類上配置 @ComponentScan 註明掃描的包名,該包及其子包下的類中,
只要代碼中的類標註了 @Controller、@Service、@Repository、@Component 都會將其加載到 spring容器中。
固然 @bean 是顯示的聲明,能夠直接加載到 spring容器中。
basePackages 表面組件掃描的包是那個,從Packages中能夠看出,該屬性的值能夠是一個數組。
excludeFilters 指定掃描的時候須要排除的組件,includeFilters 指定掃描的時候只包含的組件,
useDefaultFilters 是否開啓默認掃描規則。
例如:@ComponentScan(basePackages = "spittr",includeFilters = {},excludeFilters = {},useDefaultFilters = true)
excludeFilters 和 includeFilters 都是 Filter 數組 ,Filter中咱們須要指定類型,分別爲:
一、ANNOTATION 註解類型
二、ASSIGNABLE_TYPE 指定類型
三、ASPECTJ ASPECTJ 表達式
四、REGEX 正則表達式
五、CUSTOM 自定義
書上的例子是過濾 spittr 包中的註解類型的組件,後面跟的值我猜多是過濾那個類裏面的註解,使其不被加載的 Spring 容器中。
3、編寫基本控制器
一、HomeController
package spittr.web; import static 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("/home") public class HomeController { @RequestMapping(method = GET) public String home(Model model) { return "home"; } }
該控制器會處理 "/home" 的 GET 請求,返回的視圖名爲 home。
咱們來看下 Spittr 的首頁 index.jsp 以及 home.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Spittr</title> <link rel="stylesheet" type="text/css" href="/resources/styles.css"> </head> <body> <h1>Welcome to Spittr</h1> <a href="<c:url value="/home"/>">Home</a> | <a href="<c:url value="/spittles"/>">Spittles</a> | <a href="<c:url value="/spitter/register"/>">Register</a> </body> </html>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> Welcome to you! </body> </html>
當咱們運行 index.jsp 後點擊 Home 就會顯示 Home 頁面。
4、傳遞模型數據到視圖中
在咱們的Home 控制器中並無把數據返回到視圖中,僅僅只返回了一個視圖名,能夠說是一個超級簡單的控制器了。
如今讓咱們給視圖加點數據進去。
咱們先定義一個 Spittle 類:
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; private Double longitude; public Spittle(String message, Date time) { this(message, time, null, null); } public Spittle(String message, Date time, Double latitude, Double longitude) { this.id = null; this.message = message; this.time = time; this.latitude = latitude; this.longitude = longitude; } 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"); } }
而後假設咱們的數據都在一個倉庫裏:
定義一個接口 SpittleRepository:
package spittr.data; import java.util.List; import org.springframework.stereotype.Component; import spittr.Spittle; @Component public interface SpittleRepository { List<Spittle> findSpittles(long max, int count); }
再實現它:
package spittr.data; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.springframework.stereotype.Repository; import spittr.Spittle; @Repository public class JdbcSpittleRepository implements SpittleRepository { private List<Spittle> createSpittleList(int count) { List<Spittle> spittles = new ArrayList<Spittle>(); for (int i = 0; i < count; i++) { spittles.add(new Spittle("Spittle" + i, new Date())); } return spittles; } @Override public List<Spittle> findSpittles(long max, int count) { // TODO Auto-generated method stub return createSpittleList(count); } }
如今咱們的倉庫實現了,能夠生成 Spittle 數據。而後咱們編寫一個 Spittle 控制器:來把數據放到視圖中。
package spittr.web; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import spittr.data.SpittleRepository; @Controller public class SpittleController { private SpittleRepository spittleRepository; @Autowired public SpittleController(SpittleRepository spittleRepository) { // 注入SpittleRepository this.setSpittleRepository(spittleRepository); } @RequestMapping(value="/spittles",method = RequestMethod.GET) public String spittles(Model model) { // 將spittle添加到模型中 model.addAttribute(spittleRepository.findSpittles(Long.MAX_VALUE, 20)); return "spittles"; // 返回視圖名 } public SpittleRepository getSpittleRepository() { return spittleRepository; } public void setSpittleRepository(SpittleRepository spittleRepository) { this.spittleRepository = spittleRepository; } }
該控制器中的 @Autowired 註解會在 Spring容器中尋找有 @Repository 註解的bean,而後將該 bean 注入進去。
也就是在 Spring 容器中 尋找適當的 bean 注入到參數中去。
在控制器處理方法上,也就是帶有 @RequestMapping 註解的方法的參數有個 Model 也就是模型,
控制器能夠經過該參數將數據傳遞到視圖上去。
如今咱們看下 spittles.jsp 視圖:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="s" uri="http://www.springframework.org/tags"%> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Spitter</title> <link rel="stylesheet" type="text/css" href="<c:url value="../resources/style.css" />" > </head> <div class="listTitle"> <h1>Recent Spittles</h1> <ul class="spittleList"> <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> </div> </li> </c:forEach> </ul> </div> </body> </html>
最後讓咱們來看下效果:
若是是用 web.xml 來配置的話:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>Java_Web</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>SpringDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/SpringMVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/AppContext.xml</param-value> </context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> </web-app>
另外說明一下,若是是 SpringMVC.xml 文件放在src裏的話:<param-value>classpath:SpringMVC.xml<param-value>。
同理:AppContext.xml也是同樣。
接下來咱們看下SpringMVC.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd"> <context:component-scan base-package="spittr"></context:component-scan> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
該文件配置的是視圖解析器,講解了 javaConfig,這裏就很少說了。
AppContext.xml 文件暫未配置啥,是個空文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd"> </beans>
上面的包並無單元測試和 mock 模擬測試包。
若是須要項目及相關包能夠點此下載:Download