600 likes | 828 Views
Winter is coming (Johannes vs Johanes. Nets DevDay 2012 Johannes Brodwall , Principal Architect Steria Norway @ jhannes. Why dependency detox ?. <? xml version = "1.0" encoding = "UTF-8" ?> < beans default-autowire = " constructor " >
E N D
Winter is coming(Johannes vsJohanes Nets DevDay 2012 Johannes Brodwall, Principal Architect Steria Norway @jhannes
<?xmlversion="1.0" encoding="UTF-8"?> <beansdefault-autowire="constructor"> <beanclass="no.steria.spring.ApplicationServiceImpl" /> <beanclass="no.steria.spring.IncrementService" /> <beanclass="no.steria.spring.ReportService" /> <beanclass="no.steria.winterapp.provided.JdbcEntryDao" /> <beanclass="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="jdbc/primaryDs" /> </bean> </beans>
publicstaticvoid main(String[] args){ ApplicationContextcontext = newClassPathXmlApplicationContext("..."); ApplicationServiceapplicationService=context.getBean(ApplicationService.class); applicationService.incrementAndReport(new Date()); }
In what <import> is that bean defined? Who created that object? Is that setter still in use? The “Service” here does nothing! Where is the business logic?! Are we sure @Transactional is picked up? How does @Transaction work?!
publicstaticvoid main(String[] args) { ApplicationServiceapplicationService = newApplicationService(createDataSource()); applicationService.incrementAndReport(newDate()); }
publicclassApplicationService { privateIncrementServiceincrementService; privateReportServicereportService; publicApplicationService(DataSourcedataSource) { this(newIncrementService(dataSource), newReportService(dataSource)); } publicApplicationService(IncrementServiceincrementSvc,ReportServicereportSvc) { this.incrementService = incrementService; this.reportService = reportService; } publicvoidincrementAndReport(Date date) { incrementService.increment(date); reportService.printReport(); } }
publicclassIncrementService { privateEntryDaoentryDao; publicIncrementService(DataSourcedataSource) { this(newJdbcEntryDao(dataSource)); } publicIncrementService(EntryDaoentryDao) { this.entryDao = entryDao; } publicvoidincrement(Date date) { entryDao.insertEntry(newEntry(date)); } }
Better quality Less searching Compiler errors
”You know you are working on clean code when each routine you read turns out to be pretty much what you expected.” Ward Cunningham:
EJB 2.0 … reallysucked … onefeature: Transactions!
Java design in 2003 … reallysucked … Singleton craze … neededdependencycontrol … needed more magic
EJB 2.0 … um… no!
Transactions … it’sreally not that hard! … let me show you
publicinterfaceTransactionManager { TransactionbeginTransaction(); } publicinterfaceTransactionextendsCloseable { voidsetCommit(); voidclose(); }
@Test publicvoidrollbackTransaction() throws Exception { doThrow(newRuntimeException()).when(targetObject).doIt(); try { transactional(targetObject).doIt(); fail("Shouldthrowexception"); } catch (RuntimeException e) { // expected } verify(tx, never()).setCommit(); verify(tx).close(); }
protected<T> T transactional(final T targetObject) { returnProxy.newProxyInstance(getClass().getClassLoader(), …, newInvocationHandler() { @Override public Object invoke(…, Method method, Object[] args) { Transactiontransaction = txMgr.beginTransaction(); try { method.invoke(targetObject, args); transaction.setCommit(); } finally { transaction.close(); } returnnull; } }); } Since Java 1.4!
voidinTx(ExampleInterfacetargetObject) { try (Transactiontx = txMgr.beginTransaction()) { targetObject.doIt(); // if doIt() throws, we never get here // – then close() rolls back tx.setCommit(); } } Java 1.7
Dependency management … Goingwaaaay over board
Person-Controller Person-Service Person-Repository PersonDao Person-Controller-Impl Person-ServiceImpl Person-Repository Impl PersonDao Impl Session-Factory
Customer Invoice Order Product
<?xmlversion="1.0" encoding="UTF-8"?> <beansdefault-autowire="constructor"> <beanclass="no.steria.spring.ApplicationServiceImpl" /> <beanclass="no.steria.spring.IncrementService" /> <beanclass="no.steria.spring.ReportService" /> <beanclass="no.steria.winterapp.provided.JdbcEntryDao" /> <beanclass="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="jdbc/primaryDs" /> </bean> </beans>
DataSource (Other resources) Hibernate SessionFactory Web Services (URLs)
For test publicclassPersonServletextendsHttpServlet { privatePersonDaopersonDao; publicvoidsetPersonDao(PersonDaopersonDao) { this.personDao = personDao; } @Override publicvoidinit() throwsServletException { setPersonDao(newHibernatePersonDao("jdbc/personDs")); } } New? Wut?!?! For realz
Hmmm… publicclassHibernatePersonDaoimplementsPersonDao { publicHibernatePersonDao(StringjndiDataSource) { Configurationcfg = newConfiguration(); cfg.setProperty(Environment.DATASOURCE, jndiDataSource); …; cfg.addAnnotatedClass(Person.class); this.sessionFactory = cfg.buildSessionFactory(); } Hmmm…
publicclassPersonServletextendsHttpServlet { privatePersonDaopersonDao; publicvoidsetPersonDao(PersonDaopersonDao) { this.personDao = personDao; } @Override publicvoidinit() throwsServletException { setPersonDao(HibernatePersonDao.getInstance()); } } Singleton FTW!
Announcing A Powerful New Framework: Programming Language is a powerful new framework that enables developers to quickly and easily handle Dependency Injection, Inversion of Control, Model-View-Controller and many other common design problems. Jason Gorman http://codemanship.co.uk/parlezuml/blog/?postid=1097
Messy picture Well-architected picture
com.app.person OR com.app.controller
PersonController Service Repository Session-Factory
Hard stuff publicclassPersonController { privatePersonServicepersonService; @Autowired publicPersonController(SessionFactorysf) { this.personService = newPersonServiceImpl(sf); } publicclassPersonServiceImplimplements … { privatePersonRepositorypersonRepo; publicPersonServiceImpl(SessionFactorysf) { this.personRepo = newPersonRepositoryImpl(sf); } Hard stuff
For Spring publicclassPersonController { privatePersonServicepersonService; @Autowired publicPersonController(SessionFactorysf) { this.personService = newPersonServiceImpl(sf); } publicPersonControllerImpl(PersonServiceps) { this.personService = ps; } For mocking
SPRING! PersonController Service Repository InvoiceController Service Repository Session-Factory ReportsController FooController FooServiceImpl
SPRING! PersonController Service Repository InvoiceController Repository Session-Factory ReportsController FooController FooServiceImpl