- Spring MVC
- Flow of a requiest in Spring MVC
- Request to DispacherServlet(the front controller)
- HandlerMapping
- Model and logical view name
- Controller (return to DispacherServlet)
- ViewResolver
- View
- Setting up Spring MVC
- Configure the web.xml
The Servlet
<servlet>
<servlet-name>spitter</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
The Mapping
<servlet-mapping>
<servlet-name>spitter</servlet-name>
<url-pattern>/</url-pattern> Will handle all request including request from static content.
</servlet-mapping>
- Configure the Spring Configuration (ex: project-servelt.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:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<mvc:resourcesmapping="/resources/**" location="/resources/"/> Handle request for static resources
</beans>
- Writing Controller
- Handler Mapping - DispatcherServlet decide which controller to dispach a request to.
BeanNameUrlHandlerMapping
Maps controlers to URL that are based on the controller's BEAN name
ControllerBeanNameHandlerMapping
Similar than above. Names don't requires to follow URL conventions
ControllerClassNameHandlerMapping
Maps controlers to URL that are based on the controller's CLASS name as the basis of URL
DefaultAnnotationHandlerMapping
Maps request to controller and controller methods that are annotated with @RequestMapping
SimpleUrlHandlerMapping
Maps controllers to URL using a property collection defined in the Spring application context.
If no handler mapping is found, uses BeanNameUrlHandlerMapping and DefaultAnnotationHandlerMapping
- To use DefaultAnnotationHandlerMapping, include in app-servlet.xml:
<mvc:annotation-driven/> It includes:
validation support
message conversion
field formating
Controller Example
@Controller <---- Declare the Controller
public class HomeController{
public static final int DEFAULT_SPITTLES_PER_PAGE=25;
private SpitterService spitterService;
@Inject <---- Inject the bean of service
public HomeController(SpitterService spitterService){
this.spitterService=spitterService;
}
@RequestMapping({"/","/home"}) <------ Handle request for home page
public String showHomePage(Map<String,Object> model){ <---- Model is just a Map
List<Spitter> list = spitterService.getRecentSpittles(DEFAULT_SPITTLES_PER_PAGE);
model.put("spittles", list); <------- Data into model
return"home"; <---- Return view name
}
}
- To recognise the @Controller <context:component-scanbase-package="com.habuma.spitter.mvc"/> should be included in app-servlet.xml
- Testing the controller
public classHomeControllerTest{
@Test
public void shouldDisplayRecentSpittles(){
List<Spittle>expectedSpittles = asList(newSpittle(),newSpittle(),newSpittle());
SpitterService spitterService= mock(SpitterService.class);
when(spitterService.getRecentSpittles(DEFAULT_SPITTLES_PER_PAGE)).thenReturn(expectedSpittles);
HomeControllercontroller= new HomeController(spitterService);
HashMap<String,Object> model=new HashMap<String,Object>();
String viewName=controller.showHomePage(model);
assertEquals("home",viewName);
assertSame(expectedSpittles,model.get("spittles"));
verify(spitterService).getRecentSpittles(DEFAULT_SPITTLES_PER_PAGE);
}
- Resolving Internal views
Relating to rendering output to the user.
Can use JSP, Velocity, FreeMarker.
There are many View resolver implementations
BeanNameViewResolver, ContentNegotiatingViewResolver, FreeMarkerViewResolver, InternalResourceViewResolver, JasperReportsViewResolver, ResourceBundleViewResolver, TilesViewResolver, UrlBasedViewResolver, VelocityLayoutViewResolver, VelocityViewResolver, XmlViewResolver
- InternalResolverViewResolver.
Convention-over-configuration approach
In app-servlet.xml
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
*****
<propertyname="prefix"value="/WEB-INF/views/"/>
<propertyname="suffix"value=".jsp"/>
</bean>
Configure that al JSP will be inside /WEB-INF/views folder and will have .jsp as sufix.
DispatchServlet ask InternalResourceViewResolver to resolv a view.
InternalResourceViewResolver creates a InternalResourceView(the View) which dispatches the request to the JSP for rendering.
If the jsp users JSTL, we should replace InternalResourceView with JstlView by setting the viewClass property inside the block above
***** <propertyname="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
It exposes JSTL-specific request attributes so that we can use the internationalization
- Resolving tiles view
Templating framework for laying out of a piece of pages as fragments to make on e page.
Register TilesViewResourver in app-servlet.xml
<bean class="org.springframework.web.servlet.view.tiles2.TilesViewResolver"/>
View resolver attempts to find views that are Tiles templates.
Also add TilesConfigurer in app-servlet.xml
<bean class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/views.xml</value> <---- Where tiles are configured
</list>
</property>
</bean>
<tiles-definitions>
<definition name="template" template="/WEB-INF/views/main_template.jsp">
<put-attributename="top" value="/WEB-INF/views/tiles/spittleForm.jsp"/>
<put-attributename="side" value="/WEB-INF/views/tiles/signinsignup.jsp"/>
</definition>
<definition name="home" extends="template">
<put-attributename="content"value="/WEB-INF/views/home.jsp"/>
</definition>
</tiles-definitions>
- Defining a page
<%@ taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglibprefix="s"uri="http://www.springframework.org/tags"%>
<%@ taglibprefix="t"uri="http://tiles.apache.org/tags-tiles"%>
<%@ taglibprefix="fmt"uri="http://java.sun.com/jsp/jstl/fmt"%>
<div>
<c:forEach var="spittle"items="${spittles}">
<s:urlvalue="/spitters/{spitterName}" var="spitter_url">
<s:param name="spitterName" value="${spittle.spitter.username}"/>
</s:url>
</c:forEach>
</div>
- Handling controller input
How to deal with a set of parameters.
To link: http://localhost:8080/spitter/spittles?spitter=habuma
@Controller
@RequestMapping("/spitter") <--- Root URL path
public class SpitterController{
private final SpitterService spitterService;
@Inject
public SpitterController(SpitterService spitterService){
this.spitterService=spitterService;
}
@RequestMapping(value="/spittles",method=GET) <--- Handle Get Requests for /spitterSpittlers
public String listSpittlesForSpitter( @RequestParam("spitter") String username, Model model){ <--- The request param
Spitterspitter=spitterService.getSpitter(username);
model.addAttribute(spitter); <---- Fill model
model.addAttribute(spitterService.getSpittlesForSpitter(username));
return "spittles/list";
}
}
- Model model is just likely a Map<String,Object> with some methods: addAttribute(similar to Map.put)
- Processing Form
Two operations:
Display the form
Process the form submition
Displaying the form
First call the controlller
@RequestMapping(method=RequestMethod.GET,params="new") <--- only handle this GET if pass the param new.
public String createSpitterProfile(Model model){
model.addAttribute(new Spitter()); <---- Create a empty object
return "spitters/edit"; <---- Forward to a page
}
ex of call:
http://localhost:8080/Spitter/spitters?new
- Defining the form view
Tiles template
<definitionname="spitters/edit"extends="template">
<put-attributename="content" value="/WEB-INF/views/spitters/edit.jsp"/>
</definition>
- Write the jsp
- Processing the Form - How to handle the form
@RequestMapping(method=RequestMethod.POST)
public String addSpitterFromForm(@ValidSpitter spitter, BindingResult bindingResult){
if(bindingResult.hasErrors()){ <--- Check validation
return"spitters/edit";
}
spitterService.saveSpitter(spitter); <---- Call the service to save object
return "redirect:/spitters/"+spitter.getUsername(); <--- Redirect to a specific page
}
- Handling requests with path variable
- http://localhost:8080/Spitter//spitters/{username}
@RequestMapping(value="/{username}",method=RequestMethod.GET) <------ value indicates how the link should be called
public String showSpitterProfile(@PathVariable String username, Model model){ <---- username again indicating the path{username}
model.addAttribute(spitterService.getSpitter(username));
return "spitters/view";
}
- Validating Input
Use @Valid annotation
- Show errors in JSP
<%@ taglibprefix="sf"uri="http://www.springframework.org/tags/form"%>
<sf:errors path="fullName"cssClass="error"/>
<%@ taglibprefix="sf"uri="http://www.springframework.org/tags/form"%>
<sf:form method="POST"modelAttribute="spitter" enctype="multipart/form-data">
<fieldset>
<th><sf:label path="fullName">Fullname:</sf:label></th>
<td><sf:input path="fullName"size="15"/><br/>
<sf:errors path="fullName"cssClass="error"/>
<th><sf:label path="username">Username:</sf:label></th>
<td><sf:input path="username"size="15"maxlength="15"/>
<small id="username_msg">Nospaces,please.</small><br/>
<sf:errors path="username"cssClass="error"/>
</fieldset>
</sf:form>
Upload File
<sf:form method="POST" modelAttribute="spitter" enctype="multipart/form-data">
multipart/form-data makes each field be submitted as a distinct part of the POST
<tr>
<th><labelfor="image">Profileimage:</label></th>
<td><inputname="image"type="file"/>
</tr>
Receiving the file
@RequestMapping(method=RequestMethod.POST)
public String addSpitterFromForm(@Valid Spitter spitter, Binding Result bindingResult,
@RequestParam(value="image",required=false) MultipartFile image){
if(bindingResult.hasErrors()){
return"spitters/edit";
}
spitterService.saveSpitter(spitter);
try {
if(!image.isEmpty()){
validateImage(image);
saveImage(spitter.getId()+".jpg",image);//
}
} catch(ImageUploadExceptione){
bindingResult.reject(e.getMessage());
return"spitters/edit";
}
return "redirect:/spitters/"+spitter.getUsername();
}
private void validateImage(MultipartFile image){
if(!image.getContentType().equals("image/jpeg")){ <--- To guarantee that the file is a jpeg
thrownew ImageUploadException("OnlyJPGimagesaccepted");
}
}
To upload file, spring should be configured
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
p:maxUploadSize="500000"/>
DispatcherServlet will look for multipartResolver id. DispatcherServlet doesn't know how to deal with multipart form data.
- Spring MVC + Jquery + AJAX
- Maven
jackson <--- Responsible for marshall to JSON
- in dispatcher-servlet.xml
<context:component-scan base-package "br.com.packageRoma"/> <---- Defines which package to look for annotations
<mvc:annotation-driven> <--- Shows Spring to use annotation
- Controller
public class TestController {
@RequestMapping(value="/action.htm",method=RequestMethod.POST)
public @ResponseBody Test method(HttpServletRequest request, HttpServletResponse response) throws Exception
{
Test test = new Test()
return test; <---- Jackson do the marshall to JSON
}
}
or
public @ResponseBody User addUser(@ModelAttribute(value="user") User user, BindingResult result ){
String text;
if(!result.hasErrors()){
userList.add(user);
text = "User inserted";
}else{
text = "Couldn't insert user";
}
return user;
}
- The jsp
<script type="text/javascript" language="javascript" src="URL/jquery"></script>
<script type="text/javascript">
function testCallAjax(){
$.ajax({
type: "post",
url: "http://localhost:8080/TestSpring/test/test.htm",
cache: false,
data:'firstName=' + $("#firstName").val() + "&lastName=" + $("#lastName").val() + "&email=" + $("#email").val(),
success: function(response){
$('#result').html(""); <--- Clean the div that will be renderized
var obj = JSON.parse(response);
$('#result').html("First Name:- " + obj.firstName +"</br>Last Name:- " + obj.lastName);
},
error: function(){
alert('Error while request..');
}
});
}
</script>
....
<form name="testForm" method="post">
<table>
<tr><td><input type="text" name="firstName" id="firstName" value=""></td></tr>
<tr><td><input type="text" name="lastName" id="lastName" value=""></td></tr>
<tr><td ><input type="button" value="Ajax Submit" onclick="testCallAjax();"></td></tr>
</table>
</form>
<div id="result"></div> <----- div that will be renderized.
- Spring Web Flow
Framework that enables development of elements following a prescribed flow.
It separes the definition of an applications's flow from the classes and views that implement the flow's behavior.
- Install (maven)
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-webflow</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>