Spring 3 maven 2 web application step by step

转载 2013年12月03日 07:49:12

Before writting this I admin that there are lots of tutorials like this but what I found is that they are not step by step guides, with missing vital parts of process of making it all happen from scratch. This makes you revert to the documentation and therefore defies the point of going through the tutorial. With this one I hope to make it a bit clearer, though I do not attempt to make it IDE specific, therefore there will be no guidance on how to do it in specific IDE. The layout will be to work with standard Maven project structure.

What is in this tutorial?

  • Maven Spring project configurations
  • Persistence using Hibernate and HSQLDB
  • Cache support using Ehcahe
  • Spring MVC configurations
  • Example integration tests for DB and Caching (Please not there are no Unit tests as I assume that the reader is familiar with those already)

Link to the full set of sources for this tutorial:

denis-pavlov-spring3mvc-mvn2-example-updated.zip

First things first - the POM:

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
  2.     <modelversion>4.0.0</modelversion>  
  3.     <groupid>dp.examples</groupid>  
  4.     <artifactid>shoppingcart</artifactid>  
  5.     <version>0.0.1-SNAPSHOT</version>  
  6.     <packaging>war</packaging>  
  7.     <name>ShoppingCart</name>  
  8.   
  9.     <build>  
  10.         <plugins>  
  11.             <plugin>  
  12.                 <artifactid>maven-compiler-plugin</artifactid>  
  13.                 <configuration>  
  14.                     <source>1.5  
  15.                     <target>1.5</target>  
  16.                 </configuration>  
  17.             </plugin>  
  18.         </plugins>  
  19.     </build>  
  20.   
  21.     <!-- Shared version number properties -->  
  22.     <properties>  
  23.         <org.springframework.version>3.1.2.RELEASE</org.springframework.version>  
  24.   
  25.         <commons-dbcp.version>1.4</commons-dbcp.version>  
  26.         <sqlite-jdbc.version>3.7.2</sqlite-jdbc.version>  
  27.         <hsqldb-jdbc.version>2.2.9</hsqldb-jdbc.version>  
  28.   
  29.         <hibernate-core.version>4.1.9.Final</hibernate-core.version>  
  30.         <hibernate-search.version>4.2.0.Final</hibernate-search.version>  
  31.   
  32.         <xalan.version>2.7.0</xalan.version>  
  33.   
  34.         <backport-util-concurrent.version>3.1</backport-util-concurrent.version>  
  35.   
  36.         <ehcache.version>2.6.6</ehcache.version>  
  37.   
  38.     </properties>  
  39.   
  40.     <dependencies>  
  41.   
  42.         <dependency>  
  43.             <groupid>log4j</groupid>  
  44.             <artifactid>log4j</artifactid>  
  45.             <version>1.2.15</version>  
  46.             <exclusions>  
  47.                 <exclusion>  
  48.                     <groupid>javax.mail</groupid>  
  49.                     <artifactid>mail</artifactid>  
  50.                 </exclusion>  
  51.                 <exclusion>  
  52.                     <groupid>javax.jms</groupid>  
  53.                     <artifactid>jms</artifactid>  
  54.                 </exclusion>  
  55.                 <exclusion>  
  56.                     <groupid>com.sun.jdmk</groupid>  
  57.                     <artifactid>jmxtools</artifactid>  
  58.                 </exclusion>  
  59.                 <exclusion>  
  60.                     <groupid>com.sun.jmx</groupid>  
  61.                     <artifactid>jmxri</artifactid>  
  62.                 </exclusion>  
  63.             </exclusions>  
  64.             <scope>runtime</scope>  
  65.         </dependency>  
  66.   
  67.         <!-- 
  68.               JSP taglib support 
  69.           -->  
  70.         <dependency>  
  71.             <groupid>taglibs</groupid>  
  72.             <artifactid>standard</artifactid>  
  73.             <version>1.1.2</version>  
  74.         </dependency>  
  75.         <dependency>  
  76.             <groupid>javax.servlet</groupid>  
  77.             <artifactid>jstl</artifactid>  
  78.             <version>1.2</version>  
  79.         </dependency>  
  80.   
  81.   
  82.         <!--  
  83.               Core utilities used by other modules.  
  84.               Define this if you use Spring Utility APIs (org.springframework.core.*/org.springframework.util.*)  
  85.           -->  
  86.         <dependency>  
  87.             <groupid>org.springframework</groupid>  
  88.             <artifactid>spring-core</artifactid>  
  89.             <version>${org.springframework.version}</version>  
  90.         </dependency>  
  91.   
  92.         <!--  
  93.               Expression Language (depends on spring-core)  
  94.               Define this if you use Spring Expression APIs (org.springframework.expression.*)  
  95.           -->  
  96.         <dependency>  
  97.             <groupid>org.springframework</groupid>  
  98.             <artifactid>spring-expression</artifactid>  
  99.             <version>${org.springframework.version}</version>  
  100.         </dependency>  
  101.   
  102.         <!--  
  103.               Bean Factory and JavaBeans utilities (depends on spring-core)  
  104.               Define this if you use Spring Bean APIs (org.springframework.beans.*)  
  105.           -->  
  106.         <dependency>  
  107.             <groupid>org.springframework</groupid>  
  108.             <artifactid>spring-beans</artifactid>  
  109.             <version>${org.springframework.version}</version>  
  110.         </dependency>  
  111.   
  112.         <!--  
  113.               Aspect Oriented Programming (AOP) Framework (depends on spring-core, spring-beans)  
  114.               Define this if you use Spring AOP APIs (org.springframework.aop.*)  
  115.           -->  
  116.         <dependency>  
  117.             <groupid>org.springframework</groupid>  
  118.             <artifactid>spring-aop</artifactid>  
  119.             <version>${org.springframework.version}</version>  
  120.         </dependency>  
  121.   
  122.         <!--  
  123.               Application Context (depends on spring-core, spring-expression, spring-aop, spring-beans)  
  124.               This is the central artifact for Spring's Dependency Injection Container and is generally always defined  
  125.           -->  
  126.         <dependency>  
  127.             <groupid>org.springframework</groupid>  
  128.             <artifactid>spring-context</artifactid>  
  129.             <version>${org.springframework.version}</version>  
  130.         </dependency>  
  131.   
  132.         <!--  
  133.               Various Application Context utilities, including EhCache, JavaMail, Quartz, and Freemarker integration  
  134.               Define this if you need any of these integrations  
  135.           -->  
  136.         <dependency>  
  137.             <groupid>org.springframework</groupid>  
  138.             <artifactid>spring-context-support</artifactid>  
  139.             <version>${org.springframework.version}</version>  
  140.         </dependency>  
  141.   
  142.         <!--  
  143.               Transaction Management Abstraction (depends on spring-core, spring-beans, spring-aop, spring-context)  
  144.               Define this if you use Spring Transactions or DAO Exception Hierarchy  
  145.               (org.springframework.transaction.*/org.springframework.dao.*)  
  146.           -->  
  147.         <dependency>  
  148.             <groupid>org.springframework</groupid>  
  149.             <artifactid>spring-tx</artifactid>  
  150.             <version>${org.springframework.version}</version>  
  151.         </dependency>  
  152.   
  153.         <!--  
  154.               JDBC Data Access Library (depends on spring-core, spring-beans, spring-context, spring-tx)  
  155.               Define this if you use Spring's JdbcTemplate API (org.springframework.jdbc.*)  
  156.           -->  
  157.         <dependency>  
  158.             <groupid>org.springframework</groupid>  
  159.             <artifactid>spring-jdbc</artifactid>  
  160.             <version>${org.springframework.version}</version>  
  161.         </dependency>  
  162.   
  163.         <!--  
  164.               Object-to-Relation-Mapping (ORM) integration with Hibernate, JPA, and iBatis.  
  165.               (depends on spring-core, spring-beans, spring-context, spring-tx)  
  166.               Define this if you need ORM (org.springframework.orm.*)  
  167.           -->  
  168.         <dependency>  
  169.             <groupid>org.springframework</groupid>  
  170.             <artifactid>spring-orm</artifactid>  
  171.             <version>${org.springframework.version}</version>  
  172.         </dependency>  
  173.   
  174.         <!--  
  175.               Object-to-XML Mapping (OXM) abstraction and integration with JAXB, JiBX, Castor, XStream, and XML Beans.  
  176.               (depends on spring-core, spring-beans, spring-context)  
  177.               Define this if you need OXM (org.springframework.oxm.*)  
  178.           -->  
  179.         <dependency>  
  180.             <groupid>org.springframework</groupid>  
  181.             <artifactid>spring-oxm</artifactid>  
  182.             <version>${org.springframework.version}</version>  
  183.         </dependency>  
  184.   
  185.         <!--  
  186.               Web application development utilities applicable to both Servlet and Portlet Environments  
  187.               (depends on spring-core, spring-beans, spring-context)  
  188.               Define this if you use Spring MVC, or wish to use Struts, JSF, or another web framework with Spring (org.springframework.web.*)  
  189.           -->  
  190.         <dependency>  
  191.             <groupid>org.springframework</groupid>  
  192.             <artifactid>spring-web</artifactid>  
  193.             <version>${org.springframework.version}</version>  
  194.         </dependency>  
  195.   
  196.         <!--  
  197.               Spring MVC for Servlet Environments (depends on spring-core, spring-beans, spring-context, spring-web)  
  198.               Define this if you use Spring MVC with a Servlet Container such as Apache Tomcat (org.springframework.web.servlet.*)  
  199.           -->  
  200.         <dependency>  
  201.             <groupid>org.springframework</groupid>  
  202.             <artifactid>spring-webmvc</artifactid>  
  203.             <version>${org.springframework.version}</version>  
  204.         </dependency>  
  205.   
  206.         <!--  
  207.               Spring MVC for Portlet Environments (depends on spring-core, spring-beans, spring-context, spring-web)  
  208.               Define this if you use Spring MVC with a Portlet Container (org.springframework.web.portlet.*)  
  209.           -->  
  210.         <dependency>  
  211.             <groupid>org.springframework</groupid>  
  212.             <artifactid>spring-webmvc-portlet</artifactid>  
  213.             <version>${org.springframework.version}</version>  
  214.         </dependency>  
  215.   
  216.         <!--  
  217.               Support for testing Spring applications with tools such as JUnit and TestNG  
  218.               This artifact is generally always defined with a 'test' scope for the integration testing framework and unit testing stubs  
  219.           -->  
  220.         <dependency>  
  221.             <groupid>org.springframework</groupid>  
  222.             <artifactid>spring-test</artifactid>  
  223.             <version>${org.springframework.version}</version>  
  224.             <scope>test</scope>  
  225.         </dependency>  
  226.   
  227.         <!-- hibernate -->  
  228.         <dependency>  
  229.             <groupid>org.hibernate</groupid>  
  230.             <artifactid>hibernate-core</artifactid>  
  231.             <version>${hibernate-core.version}</version>  
  232.             <exclusions>  
  233.                 <exclusion>  
  234.                     <groupid>xml-apis</groupid>  
  235.                     <artifactid>xml-apis</artifactid>  
  236.                 </exclusion>  
  237.             </exclusions>  
  238.         </dependency>  
  239.         <dependency>  
  240.             <groupid>xalan</groupid>  
  241.             <artifactid>xalan</artifactid>  
  242.             <version>${xalan.version}</version>  
  243.         </dependency>  
  244.         <!-- dependency>  
  245.            <groupId>org.hibernate</groupId>  
  246.            <artifactId>hibernate-search</artifactId>  
  247.            <version>${hibernate-search.version}</version>  
  248.        </dependency -->  
  249.   
  250.         <!-- connection pooling -->  
  251.         <dependency>  
  252.             <groupid>commons-dbcp</groupid>  
  253.             <artifactid>commons-dbcp</artifactid>  
  254.             <version>${commons-dbcp.version}</version>  
  255.             <scope>runtime</scope>  
  256.         </dependency>  
  257.   
  258.         <!-- HSQLDB JDBC library -->  
  259.         <dependency>  
  260.             <groupid>org.hsqldb</groupid>  
  261.             <artifactid>hsqldb</artifactid>  
  262.             <version>${hsqldb-jdbc.version}</version>  
  263.             <scope>runtime</scope>  
  264.         </dependency>  
  265.   
  266.         <!-- EhCache -->  
  267.         <dependency>  
  268.             <groupid>net.sf.ehcache</groupid>  
  269.             <artifactid>ehcache-core</artifactid>  
  270.             <version>${ehcache.version}</version>  
  271.         </dependency>  
  272.   
  273.         <!-- Unit and integration testing -->  
  274.         <dependency>  
  275.             <groupid>junit</groupid>  
  276.             <artifactid>junit</artifactid>  
  277.             <version>4.9</version>  
  278.             <scope>test</scope>  
  279.         </dependency>  
  280.   
  281.   
  282.     </dependencies>  
  283. </project>  

At this point we have done all we need maven wise and can safely proceed to next step.

BTW if you are using eclipse and started of with a maven2 project and now trying to convert it to web projects I think you need to read this

We are making a webapp, so the second thing is web.xml (That should be in the src/main/webapp/WEB-INF/):

  1. <!--?xml version="1.0" encoding="UTF-8"?-->  
  2.   
  3. <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/j2ee   
  4.          http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">  
  5.     
  6.   <context-param>  
  7.     <param-name>log4jConfigLocation</param-name>  
  8.     <param-value>classpath:META-INF/properties/log4j.properties</param-value>  
  9.   </context-param>  
  10.   <listener>  
  11.     <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>  
  12.   </listener>  
  13.     
  14.   <!-- Spring context -->  
  15.   <context-param>  
  16.     <param-name>contextConfigLocation</param-name>  
  17.     <param-value>classpath*:WEB-INF/spring/*.xml</param-value>  
  18.   </context-param>  
  19.   <listener>  
  20.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  21.   </listener>  
  22.     
  23.   <!-- Spring webapp  -->  
  24.   <servlet>  
  25.     <servlet-name>spring</servlet-name>  
  26.     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  27.     <init-param>  
  28.       <param-name>contextConfigLocation</param-name>  
  29.       <param-value>/WEB-INF/spring/*.xml</param-value>  
  30.     </init-param>  
  31.     <load-on-startup>1</load-on-startup>  
  32.   </servlet>  
  33.     
  34.   <servlet-mapping>  
  35.     <servlet-name>spring</servlet-name>  
  36.     <url-pattern>/</url-pattern>  
  37.   </servlet-mapping>  
  38.   
  39.   <welcome-file-list>  
  40.     <welcome-file>  
  41.       index.jsp  
  42.     </welcome-file>  
  43.   </welcome-file-list>  
  44.   
  45. </web-app>  

Then the Spring context configuration (for webapp that is located in WEB-INF/spring/ directory as specified in web.xml parameters to the servlet):

  1. <!--?xml version="1.0" encoding="UTF-8"?-->  
  2. <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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
  3. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd  
  4. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
  5. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
  6. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">  
  7.   
  8.     <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
  9.         <property name="locations">  
  10.             <list>  
  11.                 <!-- You can have multiple files here -->  
  12.                 <value>classpath:spring-context.properties</value>  
  13.             </list>  
  14.         </property>  
  15.     </bean>  
  16.   
  17.   
  18.     <import resource="classpath*:spring/cache.xml">  
  19.     <import resource="classpath*:spring/persistence.xml">  
  20.   
  21.     <!-- Service to manipulate the cart -->  
  22.     <bean id="cartService" parent="txProxyTemplate">  
  23.         <property name="target">  
  24.             <bean class="dp.example.shoppingcart.service.impl.WebCartServiceCached">  
  25.                 <constructor-arg index="0" ref="cartDao">  
  26.             </constructor-arg></bean>  
  27.         </property>  
  28.     </bean>  
  29.       
  30.     <!-- Scans the classpath of this application for @Components to deploy as beans -->  
  31.     <context:component-scan base-package="dp.example.shoppingcart.web">  
  32.   
  33.     <!-- Configures the @Controller programming model -->  
  34.     <mvc:annotation-driven>  
  35.   
  36.     <!-- Forwards requests to the "/" resource to the "welcome" view -->  
  37.     <mvc:view-controller path="/" view-name="cart">  
  38.   
  39.     <!-- Configures Handler Interceptors -->      
  40.     <mvc:interceptors>  
  41.         <!-- Changes the locale when a 'locale' request parameter is sent; e.g. /?locale=de -->  
  42.         <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">  
  43.     </bean></mvc:interceptors>  
  44.   
  45.     <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources/ directory -->  
  46.     <mvc:resources mapping="/resources/**" location="/resources/">  
  47.   
  48.     <!-- Saves a locale change using a cookie -->  
  49.     <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">  
  50.   
  51.     <!-- Application Message Bundle -->  
  52.     <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
  53.         <property name="basename" value="/WEB-INF/messages/messages">  
  54.         <property name="cacheSeconds" value="0">  
  55.     </property></property></bean>  
  56.   
  57.     <!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->  
  58.     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  59.         <property name="prefix" value="/WEB-INF/views/">  
  60.         <property name="suffix" value=".jsp">  
  61.     </property></property></bean>  
  62.   
  63.   
  64. </bean></mvc:resources></mvc:view-controller></mvc:annotation-driven></context:component-scan></import></import></beans>  

The main context file contains two imports: spring/cache.xml that contains ehcache configuration and spring/persistence.xml that contains hibernate configuration

  1. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:util="http://www.springframework.org/schema/util" xsi:schemalocation="http://www.springframework.org/schema/beans  
  2.        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  3.        http://www.springframework.org/schema/cache  
  4.        http://www.springframework.org/schema/cache/spring-cache.xsd  
  5.        http://www.springframework.org/schema/util  
  6.        http://www.springframework.org/schema/util/spring-util-3.2.xsd">  
  7.   
  8.     <cache:annotation-driven>  
  9.   
  10.     <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cachemanager-ref="ehcache">  
  11.   
  12.     <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configlocation="classpath:ehcache.xml" p:shared="true">  
  13.   
  14. </bean></bean></cache:annotation-driven></beans>  

Note the p:shared="true" attribute. This ensures that we are using singleton manager for the Ehcache, which is important if you would like to use the same cache manager for your webapp and your hibernate 2nd level cache. "p:configLocation="classpath:ehcache.xml" tells Spring that we what to use the src/main/resources/ehcache.xml for our cache configurations

  1. <ehcache updatecheck="false">  
  2.   
  3.     <defaultcache maxelementsinmemory="10000" overflowtodisk="false" eternal="false" timetoidleseconds="1800" timetoliveseconds="3600">  
  4.   
  5.     <!-- ############################################# cluster cache #######################################-->  
  6.   
  7.     <cache name="cart" maxelementsinmemory="100" overflowtodisk="false" eternal="false" timetoliveseconds="86400" timetoidleseconds="7200">  
  8.   
  9. </cache></defaultcache></ehcache>  

Persistence condifuration is quite simple. We setup dataSource first. Note that we use Spring variable substitution, which is defined by propertyConfigurer bean in context.xml that takes values from spring-context.properties. Then we define the hibernate factory and txProxyTemplate, which is used by transactional beans. It is good practice to manage transaction boundaries on the service layer. I.e. each method of service beans represents a unit of work and hence is a good candidate for a single transaction. For that purpose our Data access object is specifically not marked as transactional to prevent using it directly and hence have transaction per single db operation, which is not optimal. Instead we will make our service (WebCartServiceCached) transactional which will be using DAO and hence the transaction will be propagated to DAO.

  1. <!--?xml version="1.0" encoding="UTF-8"?-->  
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xmlns:ctx="http://www.springframework.org/schema/context" xsi:schemalocation="http://www.springframework.org/schema/beans  
  3.                            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd  
  4.                            http://www.springframework.org/schema/util  
  5.                            http://www.springframework.org/schema/util/spring-util-3.1.xsd  
  6.                            http://www.springframework.org/schema/context  
  7.                            http://www.springframework.org/schema/context/spring-context-3.1.xsd">  
  8.   
  9.     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
  10.         <property name="driverClassName" value="${db.driverClassName}">  
  11.         <property name="url" value="${db.url}">  
  12.         <property name="username" value="${db.username}">  
  13.         <property name="password" value="${db.password}">  
  14.     </property></property></property></property></bean>  
  15.   
  16.     <util:properties id="hibernateProperties" location="classpath:hibernate/hibernate-hsqldb.properties">  
  17.   
  18.     <!-- Hibernate ******************************************************************* -->  
  19.   
  20.     <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">  
  21.         <property name="dataSource" ref="dataSource">  
  22.         <property name="hibernateProperties" ref="hibernateProperties">  
  23.         <property name="mappingResources">  
  24.             <list>  
  25.                 <value>hibernate/mapping.hbm.xml</value>  
  26.             </list>  
  27.         </property>  
  28.     </property></property></bean>  
  29.   
  30.     <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">  
  31.         <property name="sessionFactory" ref="sessionFactory">  
  32.     </property></bean>  
  33.   
  34.     <bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
  35.         <description>  
  36.             NOTE: see org.springframework.transaction.TransactionDefinition  
  37.   
  38.             DAO: all beans must be wrapped into this proxy to allow seamless  
  39.             transaction management integration via AOP.  
  40.         </description>  
  41.         <property name="transactionManager" ref="transactionManager">  
  42.         <property name="transactionAttributes">  
  43.             <props>  
  44.                 <!--<prop key="add*">PROPAGATION_REQUIRED,-Throwable</prop>-->  
  45.                 <!--<prop key="remove*">PROPAGATION_REQUIRED,-Throwable</prop>-->  
  46.                 <prop key="*">PROPAGATION_REQUIRED,-Throwable</prop>  
  47.             </props>  
  48.         </property>  
  49.     </property></bean>  
  50.   
  51.     <bean id="cartDao" class="dp.example.shoppingcart.dao.impl.CartDaoImpl">  
  52.         <constructor-arg index="0" ref="sessionFactory">  
  53.     </constructor-arg></bean>  
  54.   
  55. </util:properties></beans>  

DAO object is very simple and uses Hibernates current session which is provided by txProxyTemplate from WebCartService to perform DB operations. Note that if we to use DAO bean directly we would have a Hibernate exception and no session is available standalone.

  1. package dp.example.shoppingcart.dao.impl;  
  2.   
  3. import dp.example.shoppingcart.dao.CartDao;  
  4. import dp.example.shoppingcart.domain.impl.CartEntity;  
  5. import dp.example.shoppingcart.dto.Cart;  
  6. import dp.example.shoppingcart.dto.Item;  
  7. import dp.example.shoppingcart.service.CartService;  
  8. import org.hibernate.SessionFactory;  
  9.   
  10. import java.util.List;  
  11.   
  12. /** 
  13.  * Web implementation of cart service 
  14.  *  
  15.  * @author DPavlov 
  16.  */  
  17. public class CartDaoImpl implements CartDao  
  18. {  
  19.   
  20.     private final SessionFactory sessionFactory;  
  21.   
  22.     public CartDaoImpl(final SessionFactory sessionFactory) {  
  23.         this.sessionFactory = sessionFactory;  
  24.     }  
  25.   
  26.     public Cart createCart() {  
  27.         final Cart cart = new CartEntity();  
  28.         save(cart);  
  29.         return cart;  
  30.     }  
  31.   
  32.     public Cart findCart(final long pk) {  
  33.         return (Cart) sessionFactory.getCurrentSession().get(CartEntity.class, pk);  
  34.     }  
  35.   
  36.     public void save(final Cart cart) {  
  37.         sessionFactory.getCurrentSession().save(cart);  
  38.     }  
  39. }  

Our service uses DAO to provide business functions represented by WebCartServiceCached methods. For example #addToCart(final long pk, String item) will either find or create cart with specific primary key, add and item and persist the cart. All these db operations will happen in a single transaction. And if we have an exception all these changes are rolled back. So we have a good "all or nothing" strategy that will keep our db in a consistent state.

Caching is very simple. Facilitated by two annotations: @Cacheable(value = "cart") and @CacheEvict(value = "cart", allEntries = false, key = "#pk"). There are lots of configurations for this but this is the basic setup. @Cacheable tells ehcache to use cache named "cart" (this is why we have this entry in ehcache.xml) to keep results of this method. @CacheEvict allows to remove cache entries. In this case we remove a single entry by key 'pk', but it is possible to evict all items. As I already said it is very configurable.

  1. package dp.example.shoppingcart.service.impl;  
  2.   
  3. import dp.example.shoppingcart.dao.CartDao;  
  4. import dp.example.shoppingcart.dto.Cart;  
  5. import dp.example.shoppingcart.dto.Item;  
  6. import dp.example.shoppingcart.service.CartService;  
  7. import org.springframework.cache.annotation.CacheEvict;  
  8. import org.springframework.cache.annotation.Cacheable;  
  9.   
  10. import java.util.List;  
  11.   
  12. /** 
  13.  * Web implementation of cart service 
  14.  *  
  15.  * @author DPavlov 
  16.  */  
  17. public class WebCartServiceCached implements CartService  
  18. {  
  19.   
  20.     private final CartDao cartDao;  
  21.   
  22.     public WebCartServiceCached(final CartDao cartDao) {  
  23.         this.cartDao = cartDao;  
  24.     }  
  25.   
  26.     @CacheEvict(value = "cart", allEntries = false, key = "#pk")  
  27.     public void addToCart(final long pk, String item) {  
  28.         final Cart cart = findOrCreateCart(pk);  
  29.         cart.addItem(item);  
  30.         cartDao.save(cart);  
  31.     }  
  32.   
  33.     @Cacheable(value = "cart")  
  34.     public List<item> getItemsInCart(final long pk) {  
  35.         final Cart cart = findOrCreateCart(pk);  
  36.         return cart.getItems();  
  37.     }  
  38.   
  39.     @CacheEvict(value = "cart", allEntries = false, key = "#pk")  
  40.     public void removeFromCart(final long pk, String item) {  
  41.         final Cart cart = findOrCreateCart(pk);  
  42.         cart.removeItem(item);  
  43.         cartDao.save(cart);  
  44.     }  
  45.   
  46.   
  47.     private Cart findOrCreateCart(final long pk) {  
  48.         Cart cart = cartDao.findCart(pk);  
  49.         if (cart == null) {  
  50.             cart = cartDao.createCart();  
  51.         }  
  52.         return cart;  
  53.     }  
  54.   
  55.   
  56. }  
  57. </item>  

Basic controller uses @RequestMapping and @PathVariable to give a nice looking and SEO friendly url pattern.

  1. package dp.example.shoppingcart.web;  
  2.   
  3. import dp.example.shoppingcart.dto.Item;  
  4. import dp.example.shoppingcart.service.CartService;  
  5. import org.springframework.beans.factory.annotation.Autowired;  
  6. import org.springframework.stereotype.Controller;  
  7. import org.springframework.web.bind.annotation.PathVariable;  
  8. import org.springframework.web.bind.annotation.RequestMapping;  
  9. import org.springframework.web.bind.annotation.RequestMethod;  
  10. import org.springframework.web.servlet.ModelAndView;  
  11.   
  12. import java.util.List;  
  13.   
  14. /** 
  15.  * Basic controller 
  16.  *  
  17.  * @author DPavlov 
  18.  */  
  19. @Controller  
  20. public class CartController {  
  21.       
  22.     private CartService cartService;  
  23.       
  24.     private int counter = 1;  
  25.   
  26.     @Autowired  
  27.     public CartController(CartService cartService) {  
  28.         this.cartService = cartService;  
  29.     }  
  30.       
  31.     @RequestMapping(value = "/cart/{pk}", method = RequestMethod.GET)  
  32.     public ModelAndView getCart(@PathVariable final long pk) {  
  33.         final List<item> list = cartService.getItemsInCart(pk);  
  34.         ModelAndView mav = new ModelAndView("cart");  
  35.         mav.addObject("pk", pk);  
  36.         mav.addObject("items", list);  
  37.         mav.addObject("add""Item" + counter++);  
  38.         return mav;  
  39.           
  40.     }  
  41.       
  42.     @RequestMapping(value = "/cart/{pk}/addtocart/{item}", method = RequestMethod.GET)  
  43.     public String addToCart(@PathVariable final long pk, @PathVariable final String item) {  
  44.           
  45.         cartService.addToCart(pk, item);  
  46.         return "redirect:/cart/" + pk;  // Do redirect to prevent double post  
  47.           
  48.     }  
  49.       
  50.     @RequestMapping(value = "/cart/{pk}/removefromcart/{item}", method = RequestMethod.GET)  
  51.     public String removeFromCart(@PathVariable final long pk, @PathVariable final String item) {  
  52.           
  53.         cartService.removeFromCart(pk, item);  
  54.         return "redirect:/cart/" + pk;  // Do redirect to prevent double post  
  55.           
  56.     }  
  57.   
  58. }  
  59. </item>  

Basic view (if you look at the spring context.xml file you will see that we are using spring resolver that look for views in WEB-INF/views/ and uses view name as file name with extension .jsp):

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>  
  2. <%@ page isELIgnored="false" %>  
  3. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
  4. <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>  
  5.   
  6. <title>Items</title>  
  7.   
  8.   <h3>Items:</h3>  
  9.   <c:foreach var="item" items="${items}">  
  10.     <div>${item.articleNo} @ ${item.price}, qty: ${item.quantity}  <a href="${pageContext.request.contextPath}/cart/${pk}/removefromcart/${item.articleNo}">[-] remove</a> <a href="${pageContext.request.contextPath}/cart/${pk}/addtocart/${item.articleNo}">[+] add</a></div>  
  11.   </c:foreach>  
  12.   <a href="${pageContext.request.contextPath}/cart/${pk}/addtocart/${add}">add "${add}"</a>  

${pageContext.request.contextPath} in jsp allows to reference the root of your webapp. This is very usefull if your webapp context name changes from environment to environment as this kind of url building approach will work everywhere consistently. Now if you navigate to localhost:8080/cart/1 you will see contents for cart with PK 1.

Here is screenshot of the outcome:

This sums up the basic Spring webapp project. Now we can have a look at how we can do integration tests for what we have written.

Firstly we need an alternative Spring context where we can remove all the MVC part so we can use it for core testing (i.e. services, dao). Note: that I am jumping to integration tests straight away since I assume that you know (and already done) unit tests. Integration tests is the final frontier! Before doing such tests appropriate unit test needs to be done! Please do not substitute these kind of tests for unit tests as they are considerably slower to run and much harder to test thoroughly.

  1. package dp.example.shoppingcart.service.impl;  
  2.   
  3. import dp.example.shoppingcart.dto.Item;  
  4. import dp.example.shoppingcart.service.CartService;  
  5. import org.junit.Test;  
  6. import org.junit.runner.RunWith;  
  7. import org.springframework.beans.factory.annotation.Autowired;  
  8. import org.springframework.test.context.ContextConfiguration;  
  9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
  10. import org.springframework.transaction.annotation.Transactional;  
  11.   
  12. import java.math.BigDecimal;  
  13. import java.util.HashMap;  
  14. import java.util.List;  
  15. import java.util.Map;  
  16.   
  17. import static org.junit.Assert.*;  
  18.   
  19. /** 
  20.  * User: denispavlov 
  21.  * Date: 29/11/2013 
  22.  * Time: 13:17 
  23.  */  
  24. @RunWith(SpringJUnit4ClassRunner.class)  
  25. @ContextConfiguration(locations = { "/spring/testContext.xml" })  
  26. @Transactional  
  27. public class WebCartServiceCachedTest {  
  28.   
  29.     @Autowired  
  30.     private CartService cartService;  
  31.   
  32.     /** 
  33.      * This is NOT unit test but rather an integration suite that proves that 
  34.      * our business logic works as expected using actual db and all the wiring. 
  35.      */  
  36.     @Test  
  37.     public void testCartCRUDOperationsIntegrationTest() throws Exception {  
  38.   
  39.         List<item> items;  
  40.   
  41.         // Check empty initially  
  42.         items = cartService.getItemsInCart(1L);  
  43.   
  44.         assertNotNull(items);  
  45.         assertTrue(items.isEmpty());  
  46.   
  47.         // Add one item and check contents of cart  
  48.         cartService.addToCart(1L, "ABC");  
  49.         items = cartService.getItemsInCart(1L);  
  50.   
  51.         assertNotNull(items);  
  52.         assertFalse(items.isEmpty());  
  53.         assertEquals(1, items.size());  
  54.         assertEquals("ABC", items.get(0).getArticleNo());  
  55.         assertEquals(new BigDecimal(1).compareTo(items.get(0).getQuantity()), 0);  
  56.   
  57.         // Add same item and check contents of cart  
  58.         cartService.addToCart(1L, "ABC");  
  59.         items = cartService.getItemsInCart(1L);  
  60.   
  61.         assertNotNull(items);  
  62.         assertFalse(items.isEmpty());  
  63.         assertEquals(1, items.size());  
  64.         assertEquals("ABC", items.get(0).getArticleNo());  
  65.         assertEquals(new BigDecimal(2).compareTo(items.get(0).getQuantity()), 0);  
  66.   
  67.         // Add another item and check contents of cart  
  68.         cartService.addToCart(1L, "DEF");  
  69.         items = cartService.getItemsInCart(1L);  
  70.   
  71.         assertNotNull(items);  
  72.         assertFalse(items.isEmpty());  
  73.         assertEquals(2, items.size());  
  74.         // we re-map the items in list to have deterministic assertions  
  75.         final Map<string, item=""> byArticle = new HashMap<string, item="">();  
  76.         for (final Item item : items) {  
  77.             byArticle.put(item.getArticleNo(), item);  
  78.         }  
  79.         // deterministic assertions by article no  
  80.         assertTrue(byArticle.containsKey("ABC"));  
  81.         assertEquals(new BigDecimal(2).compareTo(byArticle.get("ABC").getQuantity()), 0);  
  82.         assertTrue(byArticle.containsKey("DEF"));  
  83.         assertEquals(new BigDecimal(1).compareTo(byArticle.get("DEF").getQuantity()), 0);  
  84.   
  85.         // Remove one item  
  86.         cartService.removeFromCart(1L, "DEF");  
  87.         items = cartService.getItemsInCart(1L);  
  88.   
  89.         assertNotNull(items);  
  90.         assertFalse(items.isEmpty());  
  91.         assertEquals(1, items.size());  
  92.         assertEquals("ABC", items.get(0).getArticleNo());  
  93.         assertEquals(new BigDecimal(2).compareTo(items.get(0).getQuantity()), 0);  
  94.   
  95.         // Remove last item  
  96.         cartService.removeFromCart(1L, "ABC");  
  97.         items = cartService.getItemsInCart(1L);  
  98.   
  99.         assertNotNull(items);  
  100.         assertTrue(items.isEmpty());  
  101.   
  102.     }  
  103.   
  104.     /** 
  105.      * Since we already established that the CRUD works assertions in this test are specific 
  106.      * to making sure that we get the cached list back, so we do not need to check individual elements. 
  107.      * 
  108.      * @throws Exception 
  109.      */  
  110.     @Test  
  111.     public void testCachingIsWorking() throws Exception {  
  112.   
  113.         // Check empty initially  
  114.         List<item> items1st = cartService.getItemsInCart(2L);  
  115.         assertEquals(0, items1st.size());  
  116.   
  117.         // add cart item  
  118.         cartService.addToCart(2L, "ABC");  
  119.         List<item> items2nd = cartService.getItemsInCart(2L);  
  120.         assertEquals(1, items2nd.size());  
  121.   
  122.         // now we can ask for the cart contents again and assert that is it same instance of list  
  123.         List<item> items3rd = cartService.getItemsInCart(2L);  
  124.         assertSame(items3rd, items2nd);  
  125.   
  126.         // we expect that adding items will evict cache  
  127.         cartService.addToCart(2L, "ABC");  
  128.         List<item> items4th = cartService.getItemsInCart(2L);  
  129.         assertEquals(1, items4th.size());  
  130.         // but these list should be different now  
  131.         assertNotSame(items2nd, items4th);  
  132.   
  133.         // now check the cart contents again which should be cached object from 4th call  
  134.         List<item> items5th = cartService.getItemsInCart(2L);  
  135.         assertSame(items4th, items5th);  
  136.   
  137.         // make sure that after adding to a different cart we still have cached version of list for 2L  
  138.         cartService.addToCart(1L, "ABC");  
  139.         List<item> items6th = cartService.getItemsInCart(2L);  
  140.         assertSame(items4th, items6th);  
  141.   
  142.   
  143.     }  
  144. }  
  145. </item></item></item></item></item></item></string,></string,></item>  

The important elements are: include JUnit dependency in pom.xml, use @RunWith and @ContextConfiguration to tell JUnit what context configuration file you want to use. If you are testing DAO objects in isolation you can add @Transactional annotation to your Test suite at class level to provide the transaction support.

You also may have noticed that we use alternative context file /spring/testContext.xml. This context imports our original cache.xml and persistence.xml but provides override for spring-context.properties to supply alternative data source configurations and also does not contain the MVC configuration which are not needed for this test.

  1. <!--?xml version="1.0" encoding="UTF-8"?-->  
  2. <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.xsd  
  3. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd  
  4.   
  5.   
  6. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">  
  7.   
  8.   
  9.     <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
  10.         <property name="locations">  
  11.             <list>  
  12.                 <!-- spring-testContext will override any properties in spring-context as it comes after -->  
  13.                 <value>classpath:spring-context.properties</value>  
  14.                 <value>classpath:spring-testContext.properties</value>  
  15.             </list>  
  16.         </property>  
  17.     </bean>  
  18.   
  19.     <import resource="classpath*:spring/cache.xml">  
  20.     <import resource="classpath*:spring/persistence.xml">  
  21.   
  22.     <!-- Service to manipulate the cart -->  
  23.     <bean id="cartService" parent="txProxyTemplate">  
  24.         <property name="target">  
  25.             <bean class="dp.example.shoppingcart.service.impl.WebCartServiceCached">  
  26.                 <constructor-arg index="0" ref="cartDao">  
  27.             </constructor-arg></bean>  
  28.         </property>  
  29.     </bean>  
  30.   
  31. </import></import></beans>  

Hope you enjoyed this tutorial and let me know if any information is inaccurate or incomplete. Here is link to the full source for this:

denis-pavlov-spring3mvc-mvn2-example-updated.zip

PLEASE BARE IN MIND THIS IS AN EXAMPLE TO SHOW HOW SPRING MAVEN PROJECT IS SETUP - THIS IS NOT PRODUCTION CODE AND NEVER WAS INTENDED TO IMITATE ONE.

This page was last updated on: 29/11/2013 15:23

vSphere及vCenter安装及使用说明

简单介绍自己在实际使用vSphere及vCenter过程中遇到的一些问题以及解决方法
  • ExceptionalBoy
  • ExceptionalBoy
  • 2017年12月20日 10:25
  • 67

正则表达式(2) step by step

\\ 反斜杠\t 间隔 ('\') \n 换行 ('\') \r 回车 ('\') \d 数字 等价于[0-9] \D 非数字 等价于[^0-9] \s 空白符号 [\t\n\x0B\f\r] \S ...
  • splove1107
  • splove1107
  • 2014年02月19日 17:38
  • 307

jenkins 自动部署失败

不能部署的问题。 [INFO] --- maven-jar-plugin:2.3.2:jar (default-jar) @ mych-maven-test --- [INFO]  [I...
  • roothomes
  • roothomes
  • 2013年03月29日 10:28
  • 4864

Python/scikit-learn机器学习库(特征选取)

去除方差小的特征 设置一个方差阈值,没有达到这个方差阈值的特征都会被丢弃。  VarianceThreshold,算法输入只要求特征(X),不需要输入结果(Y)。 from skle...
  • AnneQiQi
  • AnneQiQi
  • 2017年03月16日 13:58
  • 755

二维图像中Mat::setp、Mat::step1理解

一、前言        Mat中的step为构成图像的层次,考虑到Mat多应用于二维图像,本文讨论二维图像step的含义和应用。二维图像数据存储示意图如下:                      ...
  • AP1005834
  • AP1005834
  • 2017年02月01日 19:55
  • 710

Step.js 使用教程(附源码解析)

Step.js(https://github.com/creationix/step)是控制流程工具(大小仅 150 行代码),解决回调嵌套层次过多等问题。适用于读文件、查询数据库等回调函数相互依赖,...
  • zhangxin09
  • zhangxin09
  • 2013年10月25日 15:18
  • 8460

高次同余笔记(一):baby-step-giant-step算法

我们来看这个方程: a,b,p为常数且在int内。、p是质数。 这个怎么搞? 首先x的取值肯定在0到p-1之间。 暴搜?肯定超时啊。 优化暴搜?用meet-in-the-middle? ...
  • Quack_quack
  • Quack_quack
  • 2015年11月17日 00:35
  • 942

机器学习--线性回归R语言

回归分析就是利用样本,产生拟合方程,从而进行预测。简而言之,就是你用你手头上的数据进行模型的训练,然后用你得到的模型对于新数据进行预测。一元线性回归:例子:y...
  • dingchenxixi
  • dingchenxixi
  • 2016年01月20日 16:23
  • 3913

【Caffe细致入微】Solver_Step

void Solver::Step(int iters) 简单的说,这个函数就是核心的优化方法,不断通过前向和反向传播来更新参数的过程。 【重要变量】 int average_loss...
  • u012816621
  • u012816621
  • 2016年12月30日 11:47
  • 2160

数据库设计 Step by Step (2)——数据库生命周期

转自:http://www.cnblogs.com/DBFocus/archive/2011/04/09/2010904.html 引言:数据库设计 Step by Step (1)得到这么多朋...
  • zhouhuakang
  • zhouhuakang
  • 2016年04月10日 16:16
  • 260
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Spring 3 maven 2 web application step by step
举报原因:
原因补充:

(最多只允许输入30个字)