Modern Component Design with Spring
-
Upload
spring-io -
Category
Technology
-
view
1.118 -
download
1
description
Transcript of Modern Component Design with Spring
© 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