Components

  • Components in Spring MVC help us not to reinvent the wheel in many scenarios.
  • Customize different aspects of the request processing life cycle.
    • We use Interceptors,ControllerAdvice, Inverters, Bean Scopes
  • Additional Support for Json
    • We use Jackson with ResponseBody tag
  • Process file uploads using spring MVC.
Interceptors
  1. Handler Interceptors help us to modify the Request Processing life-cycle in Spring MVC.
  2. Helps to add functionality to request processing life-cycle at 3 different points
    1. Before a controller handles a request that has been mapped to a handler method.
      1. PreHandleMethod
        1. Accepts a request,response and handler object arguments using handler method object.
        2. We can use Reflection to inspect annotations on that method, to see url.
    2. After the  handler method has finished performing its logic within that method.
      1. PostHandleMethod
    3. When view is about to be rendered and sent back as a response to the client.
      1. AfterCompletionMethod.
  3. We create a new Interceptor class
    1. This class extends a "HandlerInterceptorAdaptor"
    2. We override the PreHandleMethod which has request as an argument
      1. We have added an attribute "currentDate" to the request which will be intercepted by this Interceptor.
  4. Next we need to register the interceptor within our configuration file(dispatcherServlet.xml) which is shown in below snippet.
    1. We can directly declare a bean which points to the interceptor class.
    2. We can also specify particular path mappings(refine path mappings) for which this interceptor will be used.
      1. Here we have mapped it to path "/project/**/".
Interceptor Class
 package com.springimplant.mvc.interceptors;  
 import java.util.Date;  
 import javax.servlet.http.HttpServletRequest;  
 import javax.servlet.http.HttpServletResponse;  
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  
 public class GlobalInterceptor extends HandlerInterceptorAdapter {  
      @Override  
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  
                throws Exception {  
           request.setAttribute("currentDate",new Date());  
           return super.preHandle(request, response, handler);  
      }  
 }  
Configuration Snippet
      <mvc:interceptors>  
           <mvc:interceptor>  
                <mvc:mapping path="/project/**"/>  
                <bean class="com.springimplant.mvc.interceptors.GlobalInterceptor"></bean>  
           </mvc:interceptor>  
      </mvc:interceptors>  

Bean Scopes
  • Beans have a particular scope in Spring Core and Spring MVC. The default scope of Bean is Singleton.
  • In Spring Core we are able to create beans with prototype scope or a singleton scope.
  • When in Spring MVC we get additional scopes because we are working with a web application content.These scopes are request scope,the session scope and application scope.
    • To get these scopes we must define a listener in our web.xml file
      •        <listener>  
                    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>  
               </listener>  
        
    • This listener associates each request that comes in to our server with a new thread.
      • This helps us in request and session scope.
  • Next we will be creating a counter bean and using it in 3 different scopes i.e. as an application Scope,Session Scope,Request Scope.
    • This counter will initialize depending on the scope of the bean injected and will increment on each request.
  • We declare this class as a bean with 3 different scopes.Since we are using 3 different scopes for same bean we need to initialize this bean with 3 different id's.
  • Also since we are injecting a bean of different scope into different scope bean i.e. counter bean which has request,application and session scopes into GlobalInterceptor which is a singleton scope we need to use a scope proxy.
    • For this we need to enable aop namespace in our dispatcherservlet.xml file since this namespace contains the scoped policy tag.
Counter Class
 package com.springimplant.mvc;  
 public class HitCounter {  
      private int hits;  
      public HitCounter()   
      {  
           System.out.println("Hit Counter Instantiated");  
      }  
      public int getHits() {  
           return hits;  
      }  
      public void setHits(int hits) {  
           this.hits = hits;  
      }  
 }  
Dispatcher Servlet
 <?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"  
      xmlns:aop="http://www.springframework.org/schema/aop"  
      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  
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">  
      <mvc:annotation-driven/>  
      <bean id="RequestHit" class="com.springimplant.mvc.HitCounter" scope="request">  
           <aop:scoped-proxy/>  
      </bean>  
      <bean id="applicationHit" class="com.springimplant.mvc.HitCounter" scope="application">  
           <aop:scoped-proxy/>  
      </bean>  
      <bean id="sessionHit" class="com.springimplant.mvc.HitCounter" scope="session">  
           <aop:scoped-proxy/>  
      </bean>  
      <mvc:interceptors>  
           <mvc:interceptor>  
                <mvc:mapping path="/project/**"/>  
                <bean class="com.springimplant.mvc.interceptors.GlobalInterceptor"></bean>  
           </mvc:interceptor>  
      </mvc:interceptors>  
      <context:component-scan base-package="com.springimplant.mvc.controllers"/>  
      <context:component-scan base-package="com.springimplant.mvc.resolvers"/>  
      <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>  
      <mvc:resources location="/resources/" mapping="/resources/**"></mvc:resources>  
 </beans>  
  • Next we need to inject these beans as "@Resource" into our GlobalInterceptor file.
    • Next we need to increment the beans in preHandle function so that each time request comes in,a new instance of counter is created depending on the scope and then it is incremented.
    • We can also use @Autowire in case we are using only single instance of a bean.
Interceptor Class
 package com.springimplant.mvc.interceptors;  
 import java.util.Date;  
 import javax.annotation.Resource;  
 import javax.servlet.http.HttpServletRequest;  
 import javax.servlet.http.HttpServletResponse;  
 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  
 import com.springimplant.mvc.HitCounter;  
 public class GlobalInterceptor extends HandlerInterceptorAdapter {  
      @Resource   
      private HitCounter RequestHit;  
      @Resource  
      private HitCounter applicationHit;  
      @Resource  
      private HitCounter sessionHit;  
      @Override  
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  
                throws Exception {  
           RequestHit.setHits(RequestHit.getHits()+1);  
           System.out.println("Request Hits "+RequestHit.getHits());  
           applicationHit.setHits(applicationHit.getHits()+1);  
           System.out.println("Application Hits "+applicationHit.getHits());  
           sessionHit.setHits(sessionHit.getHits()+1);  
           System.out.println("Session Hits "+sessionHit.getHits());  
           request.setAttribute("currentDate",new Date());  
           return super.preHandle(request, response, handler);  
      }  
 }  
  • Now we run our application and go to the url mapped to our interceptor
    • We find that bean is defined in session scope is only initialized once a new session is created.Test this by hitting same url in different browser
    • We find that the bean defined in application scope is initialized only once in in the lifetime of the application.
    • The bean defined as request scope is initialized on each request.
Jackson Json Support
  • In spring mvc the Jackson Library is leveraged in order to provide json support.
  • Add Jackson-databind artifact to our pom.xml file.This adds jackson to our classpath
  • Next time whenever we will return an object on @ResponseBody Jackson will automatically convert it to a json object.
Controller Advice
  • Controller's advice helps us to increase the re-usability of the components we are placing within our controllers.
  • It allows us to specify Exception Handler,Init Binder,Model Attributes throughout all of our controllers by placing these in a central class which applies to different controllers.
  • We annotate a class with "@ControllerAdvice" to make it a controller advice.
    • We have created a GlobalControllerAdvice here.
  • We can specify which elements the controller advice will apply too based on annotations,package name,type.
    • These are defined as parameters in annotation
Global Advice Class
 package com.springimplant.mvc.controllers;  
 import java.util.Date;  
 import javax.servlet.http.HttpServletRequest;  
 import org.springframework.stereotype.Controller;  
 import org.springframework.web.bind.WebDataBinder;  
 import org.springframework.web.bind.annotation.ControllerAdvice;  
 import org.springframework.web.bind.annotation.ExceptionHandler;  
 import org.springframework.web.bind.annotation.InitBinder;  
 import org.springframework.web.bind.annotation.ModelAttribute;  
 import com.springimplant.mvc.data.validators.ProjectValidator;  
 @ControllerAdvice(annotations = Controller.class)  
 public class GlobalControllerAdvice {  
      @ModelAttribute("currentDate")  
      public Date getCurrentDate()  
      {  
           return new Date();  
      }  
      @InitBinder  
      public void initBinder(WebDataBinder binder)  
      {  
           binder.addValidators(new ProjectValidator());  
      }  
      @ExceptionHandler(Exception.class)  
      public String handleError(HttpServletRequest request)  
      {  
           return "controller_error";   
      }  
 }  

Converters
  • Help us while we are data binding in Spring MVC
  • When we want to convert from primitive data types to object types we use converters.
  • Types that are more custom and whose conversion is not available in java automatically.
    • Example conversion from String JulianDate to Date object.
  • Each converter class in Spring implements  "org.springframework.core.convert.converter" interface.
    • This interface takes 2 arguments
      • First is initial type of object
      • Second is of object type we would like to convert too.
    • This interface has a convert method which we need to implement with conversion functionality.
  • Now whenever Spring maps an object from posted form to one of its entity types during submission.
    • If a conversion is not available for a property it checks in the registered converters and if it finds one it uses it.
  • Next we will register this converter to our dispatcher servlet as shown in snippet.
  • We can also use this converters to convert from String to our object types.
    • For example if we have a @pathVariable of type String "ResourceId" we can covert it directly to object Resource using Converters.
      • Next time when we will define this @pathVariable of type String as Resource in our method arguments.Spring will automatically convert it to Resource type. 
DateConverter
 package com.springimplant.mvc.converters;  
 import java.text.ParseException;  
 import java.text.SimpleDateFormat;  
 import java.util.Date;  
 import org.springframework.core.convert.converter.Converter;  
 public class JulianDateConverter implements Converter<String, Date> {  
      @Override  
      public Date convert(String strDate) {  
           Date tmpDate=null;  
           try {  
                System.out.println("Converting Julian Date");  
                tmpDate=new SimpleDateFormat("yyyyDDD").parse(strDate);  
           } catch (ParseException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
           }  
           return tmpDate;  
      }  
 }  
ResourceConverter
 package com.springimplant.mvc.converters;  
 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.core.convert.converter.Converter;  
 import com.springimplant.mvc.data.entities.Resource;  
 import com.springimplant.mvc.data.services.ResourceService;  
 public class ResourceConverter implements Converter<String, Resource> {  
      @Autowired  
      private ResourceService service;  
      @Override  
      public Resource convert(String resourceId) {  
           // TODO Auto-generated method stub  
           return service.find(Long.parseLong(resourceId));  
      }  
 }  
Dispatcher Servlet
      <mvc:annotation-driven conversion-service="conversion-service"/>  
      <bean id="conversion-service" class="org.springframework.context.support.ConversionServiceFactoryBean">  
           <property name="converters">  
                <list>  
                     <bean class="com.springimplant.mvc.converters.JulianDateConverter"></bean>  
                     <bean class="com.springimplant.mvc.converters.ResourceConverter"></bean>  
                </list>  
           </property>  
      </bean>  
Project Controller
      @RequestMapping("/find/{resourceId}")  
      @ResponseBody  
      public Resource findResourceObject(@PathVariable("resourceId") Resource resource){  
 //          return resourceService.find(resourceId);  
           return resource;  
      }  

File Upload
  • Add the input file type to the view.
  • In the dispatcher servlet configuration specify the "multipart-config" tag.
    • In this tag we can specify limits on file sizes, and other constraints we want to put on file uploads.
  • In dispatcher servlet register the bean "StandardServletMultipartResolver"
    • This enables the multipart functionality.
  • Next in our controller add a method with argument of Multipart File type
    • We can perform operations on file using this argument.
Web.xml
       <servlet>  
            <servlet-name>dispatcher</servlet-name>  
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
            <init-param>  
                 <param-name>contextConfigLocation</param-name>  
                 <param-value>/WEB-INF/spring/*-servlet.xml</param-value>  
            </init-param>  
            <load-on-startup>1</load-on-startup>  
            <multipart-config/>  
       </servlet>  
DispatcherServlet.xml
      <bean id="multiPartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>  
JSP file
           <spring:url value="/resource/upload" var="uploadUrl"/>  
           <form method="POST" enctype="multipart/form-data" action="${uploadUrl}">  
                File to upload: <input type="file" name="file"><br />  
                <input type="submit" value="Upload"> Press here to upload the file!  
           </form>  
ResourceController
      @RequestMapping(value="/upload",method=RequestMethod.POST)  
      @ResponseBody  
      public String handleUpload(@RequestParam("file") MultipartFile file) {  
           if(!file.isEmpty())  
           {  
                return "The file size is "+ file.getSize();  
           }  
           else  
           {  
                return "There was a Problem";  
           }  
      }  
a

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...