220 likes | 352 Views
Web Application Development with the Spring Framework. whoami. Kristoffer Moum Systems Developer and consultant at Objectware Five years of experience with Java/J2EE. Agenda. Presentation goals Development environment setup Configuration Spring and MVC Basics of testing
E N D
whoami • Kristoffer Moum • Systems Developer and consultant at Objectware • Five years of experience with Java/J2EE
Agenda • Presentation goals • Development environment setup • Configuration • Spring and MVC • Basics of testing • Interceptor chaining • Aspect Oriented Programming (AOP) examples • Spring and Hibernate • Spring and JDBC • Summary
Presentation goals • A better understanding of the Spring framework targetted on web applications • Knowledge to setup a development environment with Spring, including method interceptors/AOP and a persistence layer
Development environment setup • Required components • JDK 1.5 • A Servlet / JSP container, i.e. Tomcat • Exploded war-file for fast test and deploy cycles • All tiers cannot be tested automatically in a sensible way
Web application configuration • Web application configuration • web.xml • Spring container configuration • Various xml configuration files
Example web.xml <web-app> <display-name>springwebexample</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/spring.xml</param-value> </context-param> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>springapp</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springapp</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app>
Example spring.xml <bean id="customerDao" class="no.objectware.jz2005.springwebapp.dao.impl.HibernateCustomerDao"> <property name="sessionFactory"><ref local="sessionFactory" /></property> </bean> <bean id="customerTarget" class="no.objectware.jz2005.springwebapp.service.impl.DefaultCustomerService"> <property name="customerDao"><ref bean="customerDao" /></property> </bean> <bean id="timingInterceptor" class="no.objectware.jz2005.springwebapp.aop.TimingInterceptor"/> <bean id="timeoutInterceptor" class="no.objectware.jz2005.springwebapp.aop.TimeoutInterceptor"/> <bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"> <property name="sessionFactory"><ref local="sessionFactory" /></property> </bean> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager"> <ref bean="transactionManager" /> </property> <property name="transactionAttributeSource"> <value>no.objectware.jz2005.springwebapp.service.CustomerService.save*=PROPAGATION_REQUIRED,-Exception</value> </property> </bean> <bean id="customerService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>no.objectware.jz2005.springwebapp.service.CustomerService</value> </property> <property name="interceptorNames"> <list> <value>timingInterceptor</value> <value>timeoutInterceptor</value> <value>transactionInterceptor</value> <value>customerTarget</value> </list> </property> </bean>
Spring and MVC – example Controller public class ProductsController extends SimpleFormController { private ProductService productService; public void setProductService(ProductService productService){ this.productService = productService; } public ProductsController() { } protected ModelAndView onSubmit(Object command, BindException errors) throws Exception { List<Product> products = productService.findAllProducts(); return new ModelAndView(getSuccessView(),"products", products); } }
Spring and MVC – example configuration <bean id="productsForm" class="no.objectware.jz2005.springwebapp.web.controller.ProductsController"> <property name="productService"> <ref bean="productService"/> </property> <property name="formView"> <value>displayProducts</value> </property> <property name="successView"> <value>displayProductDetails</value> </property> <property name="validator"> <bean class="no.objectware.jz2005.springwebapp.web.validator.ProductValidator"/> </property> </bean> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/products.html">productsForm</prop> </props> </property> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/jsp/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean>
Spring and MVC • Several abstract Controller implementations • Validation through Validator interface • Binding to form fields through <spring:bind>
Basics of testing • Unit testing • Integration testing • Rolling back transactions • AbstractDependencyInjectionSpringContextTests • Hibernate laziness • Mock classes within the framework
Interceptor chaining and AOP • Interceptor chaining by declarative configuration • How to make it useful • The good old invocation timing example • Implement time-out handling • Where to apply it • Write Once Read Many • Infrastructure
AOP examples • Caching time-consuming look-ups • Caching by declarative configuration • Business interface remains unchanged • Application security • Declarative security handling • The Acegi framework • Method invocation time-out handling
Business method invocation timeout public final class ReadOnlyTimeoutInterceptor implements MethodInterceptor { private static ExecutorService runner = Executors.newCachedThreadPool(); private Map<String,String> timeouts = new HashMap<String,String>(); public void setTimeouts(Map<String, String> timeouts) { this.timeouts = timeouts; } public Object invoke(MethodInvocation invocation) throws Throwable{ String serviceMethod = invocation.getMethod().getDeclaringClass().getName() + "." + invocation.getMethod().getName(); String maxInvocation = timeouts.get(serviceMethod); if(maxInvocation == null) { maxInvocation = timeouts.get("default"); } Future asyncCaller = runner.submit(new CallableHandler(invocation, serviceMethod)); try { return asyncCaller.get(Long.parseLong(maxInvocation), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throw e.getCause(); } catch (TimeoutException e) { throw new RuntimeException("Service [" + serviceMethod + "] timed out after approximately [" + maxInvocation + "] ms"); } } (..)
Business method invocation timeout (cont.) public class CallableHandler implements Callable { private MethodInvocation invocation; private String service; public QualityOfServiceHandler(MethodInvocation invocation, String service) { this.invocation = invocation; this.service = service; } public Object call() throws Exception { try { return invocation.proceed(); } catch (Throwable throwable) { throw new RuntimeException("Exception when invoking service [" + service + "]"); } } } }
Spring and Hibernate • Small amount of code • Extend HibernateDaoSupport • No handling of checked exceptions • Avoiding the dreaded LazyInitializationException
Spring and Hibernate by example public interface CustomerService { void save(Customer customer); } public final class DefaultCustomerService implements CustomerService { private CustomerDao customerDao; public void setCustomerDao(CustomerDao customerDao) { this.customerDao = customerDao; } public void save(Customer customer) { customerDao.save(customer); } } public interface CustomerDao { void save(Customer customer); } public final class HibernateCustomerDao extends HibernateDaoSupport implements CustomerDao { public void save(Customer customer) { getHibernateTemplate().save(customer); } }
Spring and Jdbc • Still a small amount of code • Extend JdbcDaoSupport • When to apply this wrapper • Joining many tables with lazy collections isn’t exactly fast • Databases could be out of our control, i.e. Legacy and may have lots of constraints
Spring and Jdbc by example public final class JdbcProductCategoryDao extends JdbcDaoSupport implements ProductCategoryDao { private static final String findAllProductCategories = "select * from product_category"; public List<ProductCategory> findAll() { return getJdbcTemplate().query(findAllProductCategories, new ProductCategoryRowMapper()); } public static class ProductCategoryRowMapper implements RowMapper { public Object mapRow(ResultSet rs, int i) throws SQLException { long categoryId = rs.getLong("product_category_id"); String description = rs.getString("product_category_desc"); return new ProductCategory(categoryId, description); } } }
Summary • Development environment setup • Configuration • Spring and MVC • Basics of testing • Interceptor chaining • Aspect Oriented Programming (AOP) examples • Spring and Hibernate • Spring and JDBC
Further reading • Introduction to Dependency Injection • http://www.martinfowler.com/articles/injection.html • Introduction to Spring • http://www.theserverside.com/articles/article.tss?l=SpringFramework • Spring Forum • http://forum.springframework.org • Blog on Hibernate and laziness in different application tiers • http://jroller.com/comments/kbaum/Weblog/orm_lazy_initialization_with_dao • The Serverside discussion thread on testing • http://theserverside.com/news/thread.tss?thread_id=33215 • Matt Raible’s appfuse project