《spring 實戰 第四版》第六章 渲染web視圖

電子書以及項目完整源代碼

請戳我

視圖解析

ViewResolver 和 View

  • 視圖解析器ViewResolver負責處理視圖名與實際視圖之間的映射關係。 當給resolveViewName()方法傳入一個視圖名和Locale對象時,它會返回一個View實例.
public interface ViewResolver {
    View resolveViewName(String var1, Locale var2) throws Exception;
}
  • 視圖接口View負責準備請求,並將請求的渲染交給某種具體的視圖技術實現。接受模型以及Servlet的request和response對象,並將輸出結果渲染到response中。
public interface View {
    String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
    String PATH_VARIABLES = View.class.getName() + ".pathVariables";
    String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";

    String getContentType();

    void render(Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;
}

視圖解析器

  • BeanNameViewResolver
    將視圖解析爲Spring應用上下文中的bean,其中
    bean的ID與視圖的名字相同
  • ContentNegotiatingViewResolver
    通過考慮客戶端需要的內容類型來解析視圖,
    委託給另外一個能夠產生對應內容類型的視圖
    解析器
  • FreeMarkerViewResolver 將視圖解析爲FreeMarker模板
  • InternalResourceViewResolver
    將視圖解析爲Web應用的內部資源(一般爲
    JSP)
  • JasperReportsViewResolver 將視圖解析爲JasperReports定義
  • ResourceBundleViewResolver 將視圖解析爲資源bundle(一般爲屬性文件)
  • TilesViewResolver
    將視圖解析爲Apache Tile定義,其中tile ID與視
    圖名稱相同。注意有兩個不同的TilesViewResolver實現,分別對應於Tiles 2.0和
    Tiles 3.0
  • UrlBasedViewResolver
    直接根據視圖的名稱解析視圖,視圖的名稱會
    匹配一個物理視圖的定義
  • VelocityLayoutViewResolver
    將視圖解析爲Velocity佈局,從不同的Velocity模
    板中組合頁面
  • VelocityViewResolver 將視圖解析爲Velocity模板
  • XmlViewResolver
    將視圖解析爲特定XML文件中的bean定義。類
    似於BeanName-ViewResolver
  • XsltViewResolver 將視圖解析爲XSLT轉換後的結果

Jsp 視圖

兩種方式

  • 使用JSP標準標籤庫
    (JavaServer Pages Standard Tag Library,JSTL),InternalResourceViewResolver能夠將視圖名解析爲
    JstlView形式的JSP文件,從而將JSTL本地化和資源bundle變量暴
    露給JSTL的格式化(formatting)和信息(message)標籤。
  • 使用spring 提供的兩個JSP標籤庫,一個用於表單到模型的綁定,另一
    個提供了通用的工具類特性。

jsp 視圖解析器的配置

InternalResourceViewResolver 主要是採用拼接的方式,將視圖名稱拼接到配置的字符串,即添加前綴和後綴,從而確認Web應用中視圖資源的物理路徑。

  • java 配置方式啓用mvc 和 定製配置
@Configuration
@EnableWebMvc //啓用Spring MVC
@ComponentScan("com.zexing.spittr.web") //啓用組件掃描
public class WebConfig extends WebMvcConfigurerAdapter {

  @Bean
  public ViewResolver viewResolver() {    //配置視圖解析器
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    return resolver;
  }
  
  //```
  
  }
  • xml 配置方式啓用mvc 和 定製配置
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc"
  xsi:schemaLocation="http://www.springframework.org/schema/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context.xsd">

  <!-- 開啓註解驅動的spring mvc-->
  <mvc:annotation-driven /> 

  <context:component-scan base-package="com.zexing.spittr" />

  <beans:bean
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
  </beans:bean>
  
</beans:beans>

以上配置解析的是InternalResourceView視圖


  • 配置 Jstl 視圖

JSTL的格式化標籤需要一個Locale對象,以便於恰當地格式化地域
相關的值,如日期和貨幣。信息標籤可以藉助Spring的信息資源和
Locale,從而選擇適當的信息渲染到HTML之中。通過解析
JstlView,JSTL能夠獲得Locale對象以及Spring中配置的信息資
源。

設置它的viewClass屬性,即加上以下代碼

resolver.setViewClass(JstlView.class);

而 xml 加上

 <beans:property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />

spring 的JSP庫

  1. 綁定模型數據的表單標籤

在JSP頁面聲明標籤庫

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>
  • <sf:checkbox>
    渲染成一個HTML <input>標籤,其中type屬性設置
    爲checkbox
  • <sf:checkboxes>
    渲染成多個HTML <input>標籤,其中type屬性設置
    爲checkbox
  • <sf:errors> 在一個HTML <span>中渲染輸入域的錯誤
  • <sf:form> 渲染成一個HTML <form>標籤,併爲其內部標籤暴露綁定路
    徑,用於數據綁定
  • <sf:hidden> 渲染成一個HTML <input>標籤,其中type屬性設置爲hidden
  • <sf:input> 渲染成一個HTML <input>標籤,其中type屬性設置爲text
  • <sf:label> 渲染成一個HTML <label>標籤
  • <sf:option> 渲染成一個HTML <option> 標籤,其selected屬性根據所綁定的值進行設置
  • <sf:options> 按照綁定的集合、數組或Map,渲染成一個HTML <option>標
    籤的列表
  • <sf:password>
    渲染成一個HTML <input>標籤,其中type屬性設置
    爲password
  • <sf:radiobutton> 渲染成一個HTML <input>標籤,其中type屬性設置爲radio
  • <sf:select> 渲染爲一個HTML <select>標籤
  • <sf:textarea> 渲染爲一個HTML <textarea>標籤

就Spitter樣例,用到註冊表單

<sf:form method="POST" comandName="spitter">
  First Name: <sf:input path="firstName" /><br/>
  Last Name: <sf:input path="lastName" /><br/>
  Email: <sf:input path="email" /><br/>
  Username: <sf:input path="username" /><br/>
  Password: <sf:password path="password" /><br/>
  <sf:input type="submit" value="Register" />
</sf:form>

其中sf:form會渲染一個HTMl 標籤,同時通過commandName屬性構建針對某個模型對象的上下文信息。
因此在模型中必須要有一個key爲Spitter對象,否則的話,表單不能正常渲染(會出現JSP錯誤).這意味着我們需要修改SpitterController,以確保模型中存在以Spitter爲key的Spitter對象。

@RequestMapping(value = "/register",method = RequestMethod.GET)
    public String showRegistrationForm(Model model){
        model.addAttribute(new Spitter());//對應registerForm.jsp中 <sf:form> 的commandName屬性值
        return "registerForm";
    }

我們在這裏設置了path屬性,< input>標籤的value屬性值將會設置爲spitter對象中path屬性所對應的值。

例如在模型中Spitter對象中firstName屬性值爲guo,那麼<sf:input path=“firstname”/>所渲染的< input>標籤中,會存在value=“guo”。

當用戶註冊失敗後,返回表單將預設先前用戶的填寫內容


從Spring 3.1開始,<sf:input>標籤能夠允許我們指
定type屬性,即還能指定HTML 5
特定類型的文本域,如date、range和email。例如,我們可以按
照如下的方式指定email域:

Email: <sf:input path="email" type="email"/> <br/>
  1. 向用戶展現後臺校驗的錯誤信息

如果存在校驗錯誤的話,請求中會包含錯誤的詳細信息,這些信息是
與模型數據放到一起的。我們所需要做的就是到模型中將這些數據抽
取出來,並展現給用戶

<sf:errors path="firstName"/>

path屬性指定了將要顯示模型對象的哪個屬性的錯誤信息,當校驗錯誤,將會在一個HTML 標籤中顯示錯誤信息,否則不渲染任何內容。

如以上校驗失敗時渲染的內容是:

<span id="firstName.errors">size must be between 2 and 30</span>
  1. 用property文件定義錯誤信息

在校驗註解上設置message屬性,使其引
用對用戶更爲友好的信息。(PS:沒有使用{}將直接顯示)

Spitter.java

    @NotNull
    @Size(min=5, max=16, message="{username.size}")
    private String username;

ValidationMessages.properties

firstName.size=First name must be between {min} and {max} characters long.

{min}和{max}會引用@Size註解上所設
置的min和max屬性。

Spring 的通用標籤庫

聲明標籤庫

<%@ taglib uri="http://www.springframework.org.tags" prefix='s'%>

幾種標籤說明

  • <s:escapeBody> 將標籤體中的內容進行HTML和/或JavaScript轉義
  • <s:htmlEscape> 爲當前頁面設置默認的HTML轉義值
  • <s:message>
    根據給定的編碼獲取信息,然後要麼進行渲染(默認行
    爲),要麼將其設置爲頁面作用域、請求作用域、會話作用
    域或應用作用域的變量(通過使用var和scope屬性實現)
  • <s:url>
    創建相對於上下文的URL,支持URI模板變量以及
    HTML/XML/JavaScript轉義。可以渲染URL(默認行爲),也
    可以將其設置爲頁面作用域、請求作用域、會話作用域或應
    用作用域的變量(通過使用var和scope屬性實現)

  1. 使用 <s:message> 對展示信息進行國際化處理
  • 修改代碼
<h1><s:message code="spittr.welcome" /></h1>
  • 配置信息源
@Bean
  public MessageSource messageSource () {   //配置信息源
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setBasename("classpath:messages");
    messageSource.setCacheSeconds(10);
    return messageSource;
  }

basename屬性可以設置爲在類路徑下(以“classpath:”作
爲前綴)、文件系統中(以“file:”作爲前綴)或Web應用的根路徑
下(沒有前綴)查找屬性

  • 創建property文件

messages.properties

spittr.welcome=Welcome to Spitter!

2.使用<s:url> 創建URL

  • 簡單使用
 <a href=" <s:url value="/spitter/register" />">register</a>

<s:url> 會接受一個相對於Servlet上下文的URL,並在渲染的時候,在URL 後面拼接上Servlet上下文路徑,即當應用的Servlet上下文名爲spittr,渲染後的
標籤內容是

<a href="/spittr/spitter/register">register</a>
  • 將 URL 賦值變量
<s:url value="/spitter/register" var="registerUrl" />

 
<a href=" ${registerUrl}">register</a>
  • 添加作用域屬性
<s:url value="/spitter/register" var="registerUrl" scope="page"/>
  • URL上添加參數
<s:url value="/spittles" var="spittlesUrl">
        <s:param name="max" value="60"/>
        <s:param name="count" value="20" />
</s:url>
<a href="${spittlesUrl}">Spittles</a> 

跳轉的URl將會拼接參數:

http://localhost:8080/spittles?max=60&count=20
  • 渲染URL內容(需要去除 var 屬性)

設置屬性 htmlEscape 爲true

<s:url value="/spittles"  htmlEscape="true">
        <s:param name="max" value="60"/>
        <s:param name="count" value="20" />
</s:url>

將在頁面直接顯示URL的內容

/spittles?max=60&count=20

在javascript裏面使用

設置屬性 javaScriptEscape 爲true


    <s:url value="/spittles" var="jsUrl" javaScriptEscape="true">
      <s:param name="max" value="60"/>
      <s:param name="count" value="20" />
    </s:url>

    <script>
      var spittlesUrl = ${jsUrl};
    </script>

【這部分尚未完成】使用Apache Tiles視圖定義佈局

爲所有頁面定義通用頁面佈局模板

導入相關jar包

參考鏈接

需導入Tiles相關的jar包(缺少包可能導致的Exception):

  • tiles-api-3.0.8.jar
  • tiles-core-3.0.8.jar
  • tiles-jsp-3.0.8.jar
  • tiles-servlet-3.0.8.jar
  • tiles-template-3.0.8.jar
  • slf4j-api-1.7.25 (java.lang.ClassNotFoundException: org.slf4j.LoggerFactory)
  • commons-digester-2.1 (java.lang.ClassNotFoundException: org.apache.commons.digester.Rule)
  • commons-beanutils-1.9.3 (ClassNotFoundException: org.apache.commons.beanutils.MethodUtils)
  • tiles-request-api-1.0.7.(java.lang.NoClassDefFoundError: org/apache/tiles/request/render/Renderer)
  • tiles-request-servlet-1.0.7.(ClassNotFoundException: org.apache.tiles.request.servlet.ServletApplicationContext)
  • tiles-request-jsp-1.0.7 (ClassNotFoundException: org.apache.tiles.request.jsp.autotag.JspAutotagRuntime)
  • tiles-autotag-core-runtime-1.2 (ClassNotFoundException: org.apache.tiles.autotag.core.runtime.AutotagRuntime)

配置Tiles視圖解析器

  • TilesConfigurer bean

負責定位和加載Tile定義並協調生成Tiles

 // Tiles
  @Bean
  public TilesConfigurer tilesConfigurer() {
    TilesConfigurer tiles = new TilesConfigurer();
    tiles.setDefinitions(new String[] {
            "/WEB-INF/layout/tiles.xml",      //指定Tiles定義的位置
            "/WEB-INF/views/**/tiles.xml"       //遍歷“WEB-INF/”的所有子目錄來查找Tile定義。
    });
    tiles.setCheckRefresh(true);    //啓用刷新功能
    return tiles;
  }
  • TilesViewResolver bean

將邏輯視圖名稱解析爲Tile定義。

@Bean
  public ViewResolver viewResolver() {
    return new TilesViewResolver();
  }
  • WebConfig.java
@Configuration
@EnableWebMvc //啓用Spring MVC
@ComponentScan("com.zexing.spittr.web") //啓用組件掃描
public class WebConfig extends WebMvcConfigurerAdapter {

  @Bean
  public ViewResolver viewResolver() {    //配置視圖解析器
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    resolver.setViewClass(JstlView.class);
    return resolver;
  }
  
  @Override
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
  }
  
  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {     //配置靜態資源的處理
    // TODO Auto-generated method stub
    super.addResourceHandlers(registry);
  }

}

  • 通過xml配置
<bean id="tilesConfigurer"
      class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
    <property name="difinitions">
      <list>
        <value>WEB-INF/layout/tiles.xml</value>
        <value>/WEB-INF/view/**.tiles.xml</value>
    </property>
</bean>
<bean id="ViewResolver"
    class="org.springframework.web.servlet.view.tiles3.TilesViewResolver" />

【TODO】定義Tiles

未完待續

Thymeleaf

1.Thymeleaf 在有網絡和無網絡的環境下皆可運行,即它可以讓美工在瀏覽器查看頁面的靜態效果,也可以讓程序員在服務器查看帶數據的動態頁面效果。這是由於它支持 html 原型,然後在 html 標籤裏增加額外的屬性來達到模板+數據的展示方式。瀏覽器解釋 html 時會忽略未定義的標籤屬性,所以 thymeleaf 的模板可以靜態地運行;當有數據返回到頁面時,Thymeleaf 標籤會動態地替換掉靜態內容,使頁面動態顯示。
2.Thymeleaf 開箱即用的特性。它提供標準和spring標準兩種方言,可以直接套用模板實現JSTL、 OGNL表達式效果,避免每天套模板、該jstl、改標籤的困擾。同時開發人員也可以擴展和創建自定義的方言。
3.Thymeleaf 提供spring標準方言和一個與 SpringMVC 完美集成的可選模塊,可以快速的實現表單綁定、屬性編輯器、國際化等功能。

配置Thymeleaf視圖解析器

配置啓用的三個bean

  • ThymeleafViewResolver:將邏輯視圖名稱解析爲Thymeleaf模板視圖;
  • SpringTemplateEngine:處理模板並渲染結果;
  • TemplateResolver:加載Thymeleaf模板。
   @Bean
    public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
      ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
      viewResolver.setTemplateEngine(templateEngine);
      return viewResolver;
    }
    @Bean
    public SpringTemplateEngine templateEngine(TemplateResolver templateResolver) {
      SpringTemplateEngine templateEngine = new SpringTemplateEngine();
      templateEngine.setTemplateResolver(templateResolver);
      return templateEngine;
    }
  
    @Bean
    public TemplateResolver templateResolver() {
      TemplateResolver templateResolver = new ServletContextTemplateResolver();
      templateResolver.setPrefix("/WEB-INF/views/");
      templateResolver.setSuffix(".html");
      templateResolver.setTemplateMode("HTML5");
      return templateResolver;
    }
  
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
      configurer.enable();
    }

使用 xml

<bean id="viewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver"
	p:templateEngine-ref="templateEngine" />
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine"
	P:templateResolver-ref="templateResolver" />
<bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver"
	p:prefix="WEB-INF/templates/"
	p:suffix=".html"
	p:templateMode="HTML5" />

ThymeleafViewResolver是Spirng MVC中ViewResolver的一個實現類。像其他視圖解析器一樣,會接受一個邏輯視圖名稱,並將其解析爲視圖。不過在該場景下 ,視圖會是一個Thymeleaf模板。

需要注意的是:ThymeleafViewResolver bean中注入了一個對SpringTemplateEnginebean 的引用。SpringTemplateEngine會在Spring中啓用Thymeleaf引擎,用來解析模板並基於這些模板渲染結果。

TemplateResolver會最終定位和查找模板。與之前配置的InternalResourceVIewResolver類似,他使用了prefix 和 suffix屬性。它的templateMode屬性被設置爲HTML5,這表明我們預期要解析的模板會渲染成HTML5輸出。

定義Thymeleaf模板

  • 簡單模板
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">            <!--聲明Thymeleaf命名空間-->
  <head>
    <title>Spitter</title>
    <link rel="stylesheet"
          type="text/css"
          th:href="@{/resources/style.css}"></link>    <!--到樣式表的th:href鏈接-->
  </head>
  <body>
      <h1>Welcome to Spitter</h1>
      <a th:href="@{/spittles}">Spittles</a> |          <!--到頁面的th:herf鏈接-->
      <a th:href="@{/spitter/register}">Register</a>
  </body>
</html>

這裏使用th:href屬性的三個地方都是用到了“@{}”表達式,用來計算相對於URL的路徑,相比於在JSP中 使用JSTL的 <c:url>標籤或Spring<s:url>標籤,Thymeleaf 模板能夠按照原始的方式進行編輯甚至渲染,而不必經過任何類型的處理器,當然我們需要Thymeleaf來處理模板,並渲染得到最終期望的輸出。

  • 實現表單綁定
<label th:class="${#fields.hasErrors('firstName')}? 'error'">First Name</label>:
		<input type="text" th:field="*{firstName}"
					 th:class="${#fields.hasErrors('firstName')}? 'error'" /><br/>

這裏th:class屬性會渲染爲一個class屬性,它的值是根據給定的表達式計算得到的。在上面的這兩個th:class屬性中,它會直接檢查firstName域有沒有校驗錯誤。如果有的話,class屬性在渲染時的值爲error。如果這個域沒有錯誤的話,將不會渲染class屬性。

標籤使用了th:field屬性,用來引用後端對象的firstName域。

<form method="POST" th:object="${spitter}">
        <div class="errors" th:if="${#fields.hasErrors('*')}">    <!-- 展現錯誤信息 -->
          <ul>
            <li th:each="err : ${#fields.errors('*')}" 
                th:text="${err}">Input is incorrect</li>
          </ul>
        </div>
        <label th:class="${#fields.hasErrors('firstName')}? 'error'">First Name</label>: 
          <input type="text" th:field="*{firstName}"  
                 th:class="${#fields.hasErrors('firstName')}? 'error'" /><br/>
  
        <label th:class="${#fields.hasErrors('lastName')}? 'error'">Last Name</label>: 
          <input type="text" th:field="*{lastName}"
                 th:class="${#fields.hasErrors('lastName')}? 'error'" /><br/>
  
        <label th:class="${#fields.hasErrors('email')}? 'error'">Email</label>: 
          <input type="text" th:field="*{email}"
                 th:class="${#fields.hasErrors('email')}? 'error'" /><br/>
  
        <label th:class="${#fields.hasErrors('username')}? 'error'">Username</label>: 
          <input type="text" th:field="*{username}"
                 th:class="${#fields.hasErrors('username')}? 'error'" /><br/>
  
        <label th:class="${#fields.hasErrors('password')}? 'error'">Password</label>: 
          <input type="password" th:field="*{password}"  
                 th:class="${#fields.hasErrors('password')}? 'error'" /><br/>
        <input type="submit" value="Register" />

</form>

在頂部,<div>元素使用th:if屬性來檢查是否有校驗錯誤。如果有的話,會渲染<div>,否則的話,它將不會渲染。在<div>中,會使用一個無順序的列表來展現每項錯誤。<li>標籤上的th:each屬性將會通知Thymeleaf爲每項錯誤都渲染一個<li>,在每次迭代中會將當前錯誤設置到一個名爲err的變量中。

"&quot;(ObjectGrapgNavigationlanguageOGNL)使SpirngSpEL{}&quot;表達式是變量表達式,一般來講,他們是對象圖導航語言(Object-Grapg Navigation language OGNL)表達式,但是在使用Spirng的時候,他們是SpEL表達式,在{Spitter}這裏 例子中,它會解析爲ket爲spitter的model屬性。

而對於*{}表達式,他們是選擇表達式。變量表達式是基於整個SpEl上下文計算的,而選擇表達式是基於某一個選中對象計算的。在本例的表單中,選中對象就是標籤中的th:object屬性所設置的對象:模型中的Spitter對象。因此,“*{firsrName}”表達式就會計算爲Spitter對象的firstName屬性。

具體的語法參考官網 或者 博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章