Spring MVC Intro / Gore - Nov NHJUG


Published on

A Spring MVC overview, followed by the important guts and gore.

Published in: Technology, Education
  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Spring MVC Intro / Gore - Nov NHJUG

  1. 1. Spring MVC Introduction / Gore Ted Pennings NHJUG Co-Founder 16 November 2010
  2. 2. Who’s Ted ✦ Developer with Fortune 100 financial services firm since July 2008. ✦ Working with Java since late 2008. ✦ Web developer as freelance/hobby since 2001 (LAMP) ✦ Lots of freelancing and web hosting during college ✦ http://ted.pennin.gs / @thesleepyvegan / ted@pennin.gs
  3. 3. What is MVC? ✦ MVC spits code into three areas ✦ Model – data/persistence layer ✦ View – presentation layer, UI and client-side scripts ✦ Controller – business logic that controls data and execution ✦ Strong design pattern that emphasizes separation.
  4. 4. MVC Cleanliness ✦ Application logic goes in the controllers. ✦ Data persistence goes in the model. ✦ UI presentation and JavaScript varnish goes in the views. ✦ Do not try to fight MVC code separation. If you feel the need to break convention, refactor.
  5. 5. MVC Done Badly – Views ✦Doing actual work in JSPs <% try { String url="jdbc:mysql://localhost/companies?user=larry_ellison&password=yachts"; con=DriverManager.getConnection(url); stmt=con.createStatement(); } catch(Exception e){ System.err.println(“Unable to acquire companies!”); e.printStackTrace(); } if (request.getParameter("action") != null){ String bookname = request.getParameter("company_name"); String author = request.getParameter("price"); stmt.executeUpdate("insert into acquisitions(company_name,price) values ('"+company_name+"','"+price+"')"); rst=stmt.executeQuery("select * from acquisitions"); %> <html> <body> <center> <h2>Victim List</h2> <table border="1" cellspacing="0" cellpadding="0"> Adapted from http://www.roseindia.net/jsp/Accessingdatabase-fromJSP.shtml
  6. 6. MVC Done Badly – Controllers ✦HTML in the controllers @RequestMapping(“/acquisitions/new”) public void createAcquisition(Model model) { Acquisition v = new Acquisition(); model.addAttribute(“newVictim”, v); model.addAttribute(“newVictimForm”, collectVictimVitalStats(v)); } private String renderVictimInfoForm(Acquisition v) { StringBuilder sb = new StringBuilder() .append("<form action='").append(SERVLET_PATH) .append("' method='post'><h1>Please enter your information</h1>"); sb.append("<p><label for='name'>Victim Name</label>") .append("<input type='text' name='name' value='") .append(v.getName()).append("'/>").append("</p>"); [...] return sb.toString(); }
  7. 7. Speaking of Bad Things Oracle CEO Larry Ellison
  8. 8. MVC Done Badly – Models ✦Business logic in the model public class Victim { private String name; private short numberOfYachts; [. . . ] public Victim(String name, short numberOfYachts) { if (numberOfYachts < 4) { throw new VictimUnworthyException(“Too few yachts. Larry Ellison will not be sated.”); } } [. . . ] } ✦HTML in methods public String toString() { return “<h3 class=‘name’>Why Hello ” + this.name + “!</h3><br/>I admire your” + “<strong><font color=“pink”>” + this.numberOfYachts + “</font></strong> yachts”; }
  9. 9. Spring MVC is Still Spring ✦ Full Dependency Injection and Inversion of Control ✦ XML- and Annotation-based configuration ✦ Heavy use of annotation-based configuration ✦ Application context files are still available, but mostly unnecessary ✦ <mvc:annotation-driven> and <content:component-scan> ✦ Maven dependencies and archetypes ✦ Convention over configuration ✦ Very few interfaces to implement “Spring Goodness”
  10. 10. The Basic Annotations ✦ Spring MVC is mostly annotation driven, so here is your vernacular: ✦ Standard Spring annotations: ✦ @Component, @Service, @Repository, ✦ @Autowired, @Value, @Configuration ✦ @Transactional, @Aspect and JSR 284 + 330 ✦ @Controller – an MVC Controller ✦ @RequestMapping ✦ @ModelAttribute ✦ @RequestParam and @PathVariable ✦ @SessionAttributes
  11. 11. How does Spring do MVC? ✦ All requests are sent to Spring’s DispatcherServlet ✦ DispatcherServlet sends requests to @Controllers ✦ THERE ARE NO DEVELOPER-WRITTEN SERVLETS Diagram from Spring docs on their website
  12. 12. The Simplicity of Controllers ✦ @Controller makes a class a controller “bean” ✦ Specialization of @Component ✦ @RequestMapping defines the URL paths handled by a controller or method. ✦ It is possible to nest paths, as in example. ✦ Many different RequestMethods can be serviced. ✦ {} define @PathVariable/@ReqParam ✦ Value of Inheritance ✦ Annotation caveat
  13. 13. Controller Example @Controller @RequestMapping({ "/yachts", "/mah-boats" }) public class YachtController extends BaseController { @RequestMapping(value = "/{yachtKey}", method = GET) public String displayYacht( @PathVariable(“yachtKey”) String yachtKey, Model model) { LOG.debug("Displaying Yacht " + yachtKey); Yacht yacht = service.findYachtByKey(yachtKey); model.addAttribute("yacht", yacht); return YACHT_PAGE_VIEW; } }
  14. 14. Reading User Input ✦ Typically happens through method parameter annotations ✦ @PathVariable(“var”) / @RequestParam(“var”) ✦ Corresponds to a {var} value in a @RequestMapping ✦ Best practice to be explicit ✦ @ModelAttribute ✦ @RequestBody ✦ Any type can be used. ✦ Be wary of type conversion issues ✦ See PropertyEditorSupport and Spring’s Converter SPI ✦ Use @InitBinder in a controller superclass ✦ JSON payloads can often be unmarshalled on the fly.
  15. 15. Populating The Model ✦ In MVC, dynamic data only appears in views through the model. ✦ Spring MVC enforces this pattern constraint. ✦ Each request receives a fresh new Model (or equivalent). ✦ Two basic approaches ✦ In @Controller method, receive a Model or ModelAndView object ✦ Add directly to it. ✦ No need to return Model/MV object received. (More on that later.) ✦ In @Controller class, annotate methods with @ModelAttribute(“name”) ✦ Used in a @Controller superclass, can be used for template data ✦ User privileges ✦ Pseudo-security hack until full security is implemented
  16. 16. Creating Forms (1/2) ✦ Creating a new object submission form requires manually instantiating your target object ✦ if you want to create a com.oracle.acquisition.timekeeper.model.TimeEntry(), in your controller method you must ✦ model.addAttribute(“timeEntry”, new com.oracle.acquisition.timekeeper.model.TimeEntry()); Assuming: package com.oracle.acquisition.timekeeper.model; public class TimeEntry { private String victimName; private String projectCode; private double hours; private Date timestamp; […getters, setters, etc…] } ✦ This exact object will be used by your JSP (or similar) in the next step… getter/setter round-tripping on POST
  17. 17. Creating Forms (2/2) Most new object views can function as an edit view. Conserve code. <form:form method="post" modelAttribute=“timeEntry" cssClass="form"> <form:label path=“projectCode"> Project Code <form:errors path=“projectCode" cssClass="error" /> </form:label> <form:select items="${userProjectCodes}" path=“projectCode“ itemLabel=“projectName"/> <form:label path=“hours"> Hours <form:errors path=“hours" cssClass="error" /> </form:label> <form:input path=“hours“ /> <button type="submit">Submit</button> </form:form>
  18. 18. Demo ✦ Working forms ✦ Bean validation ✦ Error messages appear in browser automagically
  19. 19. Bean Validation (JSR-303) ✦ Constraints are defined by annotations on the actual data entities ✦ Validation is executed automagically by framework ✦ User-friendly errors appear automagically on the view. ✦ Object graphs can be traversed and nested objects are validated (or not, if you wish). @Entity public class Yacht { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Size(min = 4, max = 35, message = "Key must be between {min} and {max} characters long.") @NotEmpty(message = "Key is required.") @Pattern(regexp = "[A-Za-z0-9_-]*", message = "Only letters, numbers, underscores and hyphens may be used.") private String key; @Size(min = 4, max = 35, message = "Name must be between {min} and {max} characters long.") @NotEmpty(message = "Name is required.") private String name; @ValidDate private Date acquisitionDate; }
  20. 20. Setting Up And Using Bean Validation ✦ As simple as @Valid ✦ Can be applied inside domain to validate child/2nd degree objects ✦ BindingResult for errors ✦ MUST be argument after @Valid argument (quirk) ✦ RuntimeException if not ✦ toString() is your friend. ✦ Validating a large object graph is risky. ✦ Complicates future changes ✦ On the other hand, very hard to merge BindingResults
  21. 21. Updated Controller @Controller public class YachtController { @RequestMapping(method = POST) public ModelAndView saveYacht(@Valid Yacht yacht, BindingResult result, ModelAndView mv) { LOG.debug("Attempting to save Yacht: " + yacht.getKey()); if (result.hasErrors()) { LOG.debug("Submission has errors " + result); mv.setViewName(MODIFY_YACHT_VIEW); return mv; } service.store(yacht); FlashMap.setSuccessMessage("Successfully saved Yacht"); return createRedirect(yacht); } }
  22. 22. Handling Validation Errors ✦ Ensure that you have a BindingResult.hasErrors() check in @Controller ✦ When returning edit view, ensure all needed model attributes are present ✦ This includes the object being validated if you construct a new Model/ MV ✦ You may need a call to the root-level form:errors in order to capture object- level errors (not field-level). ✦ <form:errors path=“” /> ✦ Be sure you interrupt flow so you don’t persist invalid objects ✦ VALIDATION ERRORS ARE NOT EXCEPTIONS ✦ There is a Hibernate option to validate pre-persist, but this is nuanced ✦ Legacy objects ✦ May be incompatible with Spring-managed dependencies
  23. 23. Writing Your Own Constraints ✦ Constraints can be combined and composed ✦ For example, @NotEmpty actually means @NotNull, @Size(min=1) & @ReportAsSingleViolation ✦ Write an annotation class ✦ @Target(ElementType.FIELD) ✦ @Retention(RetentionPolicy.RUNTIME) ✦ @Documented (or not) ✦ @Constraint(validatedBy = YourCustomValidator.class) ✦ Be sure to add a default message ✦ These can be Java properties read from a file (Internationalization/ i18n) ✦ Write a validator ✦ Implement ConstraintValidator<AnnotationType, TargetClass> ✦ Return boolean for isValid(TargetClass object, …) ✦ Statefulness (via initialize() method) ✦ Dependency Injection, Constructors, and Hibernate ORM issue
  24. 24. Writing Unit Tests For Validation Magic ✦ A lot of JSR-303 is wizardry and magic beans. ✦ Write unit tests so you ensure code execution is predictable. ✦ Easiest to write using Spring’s JUnit Test Runner ✦ Point to an application context field that contains ✦ <bean id="validator“ class="org.springframework.validation.beanvalidation.LocalVa lidatorFactoryBean" /> ✦ Ensure a JSR-303-compliant validator is on your test classpath ✦ Eg, Hibernate Validator
  25. 25. Example Unit Test @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({ "classpath:your-persistence.xml", "classpath:your-persistence-test.xml" }) public class YachtValidationTest { @Autowired private javax.validation.Validator validator; private Yacht emptyYacht; @Before public void setUpEmptyYacht() { emptyYacht = new Yacht(); } @Test public void theKeyFieldIsRequired() { Set<ConstraintViolation<Yacht>> constraintViolations = validator .validate(emptyYacht); boolean containsYachtKeyViolation = false; for (ConstraintViolation<Yacht> violation : constraintViolations) { if ("key".equals(violation.getPropertyPath().toString()) && violation.getMessageTemplate().contains("required")) { containsYachtKeyViolation = true; } } assertTrue(containsYachtKeyViolation); } }
  26. 26. The Guts of Spring MVC ✦ Method Return Types ✦ View Resolvers ✦ Exception Handling ✦ File Uploads ✦ Servlet-level code (if you have to) ✦ URL Rewriting
  27. 27. Controller Method Return Types ✦ @Controller methods can return many different types. Often: ✦ Model ✦ ModelAndView (use Model.setView(View) or Model.setViewName (String)) ✦ Distinction between View and View Name ✦ String for the view name to display ✦ Can return void if you’ve drank plenty of convention over configuration koolaid. ✦ Dangerous if you refactor a lot (or the next person does). ✦ Methods can also be annotated with @RequestBody ✦ Used to return a value without going through MVC apparatus ✦ Generally bad form, but good for testing or mock apps. ✦ @ModelAttribute has the same two distinct meanings as @ModelAttribute
  28. 28. View Resolvers ✦ What is a ViewResolver and how does this differ from a View Name? ✦ Apache Tiles and TilesViewResolver ✦ InternalResourceViewResolver (default) ✦ ContentNegotiatingViewResolver ✦ JSON ✦ XML (if needed) ✦ RSS/Atom ✦ And many, many others… ViewResolver overload….
  29. 29. View options and Apache Tiles ✦ Spring MVC is does not attempt to implement its own view layer ✦ JSTL and JSP/JSPX ✦ Apache Tiles is commonly used for page modularization and templating ✦ org.springframework.web.servlet.view.tiles2.TilesViewResolver ✦ org.springframework.web.servlet.view.tiles2.TilesConfigurer ✦ Similar options are available for Freemarker, Velocity and others ✦ JSON can be rendered easily via MappingJacksonJsonView ✦ Don’t forget ContentNegotiatingViewResolver and ignoreAcceptHeaders (true) ✦ Straight JSPs are possible too ✦ Convention over configuration, but often creates complexity/duplication
  30. 30. Handling Exceptions ✦ Implement a HandlerExceptionResolver for application-wide errors public interface HandlerExceptionResolver { ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex); } ✦ @ExceptionHandler can annotate a method for @Controller-specific exceptions, such as an IOException in a particular method. This only applies to the controller in which it is declared. ✦ Consider adding web.xml <error-page> handlers too (if your exception handler throws an exception, or if you think everything may break.) ✦ A separate error @Controller is a good idea, with minimal dependencies
  31. 31. File Uploads ✦ Uses Commons File Upload ✦ org.springframework.web.multipart.commons.CommonsMultipartResolver ✦ Three easy steps (plus background Spring magic) ✦ Instantiate CommonsMultipartResolver in app context or @Config ✦ Add enctype=“multipart/form-data” to your <form> tag in your view ✦ Add a MultipartFile to your POST @Controller method argument ✦ Use @ModelAttribute for form submission specificity ✦ MultipartFile is your friend ✦ getBytes()* ✦ getContentType() – don’t forget about mime types! ✦ getOriginalFilename() ✦ transferTo(File dest)* (* but IOException is not)
  32. 32. Burrowing Down To The Servlets ✦ Sometimes it is necessary to write output directly to servlets. ✦ Extend AbstractView ✦ In your controller, specify your custom view ✦ ModelAndView.setViewName(String name) ✦ ModelAndView.setView(View view) ✦ This is useful for user data in binary form, such as images. ✦ In general, avoid this approach @Component public class ByteArrayView extends AbstractView { public ByteArrayView() { } @Override protected final void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { byte[] bytes = (byte[]) model.get("data"); String contentType = (String) model.get ("contentType"); response.setContentType(contentType); response.setContentLength(bytes.length); ServletOutputStream out = response.getOutputStream(); FileCopyUtils.copy(bytes, out); } }
  33. 33. URL Rewriting ✦ URL rewriting implemented as a servlet-level filter (web.xml): ✦ org.tuckey.web.filters.urlrewrite.UrlRewriteFilter ✦ Uses a urlrewrite.xml file, typically in the WEB-INF: <rule> <from>/sun/*</from> <to>/oracle/$1</to> </rule> ✦ Rules are standard regular expressions (java.util.regex.Pattern). ✦ Used extensively to separate dynamic content requests from static content requests (eg, css, images, javascript). ✦ Can be used to route multiple different request paths to the same DispatcherServlet instance (thereby conserving memory).
  34. 34. Demo ✦ Creating a new project from the Maven archetype/template in STS IDE ✦ maven-tomcat-plugin, maven-jetty-plugin ✦ Live coding!
  35. 35. References ✦ Spring Docs (3.0.x) Manual: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/ JavaDocs: http://static.springsource.org/spring/docs/3.0.x/javadoc-api/ ✦ Maven Jetty Plugin (mvn jetty:run) http://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin ✦ Miscellaneous ✦ Spring Roo Keynote http://www.youtube.com/watch?v=GQHlhIIxCIc
  36. 36. Questions? Comments?