Modern Component Design with Spring

Preview:

DESCRIPTION

Speaker: Juergen Hoeller In recent years, the Spring Framework focuses on flexible and powerful Java-based configuration. This talk presents Spring as an annotation-oriented application framework, illustrating the rich set of features that Spring has to offer for modern component design. General component model trends will be discussed along with selected Spring feature highlights, such as standardized annotations for dependency injection, stereotypes, and declarative services such as validation and scheduling.

Citation preview

© 2013 SpringOne 2GX. All rights reserved. Do not distribute without permission.

Modern Component Design with Spring

Juergen Hoeller

Overview: Spring Component Model Themes● Powerful annotated component model● Custom annotations through composition● Configuration classes and factory methods● Spring Expression Language● Flexible MVC/REST controller style● Declarative validation and formatting● Declarative scheduling and caching

Bootstrapping Your Annotated Components● Typical: XML bean definition file

– <context:component-scan base-package=”com.myapp”/>– @Component / @Repository / @Service / @Configuration stereotype– compare: JPA persistence.xml with @Entity classes

● Alternative: AnnotationConfigApplicationContext– scan(basePackage)– register(componentClass)– register(configurationClass)

A Typical Annotated Component@Servicepublic class MyBookAdminService implements BookAdminService { @Autowired public MyBookAdminService(AccountRepository ar) { ... }

@Transactional public BookUpdate updateBook(Addendum addendum) { ... }}

A More Specific Annotated Component@Service@Scope("session")@Primarypublic class MyBookAdminService implements BookAdminService { @Autowired @Qualifier("production") public MyBookAdminService(@Lazy AccountRepository ar) { ... }

@Transactional(readOnly=false, timeout=10) public BookUpdate updateBook(Addendum addendum) { ... }}

Composable Stereotype Model@Service@Scope("session")@Primary@Transactional(rollbackFor=Exception.class)@Retention(RetentionPolicy.RUNTIME)public @interface MyService {}

@MyServicepublic class MyBookAdminService { ...}

Custom Scoping Annotations@Scope(value="session", proxyMode=ScopedProxyMode.INTERFACES)@Retention(RetentionPolicy.RUNTIME)public @interface MySessionScoped {}

@Scope(value="session")@Retention(RetentionPolicy.RUNTIME)public @interface MySessionScoped { ScopedProxyMode proxyMode() default ScopedProxyMode.NO;}

Custom Transaction Annotations@Transactional(readOnly=false, timeout=10)@Retention(RetentionPolicy.RUNTIME)public @interface MyTransactional {}

@Transactional(rollbackFor=Exception.class)@Retention(RetentionPolicy.RUNTIME)public @interface MyTransactional { boolean readOnly();}

Annotated Factory Methods@Bean@Scope("session")@Primarypublic BookAdminService bookAdminService() { MyBookAdminService service = new MyBookAdminService(); service.setDataSource(…); return service;}

@Beanpublic BookAdminService bookAdminService() { return MyServiceFactory.createBookAdminService();}

Configuration Classes@Configurationpublic class MyBookAdminConfig { @Bean public BookAdminService myBookAdminService() { MyBookAdminService service = new MyBookAdminService(); service.setDataSource(bookAdminDataSource()); return service; }

@Bean public DataSource bookAdminDataSource() { ... }}

Configuration with Profile and Enable*@Configuration@Profile("standalone")@EnableTransactionManagementpublic class MyBookAdminConfig { @Bean public BookAdminService myBookAdminService() { MyBookAdminService service = new MyBookAdminService(); service.setDataSource(bookAdminDataSource()); return service; }

...}

EL in Component Annotations@Repositorypublic class BookTestDatabase { @Value("#{systemProperties.databaseName}") public void setDatabaseName(String dbName) { ... }

@Value("#{strategyBean.databaseKeyGenerator}") public void setKeyGenerator(KeyGenerator kg) { ... }

...}

Composable Annotations Revisited@Bean@Scope("session")@Primary@Retention(RetentionPolicy.RUNTIME)public @interface MyBean {}@Autowired@Qualifier("production")@Retention(RetentionPolicy.RUNTIME)public @interface MyAutowired {}@Value("#{systemProperties.databaseName}")@Retention(RetentionPolicy.RUNTIME)public @interface MyDatabaseName {}

Standardized Annotations@ManagedBean@MySessionScopedpublic class MyBookAdminService implements BookAdminService { @Inject @Named("production") public MyBookAdminService(Provider<AccountRepository> ar) { ... }

@Transactional public BookUpdate updateBook(Addendum addendum) { ... }}

JSR-330 and Co● @javax.inject.Inject is part of JSR-330

– "Dependency Injection for Java"– also defines @Scope, @Qualifier, @Named, and Provider interface

● @javax.transaction.Transactional is part of JTA 1.2 (EE 7)– finally a direct, independent equivalent of Spring's @Transactional – previously: just EJB 3.0's @javax.ejb.TransactionAttribute

● @javax.annotation.ManagedBean is part of JSR-250 v1.1– can be detected through classpath scanning

JPA Support with Spring Transactions@Repositorypublic class MyAccountRepository { @PersistenceContext private EntityManager em; @Transactional public void storeAccount(Account account) { this.em.merge(account); }}

Standardized vs. Spring-specific APIs● In general, we recommend the use of JSR-330 for DI

– JSR-330 @Inject is almost as capable as Spring's @Autowired– can mix and match with Spring's @Value, @Lazy, etc

● We also recommend the use of JPA 2.x– sufficiently expressive for many purposes now– can unwrap down to e.g. a Hibernate Session from a JPA EntityManager

● However, prefer Spring's stereotype and scoping model– composability is incredibly powerful

Annotated MVC Controllers@Controllerpublic class MyMvcController { @RequestMapping(value = "/books/{id}", method = GET) public Book findBook(@PathVariable("id") long id) { return this.bookAdminService.findBook(id); }

@RequestMapping("/books/new") public void newBook(Book book) { this.bookAdminService.storeBook(book); }}

Portlet MVC Controller@Controller@RequestMapping("EDIT")public class MyPortletController { @ActionMapping("delete") public void removeBook(@RequestParam("book") String bookId) { this.myService.deleteBook(bookId); }

@EventMapping("BookUpdate") public void updateBook(BookUpdateEvent bookUpdate) { this.myService.updateBook(…); }}

STOMP on WebSocket@Controllerpublic class MyStompController { @SubscribeEvent("/positions") public List<PortfolioPosition> getPortfolios(Principal user) { ... }

@MessageMapping(value="/trade") public void executeTrade(Trade trade, Principal user) { ... }}

Declarative Model Validationpublic class Book { @NotNull @Past private Date releaseDate;}

@RequestMapping("/books/new")public void newBook(@Valid Book book, BindingResult br) { ...}

Declarative Formattingpublic class Book { @NotNull @Past @DateTimeFormat(iso=ISO.DATE) private Date releaseDate;}

public class Book { @DateTimeFormat(iso=ISO.DATE) private LocalDate releaseDate;}

Declarative Scheduling@Asyncpublic void sendEmailNotifications() { ...}

@Asyncpublic Future<?> sendEmailNotificationsWithFuture() { return new AsyncResult(...);}

@Scheduled(cron = "0 0 12 * * ?")public void performTempFileCleanup() { ...}

Declarative Caching@Cacheablepublic Owner loadOwner(int id) { ...}

@Cacheable(condition="name.length < 10")public Owner loadOwner(String name) { ...}

@CacheEvictpublic void deleteOwner(int id) { ...}

Once Again: Spring Component Model Themes● Powerful annotated component model● Custom annotations through composition● Configuration classes and factory methods● Spring Expression Language● Flexible MVC/REST controller style● Declarative validation and formatting● Declarative scheduling and caching

Recommended