I've finished reading the spring doc and the PetClinic sample project. Just like to see some bigger real world project that's done with Spring. Thanks.
Thingsboard which is an IoT platform with the microservice architecture. Built with Spring Boot , Spring Security , Spring Data , Spring MVC etc. Spring Initializr is the backend web API that can quickly generate a sample spring-boot project. It is exactly the backend API that powered the famous start.spring.io.
Spring Boot works well with microservices. The Spring Boot artifacts can be deployed directly into Docker containers. However, some developers don't recommend the framework for building large and monolithic apps.
Spring Boot creates a lot of unused dependencies, resulting in a large deployment file; The complex and time-consuming process of converting a legacy or an existing Spring project to a Spring Boot application; Not suitable for large-scale projects.
Spring Boot helps developers create applications that just run. Specifically, it lets you create standalone applications that run on their own, without relying on an external web server, by embedding a web server such as Tomcat or Netty into your app during the initialization process.
Spring Projects
Powerstone
Tudu Lists
Alfresco Content Management
SpringSide
Plazma
agileexpress
Zksample2
Spring Rich Client
Broadleaf Commerce
OpenERP
HISPACTA
Luigi Open Search Engine
JOSSO
HSE
Java Small Business Platform
jrecruiter
I work for a big health insurance company where we heavily use Spring in backend. I will show you how a modularized application is built.
Skeleton WEB-INF without classes directory
ar
WEB-INF
web.xml
/**
* Spring related settings file
*/
ar-servlet.xml
web
moduleA
account
form.jsp
moduleB
order
form.jsp
Skeleton classes directory
classes
/**
* Spring related settings file
*/
ar-persistence.xml
ar-security.xml
ar-service.xml
messages.properties
br
com
ar
web
moduleA
AccountController.class
moduleB
OrderController.class
br
com
ar
moduleA
model
domain
Account.class
repository
moduleA.hbm.xml
service
br
com
ar
moduleB
model
domain
Order.class
repository
moduleB.hbm.xml
service
...
Notice how each package under br.com.ar.web matchs WEB-INF/view directory. It is The key needed To run convention-over-configuration in Spring MVC. How to ??? rely on ControllerClassNameHandlerMapping
WEB-INF/ar-servlet.xml Notice basePackage property which means look for any @Controller class under br.com.ar.view package. This property allows you build modularized @Controller's
<!--Scans the classpath for annotated components at br.com.ar.web package-->
<context:component-scan base-package="br.com.ar.web"/>
<!--registers the HandlerMapping and HandlerAdapter required to dispatch requests to your @Controllers-->
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
<property name="basePackage" value="br.com.ar.web"/>
<property name="caseSensitive" value="true"/>
<property name="defaultHandler">
<bean class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
Now let's see, for instance, AccountController
package br.com.ar.web;
@Controller
public class AccountController {
@Qualifier("categoryRepository")
private @Autowired Repository<Category, Category, Integer> categoryRepository;
@Qualifier("accountRepository")
private @Autowired Repository<Account, Accout, Integer> accountRepository;
/**
* mapped To /account/form
*/
@RequestMapping(method=RequesMethod.GET)
public void form(Model model) {
model.add(categoryRepository().getCategoryList());
}
/**
* mapped To account/form
*/
@RequestMapping(method=RequesMethod.POST)
public void form(Account account, Errors errors) {
accountRepository.add(account);
}
}
How does it work ???
Suppose you make a request for http://127.0.0.1:8080/ar/moduleA/account/form.html
Spring will remove the path between context path and file extension - highlighted above. Let's read the extracted path from the right To the left
which is translated to
br.com.ar.web.moduleA.AccountController.form
Ok. But how does Spring know which view to show ??? See here
And about persistence related issues ???
First of all, see here how we implement repository. Notice each related module query is stored at its related repository package. See skeleton above. Here is shown ar-persistence.xml Notice mappingLocations and packagesToScan property
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/dataSource" resource-ref="true">
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingLocations">
<util:list>
<value>classpath:br/com/ar/model/repository/hql.moduleA.hbm.xml</value>
<value>classpath:br/com/ar/model/repository/hql.moduleB.hbm.xml</value>
</util:list>
</property>
<property name="packagesToScan">
<util:list>
<value>br.com.ar.moduleA.model.domain</value>
<value>br.com.ar.moduleB.model.domain</value>
</util:list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.connection.charSet">UTF-8</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.validator.autoregister_listeners">false</prop>
</props>
</property>
</bean>
</beans>
Notice i am using Hibernate. JPA should be properly configured.
Transaction management and component scanning ar-service.xml Notice Two dots after br.com.ar in aop:pointcut's expression attribute which means
Any package and sub-package under br.com.ar package
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="br.com.ar.model">
<!--Transaction manager - It takes care of calling begin and commit in the underlying resource - here a Hibernate Transaction -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:advice id="repositoryTransactionManagementAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="remove" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<tx:advice id="serviceTransactionManagementAdvice" transaction-manager="transactionManager">
<!--Any method - * - in service layer should have an active Transaction - REQUIRED - -->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="servicePointcut" expression="execution(* br.com.ar..service.*Service.*(..))"/>
<aop:pointcut id="repositoryPointcut" expression="execution(* br.com.ar..repository.*Repository.*(..))"/>
<aop:advisor advice-ref="serviceTransactionManagementAdvice" pointcut-ref="servicePointcut"/>
<aop:advisor advice-ref="repositoryTransactionManagementAdvice" pointcut-ref="repositoryPointcut"/>
</aop:config>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
</beans>
Testing
To test annotated @Controller method, see here how to
Other than web layer. Notice how i configure a JNDI dataSource in @Before method
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:ar-service.xml", "classpath:ar-persistence.xml"})
public class AccountRepositoryIntegrationTest {
@Autowired
@Qualifier("accountRepository")
private Repository<Account, Account, Integer> repository;
private Integer id;
@Before
public void setUp() {
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
DataSource ds = new SimpleDriverDataSource(new oracle.jdbc.driver.OracleDriver(), "jdbc:oracle:thin:@127.0.0.1:1521:ar", "#$%#", "#$%#");
builder.bind("/jdbc/dataSource", ds);
builder.activate();
/**
* Save an Account and set up id field
*/
}
@Test
public void assertSavedAccount() {
Account account = repository.findById(id);
assertNotNull(account);
}
}
If you need a suite of tests, do as follows
@RunWith(Suite.class)
@Suite.SuiteClasses(value={AccountRepositoryIntegrationTest.class})
public void ModuleASuiteTest {}
web.xml is shown as follows
<?xml version="1.0" encoding="UTF-8"?>
<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 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:ar-persistence.xml
classpath:ar-service.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>ar</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ar</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<resource-ref>
<description>datasource</description>
<res-ref-name>jdbc/dataSource</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
I hope it can be useful. Update schema To Spring 3.0. See Spring reference documentation. mvc schema, As far As i know, is supported just in Spring 3.0. Keep this in mind
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With