SpringMVC源碼分析(8)剖析ViewResolver

ViewResolver很簡單,經過名稱(name),獲取View視圖的。java

View視圖 其實就是對應MVC中的"V"web

1.ViewResolver 結構圖
spring


wKioL1hGvzHwOJ6pAABL6CTjprE928.png

2.BeanNameViewResolver緩存

經過把返回的邏輯視圖名稱去匹配定義好的視圖bean對象。服務器

@Test
public void testBeanNameViewResolver() throws ServletException {
    StaticWebApplicationContext wac = new StaticWebApplicationContext();
    wac.setServletContext(new MockServletContext());
    MutablePropertyValues pvs1 = new MutablePropertyValues();
    pvs1.addPropertyValue(new PropertyValue("url", "/example1.jsp"));
    wac.registerSingleton("example1", InternalResourceView.class, pvs1);
    BeanNameViewResolver vr = new BeanNameViewResolver();
    vr.setApplicationContext(wac);
    wac.refresh();
    
    View view = vr.resolveViewName("example1", Locale.getDefault());
    assertEquals("Correct view class", InternalResourceView.class, view.getClass());
    assertEquals("Correct URL", "/example1.jsp", ((InternalResourceView) view).getUrl());
}

3.XmlViewResolver
XmlViewResolver這個視圖解析器跟 BeanNameViewResolver 有點相似,也是經過把返回的邏輯視圖名稱去匹配定義好的視圖 bean 對象。app

3.1 配置XMLjsp

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>
   <bean id="example1" class="org.springframework.web.servlet.view.ViewResolverTests$TestView">
      <property name="url"><value>/example1.jsp</value></property>
      <property name="attributesMap">
         <map>
            <entry key="test1"><value>testvalue1</value></entry>
            <entry key="test2"><ref bean="testBean"/></entry>
         </map>
      </property>
      <property name="location"><value>test</value></property>
   </bean>
</beans>

3.2 測試用例
ide

@Test
public void testXmlViewResolver() throws Exception {
   StaticWebApplicationContext wac = new StaticWebApplicationContext();
   wac.registerSingleton("testBean", TestBean.class);
   wac.setServletContext(new MockServletContext());
   wac.refresh();
   TestBean testBean = (TestBean) wac.getBean("testBean");
   XmlViewResolver vr = new XmlViewResolver();
   vr.setLocation(new ClassPathResource("org/springframework/web/servlet/view/views.xml"));
   vr.setApplicationContext(wac);

   View view1 = vr.resolveViewName("example1", Locale.getDefault());
   assertTrue("Correct view class", TestView.class.equals(view1.getClass()));
   assertTrue("Correct URL", "/example1.jsp".equals(((InternalResourceView) view1).getUrl()));
BeanNameViewResolver VS XmlViewResolver 

1. BeanNameViewResolver 要求視圖 bean 對象都定義在 Spring 的 application context 中,而 XmlViewResolver 是在指定的配置文件中尋找視圖 bean 對象,測試

2. XmlViewResolver是 AbstractCachingViewResolver的子類,支持緩存;ui

BeanNameViewResolver 不會進行視圖緩存。
4. ResourceBundleViewResolver
和 XmlViewResolver 同樣它也須要有一個配置文件來定義邏輯視圖名稱和真正的 View 對象的對應關係,不一樣的是 ResourceBundleViewResolver 的配置文件是一個屬性文件,並且必須是放在 classpath 路徑下面的,默認狀況下這個配置文件是在 classpath 根目錄下的 views.properties 文件,若是不使用默認值的話,則能夠經過屬性 baseName 或 baseNames 來指定。

4.1 配置文件testviews_fr.properties

debugView.(class)= org.springframework.web.servlet.view.InternalResourceView
debugView.url=jsp/debug/deboug.jsp
debugView.contentType=text/xml;charset=ISO-8859-1

4.2 測試用例

public class ResourceBundleViewResolverTests extends TestCase {
/** Comes from this package */
private static String PROPS_FILE = "org.springframework.web.servlet.view.testviews";

private ResourceBundleViewResolver rb;

private StaticWebApplicationContext wac;
    protected void setUp() throws Exception {
       rb = new ResourceBundleViewResolver();
       rb.setBasename(PROPS_FILE);
       rb.setCache(getCache());
       rb.setDefaultParentView("testParent");
    
       wac = new StaticWebApplicationContext();
       wac.setServletContext(new MockServletContext());
       wac.refresh();
    
       // This will be propagated to views, so we need it.
       rb.setApplicationContext(wac);
  }
  
  public void testDebugViewFrench() throws Exception {
   View v = rb.resolveViewName("debugView", Locale.FRENCH);
   assertTrue("French debugView must be of type InternalResourceView", v instanceof InternalResourceView);
   InternalResourceView jv = (InternalResourceView) v;
   assertTrue("French debugView must have correct URL", "jsp/debug/deboug.jsp".equals(jv.getUrl()));
   assertTrue(
      "Correct overridden (XML) content type, not '" + jv.getContentType() + "'",
      jv.getContentType().equals("text/xml;charset=ISO-8859-1"));
}
}

ResourceBundleViewResolver第一次進行視圖解析的時候會先new一個BeanFactory對象,而後把properties文件中定義好的屬性按照它自身的規則生成一個個的bean對象註冊到該BeanFactory中,以後會把該BeanFactory對象保存起來,因此ResourceBundleViewResolver緩存的是BeanFactory,而不是直接的緩存從BeanFactory中取出的視圖bean。而後會從bean工廠中取出名稱爲邏輯視圖名稱的視圖bean進行返回。接下來就講講Spring經過properties文件生成bean的規則。它會把properties文件中定義的屬性名稱按最後一個點「.」進行分割,把點前面的內容當作是bean名稱,點後面的內容當作是bean的屬性。這其中有幾個特別的屬性,Spring把它們用小括號包起來了,這些特殊的屬性通常是對應的attribute,但不是bean對象全部的attribute均可以這樣用。其中(class)是一個,除了(class)以外,還有(scope)(parent)(abstract)(lazy-init)。而除了這些特殊的屬性以外的其餘屬性,Spring會把它們當作bean對象的通常屬性進行處理,就是bean對象對應的property。因此根據上面的屬性配置文件將生成以下兩個bean對象:

from http://elim.iteye.com/blog/1770554

5.UrlBasedViewResolver

它是對ViewResolver的一種簡單實現,並且繼承了AbstractCachingViewResolver,主要就是提供的一種拼接URL的方式來解析視圖,它可讓咱們經過prefix屬性指定一個指定的前綴,經過suffix屬性指定一個指定的後綴,而後把返回的邏輯視圖名稱加上指定的前綴和後綴就是指定的視圖URL了。

wKioL1hGxViDg90pAAAhdL5WGMg834.png

5.2 重要屬性

public class UrlBasedViewResolver extends AbstractCachingViewResolver implements Ordered {

   /**
    * Prefix for special view names that specify a redirect URL (usually
    * to a controller after a form has been submitted and processed).
    * Such view names will not be resolved in the configured default
    * way but rather be treated as special shortcut.
    */
   public static final String REDIRECT_URL_PREFIX = "redirect:";

   /**
    * Prefix for special view names that specify a forward URL (usually
    * to a controller after a form has been submitted and processed).
    * Such view names will not be resolved in the configured default
    * way but rather be treated as special shortcut.
    */
   public static final String FORWARD_URL_PREFIX = "forward:";

   //Set the view class that should be used to create views.
   private Class viewClass;
// Set the prefix that gets prepended to view names when building a URL.
   private String prefix = "";
    //the suffix that gets appended to view names when building a URL.
   private String suffix = "";
    //view names; such that 'my*', '*Report' and '*Repo*' 
   private String[] viewNames = null;
    //content type for all views.
   private String contentType;
    //UrlBasedViewResolver 的 redirectContextRelative 的默認值爲 true,
    //這意味着,只要重定向的資源以/開頭,那麼 spring 會幫你添加 contextPath
   private boolean redirectContextRelative = true;
//whether redirects should stay compatible with HTTP 1.0 clients
   private boolean redirectHttp10Compatible = true;
    //the name of the RequestContext attribute for all views
   private String requestContextAttribute;

   private int order = Integer.MAX_VALUE;

   /** Map of static attributes, keyed by attribute name (String) */
   private final Map<String, Object> staticAttributes = new HashMap<String, Object>();
   ...
   }

5.3  createView方法

public static final String REDIRECT_URL_PREFIX = "redirect:";
protected View createView(String viewName, Locale locale) throws Exception {
   // If this resolver is not supposed to handle the given view,
   // return null to pass on to the next resolver in the chain.
   if (!canHandle(viewName, locale)) {
      return null;
   }
   // Check for special "redirect:" prefix.
   if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
      String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
      return new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
   }
   // Check for special "forward:" prefix.
   if (viewName.startsWith(FORWARD_URL_PREFIX)) {
      String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
      return new InternalResourceView(forwardUrl);
   }
   // Else fall back to superclass implementation: calling loadView.
   return super.createView(viewName, locale);
}

URLBasedViewResolver發現返回的視圖名稱包含」redirect:」前綴,因而把返回的視圖名稱前綴」redirect:」去掉,取後面的test.do組成一個RedirectViewRedirectView中將把請求返回的模型屬性組合成查詢參數的形式組合到redirectURL後面,而後調用HttpServletResponse對象的sendRedirect方法進行重定向。一樣URLBasedViewResolver還支持forword:前綴,對於視圖名稱中包含forword:前綴的視圖名稱將會被封裝成一個InternalResourceView對象,而後在服務器端利用RequestDispatcherforword方式跳轉到指定的地址。使用UrlBasedViewResolver的時候必須指定屬性viewClass,表示解析成哪一種視圖,通常使用較多的就是InternalResourceView,利用它來展示jsp,可是當咱們使用JSTL的時候咱們必須使用JstlView


5.4 一段UrlBasedViewResolver的定義

<bean  
   class="org.springframework.web.servlet.view.UrlBasedViewResolver">  
   <property name="prefix" value="/WEB-INF/" />  
   <property name="suffix" value=".jsp" />  
   <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView"/>  
</bean>

6.InternalResourceViewResolver 內部資源視圖解析器

InternalResourceViewResolver會把返回的視圖名稱都解析爲InternalResourceView對象,InternalResourceView會把Controller處理器方法返回的模型屬性都存放到對應的request屬性中,而後經過RequestDispatcher在服務器端把請求forword重定向到目標URL。

6.1最熟悉的一段配置

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

總結

ViewResolver解決的事情很單一

  1. 經過配置,根據不一樣策略,找出匹配的JSP(也能夠是其餘)。

  2. 適當添加緩存處理

  3. 根據策略不一樣,返回不一樣的VIEW,下降耦合度。

相關文章
相關標籤/搜索