Skip to content

Instantly share code, notes, and snippets.

@TerryTsao
Last active July 1, 2017 21:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TerryTsao/83cb76dba8252c283dd1c611f45c1f3c to your computer and use it in GitHub Desktop.
Save TerryTsao/83cb76dba8252c283dd1c611f45c1f3c to your computer and use it in GitHub Desktop.
Spring MVC "must knows"

Spring flowchart

                            HandlerMapping 
                                  ^|
                                  ||
                                1 || 2
                                  ||
                  -> 0            |V                     -> 3
      Browser   <========>  FrontController   <========================>   Controller
                  9 <-        ^|      ^|                 4 <-
                              ||      ||
                             8||7    6||5
                              ||      ||
                              |V      |V
                             View   ViewResolver

web.xml

define FrontController(DispatcherServlet)

dispatch-servlet.xml

define HandlerMapping, ViewResolver

Annotation: @Controller, @RequestMapping

Add <context:component-scan base-package="your.controller.package" />
@Controller
public class HelloController {

    @RequestMapping("/hello")
    public ModelAndView helloWorld() {
        ModelAndView model = new ModelAndView("HelloPage");
        model.addObject("msg", "hello world");

        return model;
    }
}

@Controller
@RequestMapping("/greeting")
public class HelloController {

    @RequestMapping("/hi")
    public ModelAndView helloWorld() {
        ModelAndView model = new ModelAndView("HelloPage");
        model.addObject("msg", "hello world");

        return model;
    }
}

Annotation: @PathVariable

Add <mvc:annotation-driven /> for the latter case
@Controller
public class HelloController {

    @RequestMapping("/hello/{userName}")
    public ModelAndView helloWorld(@PathVariable("userName") String name) {
        ModelAndView model = new ModelAndView("HelloPage");
        model.addObject("msg", "hello world" + name);

        return model;
    }
}

@Controller
public class HelloController {

    @RequestMapping("/hello/{country}/{userName}")
    public ModelAndView helloWorld(@PathVariable Map<String, String> vars) {
        ModelAndView model = new ModelAndView("HelloPage");
        
        // TODO: Here you can use the Map vars

        return model;
    }
}

Annotation: @RequestParam

// Example use for POST method
@Controller
public class StudentController {

    @RequestMapping(value="/url", method = RequestMethod.POST)
    public ModelAndView submitForm(@RequestParam(value="name", defaultValue="TerryTsao") String name) {
        // TODO: do sth with name
    }

    // or use map
}

Annotation: @ModelAttribute

@Controller
public class StudentController {

    @RequestMapping(value="/url", method = RequestMethod.POST)
    public ModelAndView submitForm(@ModelAttribute("student") Student student) { // Param level
        // TODO: do sth with student
    }

    @ModelAttribute
    public void addCommonObj(Model model) {
        model.addAttribute("headerMsg", "This is the header");
    }
}

BindingResult

@Controller
public class StudentController {

    @RequestMapping(value="/url", method = RequestMethod.POST)
    public ModelAndView submitForm(@ModelAttribute("student") Student student,
            BindingResult result) { // Param level
        if(result.hasErrors()) {
            // do Sth;
        }
    }
}
on jsp
<form:errors path="student.*" />   <!-- use taglib -->

@Annotation: InitBinder, CustomDateEditor, CustomPropertyEditor

public class SomeController {
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.setDisallowedFields(new String[] {"someMembersHere"});
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy***MM***dd");
        binder.registerCustomEditor(Date.class, "studentDOB", new CustomDateEditor(dateFormat, false));
        binder.registerCustomEditor(String.class, "studentName", new StudentNameEditor());
    }
}

public class StudentNameEditor extends PropertyEditorSupport {
    @Override
        public void setAsText(String studentName) throws IllegalArgumentException {
            // if/else do sth
        }
}

Form Validation(JSR 303/349)

Library dependency

Annotation: @Valid, @Size, Custom message, @Pattern, @Past, @Max, custom annotation

public class SomeController {
    public ModelAndView someMethod(@Valid @ModelAttribute("student") Student student) {
        // do sth
    }
}

public class Student {

    // message param no longer needed due to configuration below
    @Size(min=2, max=30/*, message="some msg with {min} and {max}"*/) @IsValidHobby(listOfValidHobbies="someHobbies")
    private String studentHobby;

    @Pattern(regexp="[^0-9]*")
    private String studentName;

    @Past
    private Date studentDOB;

    @Max(2048)
    private Long someNum;
}

@Documented
@Constraint(validateBy = HobbyValidator.class)
@Target( { ElementType.FIELD } )
@Retention(RetentionPolicy.RUNTIME)
public @interface IsValidHobby {

    // an example customization
    String listOfValidHobbies() default "Soccer|Game|blahblah";
    
    // Default message
    String message() default "Please provide a valid Hobby ...";

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

    Class<? extends Payload>[] payload() default {};
}

public class HobbyValidator implements ConstraintValidator<IsValidHobby, String> {

    private String someHobbies;

    @Override
    public void initialize(IsValidHobby isValidHobby) {
        someHobbies = IsValidHobby.listOfValidHobbies();
    }

    @Override
    public boolean isValid(String studentHobby, ConstraintValidatorContext ctx) {
        // do stuff to return a boolean; you can use someHobbies
    }
}

Using spring MessageSource

for servlet.xml, add
<bean id="messageSource"
    class="org.springframework.context.support.ReloadableResourceBundleMessageSource">

    <property name="basename" value="/WEB-INF/studentmessages" />
</bean>
add studentmessages.properties
Size.student.studentHobby = some msg here about {0} with {1} {2} in alphabetical order.
public class SomeController {
    public ModelAndView someMethod(@Valid @ModelAttribute("student") Student student) {
        // do sth
    }
}

public class Student {

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

Interceptor

added to servlet.xml
<mvc:interceptors>
    <bean class="demo.terrytsao.controller.SomeInterceptor" />
</mvc:interceptors>
public class SomeInterceptor extends HandlerInterceptorAdaptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object Handler) throws Exception {

        // if return true, app will further handle request
        // if return false, app will not further handle request
    }

    @Override
    public boolean postHandle(HttpServletRequest request,
            HttpServletResponse response, Object Handler,
            ModelAndView model) throws Exception {

        // this method would be called after Spring MVC
        // executes the request handler method for the request
    }

    @Override
    public boolean afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object Handler,
            Exception ex) throws Exception {

        // this method would be called after the response object 
        // is produced by the view for the request
    }
}

Internationalization

for jsp of view, you can use taglib to put labels in the code
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<spring:message code="label.studentName" />
for studentmessages_en.properties and studentmessages_fr.properties
label.studentName = some thing here
for servlet.xml add beans, so that we can use siteLanguage in the url in the form "?siteLanguage=en"
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    <property name="paramName" value="siteLanguage"/>
</bean>

<bean id="LocaleResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>
    <property name="defaultLocale" value="en" />
</bean>

Support multiple themes

add in servlet.xml
<bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource">
    <property name="basenamePrefix" value="theme-" />
</bean>

<bean id="themeResolver" class="org.springframework.web.servlet.theme.CookieThemeResolver">
    <property name="defaultThemeName" value="green" />
</bean>


<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor">
    <property name="paramName" value="siteTheme" />
</bean>
add in jsp file
<link rel="stylesheet" href="<spring:theme code='styleSheet' />" type="text/css" />
add theme-red.properties and stylered.css
styleSheet=/path/to/css

Exception Handling

public class SomeController {

    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = NullPointerException.class)
    public String handleNullPointerException(Exception ex) {
        // do sth
    }
}

@ControllerAdvice
public class GlobalExceptionHandler {
    // @ExceptionHandler like above
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment