Skip to content

Instantly share code, notes, and snippets.

@Theartbug
Last active May 31, 2019 16:14
Show Gist options
  • Select an option

  • Save Theartbug/5e99d9efda45161267ed1d2fd6de32a5 to your computer and use it in GitHub Desktop.

Select an option

Save Theartbug/5e99d9efda45161267ed1d2fd6de32a5 to your computer and use it in GitHub Desktop.
Spring MVC 4

Spring MVC 4 REST and AJAX

RestController

  • Convenience annotation for annotating a Controller and ResponseBody at the same time
  • Assumes each RequestMapping is going to return the body of it's request
  • less verbose than having to define each method and it's response type
  • no longer need to use the ContentNegotiatingViewResolver
    • but still can for fine tuning
  • Example: EventsReportController.java
package com.pluralsight.controller;

import com.pluralsight.model.Event;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class EventsReportController {

    @RequestMapping("/events")
    public List<Event> getEvents() {
        List<Event> events = new ArrayList<>();

        Event event1 = new Event();
        event1.setName("Java User Group");
        events.add(event1);

        Event event2 = new Event();
        event2.setName("Angular User Group");
        events.add(event2);

        return events; // [{"name":"Java User Group"},{"name":"Angular User Group"}]
    }
}

Angular frontent addition

  • Example: create a function that retrieves data and binds it to angular scope
function Events($scope, $http) {
    $http.get('events.json').
        success(function(data) {
            $scope.events = data;
    })
}
  • Example: create a simple angular UI
<!doctype html>
<html ng-app>
    <head>
        <title>Hello Events Angular</title>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
        <script src="events.js"></script> // will retrieve the events
    </head>
    <body>
        <div ng-controller="Events">
            I have {{events.length}} events!

            <ul class="events-container">
                <li ng-repeat="event in events"> // loops over events
                    {{event.name}}
                </li>
            </ul>
        </div>
    </body>
</html>

Spring MVC 4 Validation

  • already have proper form tags inside of JSP pages
  • just need to add Maven Dependencies and use the BindingResult method signature

Adding Validation

  • Example: pom.xml, add two dependencies
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.0.3.Final</version>
        </dependency>
  • Example: Attendee.java without custom errors
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

import javax.validation.constraints.Size;

public class Attendee {

    @Size(min=2, max=30)
    private String name;

    @NotEmpty @Email
    private String emailAddress;
    ...
}
  • Example: AttendeeController.java
    @RequestMapping(value = "/attendee", method = RequestMethod.POST)
    // signature must go in this order or will not work
    public String processAttendee(@Valid Attendee attendee, BindingResult result, Model m) {
        System.out.println(attendee);

        // must check if there are errors
        if(result.hasErrors()) return "attendee";

        return "redirect:index.html";
    }
  • Example: custom error messages
// messages_es.properties

// corresponds to the validation constraint annotations
// Size is special since it can be specific to class and method
Size.attendee.name=El nombre debe estar entre {2} y {1} caracteres

// email denotes the same error message for all email addresses throughout the app using this validator
Email=¡¡¡La dirección de email no es válida!!! UG!
NotEmpty=¡El campo no se puede dejar en blanco!

Custome phone validation

  • Example: Phone.java interface
package com.pluralsight.view;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Constraint(validatedBy = PhoneConstraintValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Phone {
    // refers to key inside of messages.properties
    String message() default "{Phone}";

    // generic array
    Class<?>[] groups() default {};

    // pass in values associated with annotation
    Class<? extends Payload>[] payload() default{};
}
  • Example: PhoneConstraintValidator.java
package com.pluralsight.view;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class PhoneConstraintValidator implements ConstraintValidator<Phone, String> {

    @Override
    public void initialize(Phone phone) {

    }

    @Override
    public boolean isValid(String phoneField, ConstraintValidatorContext cxt) {
        if(phoneField == null) return false;

        return phoneField.matches("[0-9()-]*");
    }
}
  • Example: Attendee.java add phone field and validation
public class Attendee {

    @Size(min=2, max=30)
    private String name;

    @NotEmpty @Email
    private String emailAddress;

    @Phone
    private String phone;
...
}
  • Example: add to attendee.jsp
    <label for="textinput3"><spring:message code="attendee.phone"/>:</label>
    <form:input path="phone" cssErrorClass="error"/>
    <form:errors path="phone" cssClass="error"/>

Spring MVC 4 Build Out

WebMvcConfigurerAdapter

  • used to aid in configuration with out WebConfig.java
  • helps replace items that were simplified with schemas
    • like the xmlns="http://www.springframework.org/shema" and more
    • can be replaced with extends WebMvcConfigurerAdapter

Static Resources

  • more verbose in java configuration, but just as clear <mvc:resources location="assets" mapping="/assets/**"/>
  • vs:
// WebConfig.java
    @Override
    public void addResourceHanlders(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pdfs/**").addResourceLocations("/WEB-INF/pdf/");
    }
// WebApplicationInitializer.java
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        ...
        dispatcher.addMapping("*.pdf");
    }

Recap techniques

  • Session variables can be stored using @SessionAttribute
  • when to use forward vs redirect
  • index.html vs index.jsp
    • more secure to not expose our backend technology of jsp pages

forward vs redirect

  • Example: redirect in EventController.java
    @RequestMapping(value="/event", method = RequestMethod.POST)
    public String processEvent(@ModelAttribute("event") Event event) {
        System.out.println(event);

        // will redirect as opposed to using the internal resource view resolver
        return "redirect:index.html";
    }
  • will change the url
  • Example: forward in HelloController.java
    // forward request along to jsp
    // different than just returning index.jsp since response is continuing
    // our response object, building onto it (not creating a new request)
    @RequestMapping(value="/index")
    public String index(Model model) {
        return "forward:index.jsp";
    }
  • will leave the url as it was so users cannot bookmark the page in the process!

SessionAttributes

  • Example: SessionAttribute persists the event object
@Controller
// saves the "event" model through page navigation / sessions
// references the same model object in processEvent
@SessionAttributes("event")
public class EventController {
    @RequestMapping(value="/event", method = RequestMethod.GET)
    public String displayEventPage(Model model) {
        Event event = new Event();
        event.setName("Jave User Group");
        model.addAttribute("event", event);

        return "event";
    }

    @RequestMapping(value="/event", method = RequestMethod.POST)
    // links up with the SessionAttribute
    public String processEvent(@ModelAttribute("event") Event event) {
        System.out.println(event);

        // will redirect as opposed to using the internal resource view resolver
        return "redirect:index.html";
    }
}

Attendee Build out

  • Example: Attendee.java
package com.pluralsight.model;

public class Attendee {

    private String name;

    private String emailAddress;

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • Example: AttendeeController.java
package com.pluralsight.controller;

import com.pluralsight.model.Attendee;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class AttendeeController {

    @RequestMapping(value = "/attendee", method = RequestMethod.GET)
    public String displayAttendeePage(Model model) {
        Attendee attendee = new Attendee();

        model.addAttribute("attendee", attendee);

        return "attendee";
    }

    @RequestMapping(value = "/attendee", method = RequestMethod.POST)
    public String processAttendee(@ModelAttribute("attendee") Attendee attendee) {
        System.out.println(attendee);

        return "redirect:index.html";
    }
}
  • Example: attendee.jsp
<%--
  Created by IntelliJ IDEA.
  User: gprovost
  Date: 2019-05-16
  Time: 09:10
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
    <title>Attendee Page</title>

    <style type="text/css">
        .error {
            color: #ff0000;
        }
        .errorBlock {
            color: #000;
            background-color: #ffEEEE;
            border: 3px solid #ff0000;
            padding: 8px;
            margin: 16px;
        }
    </style>
</head>
<body>

<form:form commandName="attendee">
    <form:errors path="*" cssClass="errorBlock" element="div"/>
    <label for="textinput1">Enter Name:</label>
    <form:input path="name" cssErrorClass="error"/>
    <form:errors path="name" cssClass="error"/>
    <br>
    <label for="textinput2">Enter Email Address:</label>
    <form:input path="emailAddress" cssErrorClass="error"/>
    <form:errors path="emailAddress" cssClass="error"/>
    <br>
    <input type="submit" class="btn" value="Enter Attendee" />
</form:form>
</body>
</html>

Internationalization

  • Example: attendee.jsp
...

<body>

<a href="?language=en">
    English
</a>
<br/>
<a href="?language=es">
    Spanish
</a>

<form:form commandName="attendee">
    <form:errors path="*" cssClass="errorBlock" element="div"/>
<%--    align with the keys in the properties files--%>
    <label for="textinput1"><spring:message code="attendee.name"/> :</label>
    <form:input path="name" cssErrorClass="error"/>
    <form:errors path="name" cssClass="error"/>
    <br>
<%--    align with the keys in the properties files--%>
    <label for="textinput2"><spring:message code="attendee.email.address"/> :</label>
    <form:input path="emailAddress" cssErrorClass="error"/>
    <form:errors path="emailAddress" cssClass="error"/>
    <br>
    <input type="submit" class="btn" value="Enter Attendee" />
</form:form>

</body>
</html>
  • Example: WebConfig.java
    // loads message properties
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        // prefix of the file that will store name / value properties for internationalization
        messageSource.setBasename("messages");
        return messageSource;
    }

    // sees if there is a current local instead of requesting it every time
    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver resolver = new SessionLocaleResolver();
        // can use a type-safe enum for java config instead of a string "en" in xml config
        resolver.setDefaultLocale(Locale.ENGLISH);
        return resolver;
    }

    // detects when there is a change in the url
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LocaleChangeInterceptor changeInterceptor = new LocaleChangeInterceptor();
        changeInterceptor.setParamName("language");
        registry.addInterceptor(changeInterceptor);
    }
  • Example: messags.properties
// messags_en.properties
attendee.name=Enter Name
attendee.email.address=Enter Email Address

// messages_es.properties
attendee.name=Introducir nombre
attendee.email.address=Introduzca la dirección de correo electrónico

Spring MVC 4 Views

Conventions

  • place .jsp pages under WEB-INF folder
  • force users to go through application instead of allowing them to directly see .jsp pages

Resolving a View

  • create a Bean within the WebConfig.java file containing an InternalResourceViewResolver
    • removes the need for the controller to return the file as a string (hello.jsp), instead just the name of the file (hello)
  • Example: WebConfig.java
// creates the same as serverlet-config in web.xml and appContext.xml
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.pluralsight")
public class WebConfig {
    
    @Bean
    public InternalResourceViewResolver getInternalresourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/jsp/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
    
}

Application

  • Creating an Event: EventController.java
@Controller
public class EventController {
    @RequestMapping(value="/event", method = RequestMethod.GET)
    public String displayEventPage(Model model) {
        Event event = new Event();
        event.setName("Jave User Group");
        model.addAttribute("event", event);

        return "event";
    }
}
  • event.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
    <title>Event Page</title>

<style type="text/css">
    .error {
        color: #ff0000;
    }
    .errorBlock {
        color: #000;
        background-color: #ffEEEE;
        border: 3px solid #ff0000;
        padding: 8px;
        margin: 16px;
    }
</style>
</head>
<body>

    <form:form commandName="event">
        <form:errors path="*" cssClass="errorBlock" element="div"/>
        <label for="textinput1">Enter Minutes:</label>
        <form:input path="name" cssErrorClass="error"/>
        <form:errors path="name" cssClass="error"/>
        <br>
        <input type="submit" class="btn" value="Enter Event" />
    </form:form>

</body>
</html>

Spring MVC 4 Controllers

  • controllers are the "verbs" in the system that do an action on something

Responsibilities

  • interpret user input and transform that input into a model
  • provide access to business logic
  • determine view based off logic
  • interprets exceptions from business logic / service layer
    • act as traffic cop

controller annotations

  • @Controller: works similar to MVC 3
  • @RestController: new in MVC 4
    • makes it easier to expose RESTful services outside of the controller
  • @Configuration: signifies a configuration class
    • how to step outside of needed xml config files
  • @EnableWebMvc: Enables Java Config
  • @ComponentScan: Override the default scan location for controllers
  • Example: Controller annotation
package com.pluralsight.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController {

    @RequestMapping(value = "/greeting")
    public String sayHello(Model model) {
        model.addAttribute("greeting", "Hello World");

        return "hello";
    }
}

EnableWebMvc

  • convenience annotation for WebMvcConfigurationSupport
  • Only used for Java configuration of Spring MVC webapps
  • customizable by extending WebMvcConfigurerAdapter

web.xml

  • does not abandon Java config
  • still need Dispatcher Servlet
    • some people prefer to do this in web.xml as opposed to java config
  • contextConfigurationLocation points to class rather than XML file
  • Example:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
        </init-param>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.pluralsight.WebConfig</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

</web-app>

Without web.xml

  • still need a mapping somewhere
  • WebApplicationInitializer is needed
    • uses Servlet 3.0 hooks
  • builds app context for us
  • Example: WebAppInitializer
// hooks into the servlet engine
public class WebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        WebApplicationContext context = getContext();
        // add a listener to listen to registration events from the servlet context
        servletContext.addListener(new ContextLoaderListener(context));
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(context));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("*.html");
    }

    private AnnotationConfigWebApplicationContext getContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.setConfigLocation("com.pluralsight.WebConfig");
        return context;
    }
}

Spring MVC 4 Architecture

Patterns

  • MVC still sound pattern, but there are others
    • MVP, MVVM, MV*
    • to help handle different JS frameworks and mobile applications

ModelView-ViewModel

  • originated from .NET
  • RESTful backend, rich JS frontend
  • layers cleanly separated
  • view can receive notifications and give commands to the ViewModel and there is data binding between the two
    • View <===> ViewModel <===> Model

Controllers

  • XML config is reduced, can get away with none
  • new @RestController annotation
    • more easily expose RESTful services from code without boilerplate inside of xml config
    • set up using @EnableWebMvc

Service

  • same as in Spring MVC 3, but java config no longer requires appContext.xml or servlet-config.xml
  • use annotations for various configuration elements
    • comonent-scan, interceptors, handlers, formatters, converters
  • convenience annotations similar to namespaces

Repository

  • Java config does not require an appContext.xml or a servlet-config.xml
    • you can though, personal preference
  • different types of Repositories if using Spring Data JPA
  • convention over configuration

Configuration

Container-less

  • can run without a container with MVC 4
    • instead run embedded withing Tomcat

java configuration

  • do not need appContext.xml or servlet-config.xml
    • same stuff moved into java code rather than xml file
  • Annotations will be used instead for various configuration elements / namespaces
  • Example: WebConfig.java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

// these annotations create the same as serverlet-config in web.xml and appContext.xml
@Configuration
@EnableWebMvc
public class WebConfig {
}
  • no need for web.xml configuration
    • servlet 3+ spec negates need for it
    • spring helps by doing initialization interfaces
    • can create one if needed (legacy code)
      • inside of src/main/webapp/WEB-INF
    • Spring facilitates any configuration needs

Upgrading

  • order does matter in the pom.xml file for dependencies.
    • If one dependency is a lower version than another and they have the same transient dependencies, order in the pom.xml will dictate which version is downloaded
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment