View Resolvers

  • Views are rendered using a view resolver.
  • Views come in many formats such as JSP,PDF,JSON.
  • View and ViewResolver exist as interfaces in Spring MVC
    • Using the implementation of these interfaces we can display wide range of views to the user
  •  In a complex view resolution environment 
    • We resolve views using chained view resolvers.
    • Present resources using different formats(xml,json)
    • Process form requests using POST-REDIRECT-GET
      • In a POST-REDIRECT-GET pattern 
        • We Post a Form
        • We Perform Logic in Controller
        • We send back to the browser a redirect i.e. 302,301 code on redirect.
        • Tells the browser that it needs to perform a GET request on a url.
      • Helps us to avoid double posting of a form.
      • Redirect attributes and Flash attributes assist us to POST-REDIRECT-GET pattern. 
    • We register multiple view resolvers in our configuration file and order them.
  • Spring MVC allows us to perform content negotiations.
    • Looks at Accept Header in Request
      • Defines the content type that Request is willing to accept.
    • Looks for a prefix at the end of the request for example "/project.json" will show all projects in json format.
Chaining View Resolvers
  • We chain view resolvers  in our Dispatcher Servlet File
  • We create a new view resolver using bean tag which points to class "org.springframework.web.servlet.XmlViewResolver".
    • This view resolver points to an xml view file that is able to map a logical view name to the appropriate view file in our project.
      • We provide property location for this.
    • We also need to define a property order in which they will be consulted.
      • We do this by specifying the order property where the lowest number wins out.
  • Now in the xml view file(which is the type of Spring Bean Configuration file) we configure types of views which we want to display.
    • We do this by creating beans pointing to our view types and giving them id.
    • We return this id from the controller's method to show view
    • We define the property "URL" which points to our view file.
DispatcherServlet
 <?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: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-4.3.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-4.3.xsd">  
      <mvc:annotation-driven/>  
      <context:component-scan base-package="com.springimplant.mvc.controllers"/>  
      <context:component-scan base-package="com.springimplant.mvc.resolvers"/>  
      <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
           <property name="prefix" value="/WEB-INF/views/"></property>  
           <property name="suffix" value=".jsp"></property>  
      </bean>  
      <bean class="org.springframework.web.servlet.view.XmlViewResolver">  
           <property name="location" value="/WEB-INF/spring/views.xml"></property>  
           <property name="order" value="1"></property>  
      </bean>  
      <mvc:resources location="/resources/" mapping="/resources/**"></mvc:resources>  
 </beans>  
views.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"  
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
      <bean id="xmlwelcome" class="org.springframework.web.servlet.view.InternalResourceView">  
           <property name="url" value="/WEB-INF/views/xml_welcome.jsp"/>  
      </bean>  
 </beans>  
Controller Methods
      @RequestMapping("/xml")  
      public String goXml(Model model)  
      {  
           Project project=new Project();  
           project.setName("Java Tutorial");  
           project.setSponsor(null);  
           project.setDescription("A simple project initiated to Learn Java with Fun");  
           model.addAttribute("currentProject",project);  
           return "xmlwelcome";  
      }  
      @RequestMapping("/welcome")  
      public String goNoxml(Model model)  
      {  
           Project project=new Project();  
           project.setName("Java Tutorial");  
           project.setSponsor(null);  
           project.setDescription("A simple project initiated to Learn Java with Fun");  
           model.addAttribute("currentProject",project);  
           return "welcome";  
      }  
ContentNegotiatingViewResolver
  • Delegates to other view resolvers in order to find a particular strategy to represent a particular resource.
  • Main determinant of which view resolver will be used to resolve a resource depends on the file extension of the resource or the accept header that is provided with a HTTP Request for the resource.
  • The contentNegotiating view resolver is capable of returning different view in format such as json or xml.
  • Also capable of returning jsp pages.
  • Configuration of this view resolver is done as follows
    • In Dispatcher Servlet add a bean which points to "org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
      • Add a property with name "viewResolvers" in which we specify our view resolvers in a list.
      • Specify the next property with name "contentNegotationManager".
        • This takes a constructor argument of bean type of "org.springframework.web.accept.ContentNegotiationManager".
          • This takes a bean as a constructor argument which is of type "org.springframework.web.accept.PathExtensionContentNegotiationStrategy".
            • This takes a constructor argument of type map which contains key value pairs of all the acceptable header types that we are going to process with this contentNegotationViewResolver.
      • The last property specifies the default view for the content negotiation view resolver.
        • The name of this property is "defaultViews"
        • We provide a list of default views for example from jackson-databind library we can provide "MappingJackson2JsonView".
Dispatcher Servlet.xml
 <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">  
           <property name="viewResolvers">  
                <list>  
                     <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
                          <property name="prefix" value="/WEB-INF/views/"></property>  
                          <property name="suffix" value=".jsp"></property>  
                     </bean>  
                     <bean class="org.springframework.web.servlet.view.XmlViewResolver">  
                          <property name="location" value="/WEB-INF/spring/views.xml"></property>  
                          <property name="order" value="1"></property>  
                     </bean>       
                </list>  
           </property>  
           <property name="contentNegotiationManager">  
                <bean class="org.springframework.web.accept.ContentNegotiationManager">  
                     <constructor-arg>  
                          <bean class="org.springframework.web.accept.PathExtensionContentNegotiationStrategy">  
                               <constructor-arg>  
                                    <map>  
                                         <entry key="json" value="application/json"/>  
                                         <entry key="xml" value="application/xml"/>  
                                    </map>  
                               </constructor-arg>  
                          </bean>  
                     </constructor-arg>  
                </bean>  
           </property>  
           <property name="defaultViews">  
                <list>  
                     <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>  
                </list>  
           </property>  
      </bean>  
  • As we see that a ContentNegotiationViewResolver utilizes several view resolvers in order to resolve a resource that we specify.
  • We have added a new method in our project controller that returns a project object.
    • This method has a @ResponseBody annotation so we will print the object directly in a serialized form such as xml or json.
  • If we look at the Project Entity class we have added @XMLRootElement annotation 
    • This is a jaxb annotation which helps us to control what the json or xml built will look like.
  • How the ContentNegotiationViewResolver works is as follows
    • When we return a string from the controller method it knows that we are trying to display html to a user that will be generated by a jsp file.
    • Since html is not present in the map in ContentNegotiationViewResolver configuration. It delegates to one of its view resolvers.
    • It finds that InternalResourceViewResolver is capable of rendering that view because it resolves the logical view name with prefix and suffix and find that particular view.
    • If we had a mapping in view.xml file it would have resolved our view first since its order is first.
    • When we fire a url with xml extension the ContentNegotiationViewResolver consults the map entry and checks the xml extension and realizes that the content accepted back is an xml file it looks for a view that can render xml.
      • Since latest versions of java have jaxb within them the translation occurs automatically without specifying a view.
  • ContentNegotiationViewResolver is very dynamic it is able to contain other view resolvers also it is able to look at the extension that we are providing in the url and provide us appropriate content type.
    • It first consults all the view resolvers to provide content type if none of them are able to resolve then it checks the default views and check if any views can return the requested content type.
      • It passes object to the concerned view and returns us json.
  • If we are sending a request via javascript via ajax we need to specify headers in the request i.e "Accept-Header" header
    • It will then look at the header in the request and match them to the appropriate type and render appropriate response.
ProjectController
      @RequestMapping(value="/find/{projectId}")  
      @ResponseBody  
      public Project findProjectObject(Model model, @PathVariable("projectId") Long projectId)  
      {  
           Project p1=this.projectService.find(projectId);            
           return p1;  
      }  
Project Entity
 @XmlRootElement(name="Project")  
 public class Project {  
Pom.xml
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.springimplant.mvc</groupId>  
  <artifactId>course-project</artifactId>  
  <version>0.0.1-SNAPSHOT</version>  
  <properties>  
   <maven.compiler.target>1.8</maven.compiler.target>  
   <maven.compiler.source>1.8</maven.compiler.source>  
  </properties>  
  <dependencies>  
       <dependency>  
            <groupId>org.slf4j</groupId>  
            <artifactId>slf4j-log4j13</artifactId>  
            <version>1.0.1</version>  
       </dependency>  
       <dependency>  
            <groupId>log4j</groupId>  
            <artifactId>log4j</artifactId>  
            <version>1.2.17</version>  
       </dependency>  
       <dependency>  
            <groupId>org.slf4j</groupId>  
            <artifactId>slf4j-simple</artifactId>  
            <version>1.8.0-beta2</version>  
       </dependency>  
       <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-webmvc</artifactId>  
            <version>5.1.4.RELEASE</version>  
       </dependency>  
       <dependency>  
            <groupId>jstl</groupId>  
            <artifactId>jstl</artifactId>  
            <version>1.2</version>  
       </dependency>  
       <dependency>  
            <groupId>javax.validation</groupId>  
            <artifactId>validation-api</artifactId>  
            <version>2.0.1.Final</version>  
       </dependency>  
       <dependency>  
            <groupId>org.hibernate</groupId>  
            <artifactId>hibernate-validator</artifactId>  
            <version>6.1.0.Alpha4</version>  
       </dependency>  
       <dependency>  
            <groupId>com.fasterxml.jackson.core</groupId>  
            <artifactId>jackson-databind</artifactId>  
            <version>2.9.10</version>  
       </dependency>  
       <dependency>  
            <groupId>com.fasterxml.jackson.dataformat</groupId>  
            <artifactId>jackson-dataformat-xml</artifactId>  
            <version>2.9.9</version>  
       </dependency>  
       <!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->  
      <dependency>  
        <groupId>javax.xml.bind</groupId>  
        <artifactId>jaxb-api</artifactId>  
        <version>2.3.1</version>  
      </dependency>  
  </dependencies>  
  <packaging>war</packaging>  
 </project>  

df

No comments:

Post a Comment

Spring Boot

What is circular/cyclic dependency in spring boot? When two services are interdependent on each other, that is to start one service, we requ...