Spring MVC Intro / Gore - Nov NHJUG

  • Published on
    12-Jan-2015

  • View
    3.733

  • Download
    3

Embed Size (px)

DESCRIPTION

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

Transcript

<ul><li> 1. Spring MVC Introduction / Gore Ted Pennings NHJUG Co-Founder16 November 2010 </li></ul> <p> 2. Whos Ted Developer with Fortune 100 nancial services rm 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. 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. 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 ght MVC code separation. If you feel the need to break convention, refactor. 5. MVC Done Badly Views Doing actual work in JSPs </p> <div>Victim List Adapted from http://www.roseindia.net/jsp/Accessingdatabase-fromJSP.shtml 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("Please enter your information"); sb.append("<p>Victim Name") .append("").append("</p>"); [...] return sb.toString(); } 7. Speaking of Bad Things Oracle CEO Larry Ellison 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 &lt; 4) { throw new VictimUnworthyException(Too few yachts. Larry Ellison will not be sated.); } } [. . . ] } HTML in methods public String toString() { return Why Hello + this.name + !<br />I admire your+ + this.numberOfYachts + yachts; } 9. Spring MVC is Still Spring Full Dependency Injection and Inversion of Control XML- and Annotation-based conguration Heavy use of annotation-based conguration Application context les are still available, but mostly unnecessary and ess Maven dependencies and archetypesdn G oo ri ng Convention over conguration p S Very few interfaces to implement 10. The Basic Annotations Spring MVC is mostly annotation driven, so here is your vernacular: Standard Spring annotations: @Component, @Service, @Repository, @Autowired, @Value, @Conguration @Transactional, @Aspect and JSR 284 + 330 @Controller an MVC Controller @RequestMapping @ModelAttribute @RequestParam and @PathVariable @SessionAttributes 11. How does Spring do MVC? All requests are sent to Springs DispatcherServlet DispatcherServlet sends requests to @Controllers Diagram from Spring docs on their website THERE ARE NO DEVELOPER-WRITTEN SERVLETS 12. The Simplicity of Controllers @Controller makes a class a controller bean Specialization of @Component @RequestMapping denes the URL paths handled by acontroller or method. It is possible to nest paths, as in example. Many different RequestMethods can be serviced. {} dene @PathVariable/@ReqParam Value of Inheritance Annotation caveat 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. 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 Springs Converter SPI Use @InitBinder in a controller superclass JSON payloads can often be unmarshalled on the y. 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. Creating Forms (1/2) Creating a new object submission form requires manually instantiating yourtarget 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 thenext step getter/setter round-tripping on POST 17. Creating Forms (2/2) Project Code Hours Submit Most new object views can function as an edit view. Conserve code. 18. Demo Working forms Bean validation Error messages appear in browser automagically 19. Bean Validation (JSR-303) Constraints are dened by @Entity public class Yacht {annotations on the actual data @Id @GeneratedValue(strategy = GenerationType.AUTO)entities private Long id; Validation is executed @Size(min = 4, max = 35, message = "Key must be between {min} and {max} characters long.")automagically by framework@NotEmpty(message = "Key is required.")@Pattern(regexp = "[A-Za-z0-9_-]*", message = "Only letters, numbers, underscores and hyphens may be used.") User-friendly errors appearprivate String key; automagically on the view. @Size(min = 4, max = 35, message = "Name must be between {min} and {max} characters long.") Object graphs can be traversed@NotEmpty(message = "Name is required.") private String name;and nested objects are validated @ValidDate(or not, if you wish). private Date acquisitionDate;} 20. Setting Up And Using Bean Validation As simple as @Valid Can be applied inside domain to validate child/2nd degreeobjects 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. 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. Handling Validation Errors Ensure that you have a BindingResult.hasErrors() check in @Controller When returning edit view, ensure all needed model attributes arepresent 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 eld-level). Be sure you interrupt ow so you dont 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. Writing Your Own Constraints Constraints can be combined and composed For example, @NotEmpty actually means @NotNull, @Size(min=1) &amp; @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 le (Internationalization/i18n) Write a validator Implement ConstraintValidator Return boolean for isValid(TargetClass object, ) Statefulness (via initialize() method) Dependency Injection, Constructors, and Hibernate ORM issue 24. Writing Unit Tests For ValidationMagic A lot of JSR-303 is wizardry and magic beans. Write unit tests so you ensure code execution is predictable. Easiest to write using Springs JUnit Test Runner Point to an application context eld that contains Ensure a JSR-303-compliant validator is on your testclasspath Eg, Hibernate Validator 25. Example Unit Test@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration({ "classpath:your-persistence.xml","classpath:your-persistence-test.xml" })public class YachtValidationTest { @Autowiredprivate javax.validation.Validator validator; private Yacht emptyYacht; @Beforepublic void setUpEmptyYacht() {emptyYacht = new Yacht();} @Testpublic void theKeyFieldIsRequired() { Set&gt; constraintViolations = validator.validate(emptyYacht); boolean containsYachtKeyViolation = false; for (ConstraintViolation violation : constraintViolations) {if ("key".equals(violation.getPropertyPath().toString())&amp;&amp; violation.getMessageTemplate().contains("required")) {containsYachtKeyViolation = true;}} assertTrue(containsYachtKeyViolation);}} 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. 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 youve drank plenty of convention over congurationkoolaid. 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. 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. 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 andtemplating org.springframework.web.servlet.view.tiles2.TilesViewResolver org.springframework.web.servlet.view.tiles2.TilesCongurer Similar options are available for Freemarker, Velocity and others JSON can be rendered easily via MappingJacksonJsonView Dont forget ContentNegotiatingViewResolver and ignoreAcceptHeaders(true) Straight JSPs are possible too Convention over conguration, but often creates complexity/duplication 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-specicexceptions, such as an IOException in a particular method. This onlyapplies to the controller in which it is declared. Consider adding web.xml handlers too (if your exceptionhandler throws an exception, or if you think everything may break.) A separate error @Controller is a good idea, with minimaldependencies 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 @Cong Add enctype=multipart/form-data to your tag in your view Add a MultipartFile to your POST @Controller method argument Use @ModelAttribute for form submission specicity MultipartFile is your friendgetBytes()*getContentType() dont forget about mime types!getOriginalFilename()transferTo(File dest)*(* but IOException is not) 32. Burrowing Down To TheServlets Sometimes it is necessary to write output@Componentpublic class ByteArrayView extends AbstractView {directly to servlets.public ByteArrayView() { } Extend AbstractView@Overrideprotected final void renderMergedOutputModel(Map In your controller, specify your custommodel, HttpServletRequest request, HttpServletResponseviewresponse)ModelAndView.setViewName(String throws Exception { name) byte[] bytes = (byte[]) model.get("data");ModelAndView.setView(View view) String contentType = (String) model.get("contentType"); This is useful for user data in binary form,response.setContentType(contentType);such as images. response.setContentLength(bytes.length); In general, avoid this approachServletOutputStream out =response.getOutputStream();FileCopyUtils.copy(bytes, out);}} 33. URL Rewriting URL rewriting implemented as a servlet-level lter (web.xml): org.tuckey.web.lters.urlrewrite.UrlRewriteFilter Uses a urlrewrite.xml le, typically in the WEB-INF: /sun/*/oracle/$1 Rules are standard regular expressions (java.util.regex.Pattern). Used extensively to separate dynamic content requests from static contentrequests (eg, css, images, javascript). Can be used to route multiple different request paths to the sameDispatcherServlet instance (thereby conserving memory). 34. Demo Creating a new project from the Maven archetype/template in STS IDE maven-tomcat-plugin, maven-jetty-plugin Live coding! 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. Questions? Comments? </div>