spring 3: what's new

Post on 13-Jan-2015

1.976 Views

Category:

Technology

9 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

Spring 3An overview of changes from version 2.5

Ted Pennings

8 December 2010

Overview of Changes

Annotations

Fewer interfaces

MVC overhaul

Dropped binary compatibility with Java 1.4

Java 5 generics, for(), autoboxing, etc

Spring 3 = AnnotationsSpring 3 emphasizes annotation config

@Component, @Configuration, etc

JSR 250 Common Annotations

JSR 299/330 Bean Injection

JSR 303 Bean Validation

Java Persistence API (JPA)

Enabling Annotation Configuration

<context:component-scan base-package=”com.foo.bar”>

<mvc:annotation-driven>

also, <aspectj:auto-proxy /> for @Aspects

Annotation Config Example

<?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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<!-- Scans within the base package of the application for @Components to configure as beans --> <context:component-scan base-package="com.tedpennings.bu.cs667" /> <mvc:annotation-driven />

</beans>

Annotation Taxonomy

@Component and its specializations

@Service, @Repository

@Controller

@Configuration + @Bean

@Autowired

@Value

@Component Example

@Component("rssView")public class RomeRssView extends AbstractView {

@Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { doStuff();

}

}

@Service Example@Service

public class TwitterStatusProvider implements StatusProvider {

@Autowired private CacheRepository cache;

@Override public String getStatus(boolean skipCache) { String status = cache.findString(CACHE_KEY); if (skipCache || status == null) { status = getStatusFromTwitter(); cache.cacheString(CACHE_KEY, status, DEFAULT_CACHE_EXPIRY); } return status; }

}

@Configuration Classes

Java classes that do the work of XML Application Context files

Allow much more control over object creation

Instantiated before XML beans

@Configuration @Beans can be referenced by XML beans/config

Can also reference XML beans

@Config Example@Configuration

public class MongoConfiguration {

@Value("${db.host}") private String dbHost;

@Value("${db.port}") private int dbPort;

@Bean @Lazy public Mongo mongo() throws UnknownHostException { return new Mongo(dbHost, dbPort); }

}

MVC ChangesController interfaced dropped

@RequestMapping instead of XML config

@Controller instead of explicit XML config

Lots of return types possible

Simplicity

The Simplicity of Controllers

@Controller makes a class a controller “bean”Specialization of @Component

@RequestMapping defines the URL paths handled by a class and/or method.

It is possible to nest paths, as in example on next slide.Many different RequestMethods allowed in a @Controller {} in @RM path define @PathVariable/@ReqParam

Value of InheritanceAnnotation caveat

@Controller Example@Controller

@RequestMapping({ "/yacht", "/yachts", "/mah-boats" })public class YachtController { @Autowired private YachtService service;

private static final String YACHT_PAGE_VIEW = "yachts/view";

@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; }

}

MVC Annotations

@Controller – an MVC Controller

@RequestMapping

@ModelAttribute

@RequestParam and @PathVariable

@SessionAttributes

DemoAnnotation Config

Working forms

Bean validation (JSR-303)

Controller Method Return Types

@Controller methods can return many different types. Often:ModelModelAndView (use Model.setView(View) or Model.setViewName(String))

Distinction between View and View NameString 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

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).

@Entitypublic 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;

}

Setting Up And Using Bean Validation

As simple as @ValidCan be applied inside domain to validate child/2nd degree objects

BindingResult for errorsMUST be argument after @Valid argument (quirk)RuntimeException if nottoString() is your friend.

Validating a large object graph is risky.Complicates future changesOn the other hand, very hard to merge BindingResults

Updated Controller@Controllerpublic 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);}

}

Handling Validation Errors

Ensure that you have a BindingResult.hasErrors() check in @ControllerWhen returning edit view, ensure all needed model attributes are presentThis 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 objectsVALIDATION ERRORS ARE NOT EXCEPTIONS

There is a Hibernate option to validate pre-persist, but this is nuancedLegacy objectsMay be incompatible with Spring-managed dependencies

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 validatorImplement ConstraintValidator<AnnotationType, TargetClass>Return boolean for isValid(TargetClass object, …)Statefulness (via initialize() method)Dependency Injection, Constructors, and Hibernate ORM issue

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 RunnerPoint to an application context field that contains

<bean id="validator“ class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />

Ensure a JSR-303-compliant validator is on your test classpath

Eg, Hibernate Validator

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); }}

Even More Annotations

JSR 250 Resource Management

JSR 299/330 Bean Injection

JPA!

JSR 250 Resource Management

@Resource (typically fields)

@PostConstruct (method)

Replaces InitializingBean + afterPropertiesSet()

@PreDestroy (method)

Replaces DisposableBean + destroy()

Bean Injection JSRjavax.inject package:

@Inject (equivalent to @Autowired)

@Qualifier (to resolve ambiguities)

@Named

@Scope

@Singleton

JPA Annotations

@PersistenceContext / @PersistenceUnit

@Entity

@Column, @Id, @Enumerated, @ManyToOne, etc

Mixes well with Spring-tx and @Transactional

Basic JPA Configuration in Spring 3.0

PersistenceAnnotationBeanPostProcessor for @PersistenceContext/Unit EntityManagers

LocalContainerEntityManagerFactoryBean to bootstrap JPA and read persistence.xml

Still need to configure provider, eg, Hibernate

Need to provide data source, either as a constructed bean or JNDI reference

Hibernate as JPA Provider

HibernateJpaVendorAdapter + Properties <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true" /> <property name="generateDdl" value="true" /> <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" /> </bean>

<bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="locations"> <list> <value>classpath:hibernate.properties</value> </list> </property> </bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/yourdb" /> <property name="username" value="user" /> <property name="password" value="pass" /> </bean>

hibernate.properties:

hibernate.hbm2ddl.auto=updatehibernate.connection.autocommit=truehibernate.show.sql=truehibernate.generate_statistics=truejavax.persistence.validation.mode=ddlhibernate.dialect=org.hibernate.dialect.MySQL5Dialect

Questions?

http://ted.pennin.gs /ted@pennin.gs

@thesleepyvegan on twitter

not always Java related

top related