Некоторые кандидаты:
AppFuse - В AppFuse используется Spring Framework благодаря поддержке Hibernate/iBATIS, декларативным транзакциям, связыванию зависимостей и развязке уровней.
Equinox (он же AppFuse Light) - простое CRUD-приложение, созданное в рамках Spring Live.
Spring by Example - различные примеры Spring плюс некоторые загружаемые библиотеки.
Tudu Lists - Tudu Lists - это J2EE приложение для управления списками дел. Оно основано на JDK 5.0, Spring, Hibernate и AJAX-интерфейсе (с использованием фреймворка DWR).
Я работаю в большой компании медицинского страхования, где мы активно используем Spring в бэкенде. Я покажу вам, как строится модульное приложение.
Скелет WEB-INF без каталога classes
ar
WEB-INF
web.xml
/**
* Spring related settings file
*/
ar-servlet.xml
web
moduleA
account
form.jsp
moduleB
order
form.jsp
Скелет каталог classes
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
...
Обратите внимание, как каждый пакет под br.com.ar.web соответствует каталогу WEB-INF/view. Это ключ, необходимый для запуска convention-over-configuration в Spring MVC. Как ??? полагаться на ControllerClassNameHandlerMapping
WEB-INF/ar-servlet.xml Обратите внимание на свойство basePackage, которое означает поиск любого @Controller класса в br.com.ar.view пакете. Это свойство позволяет строить модульные @Controller'ы
<!--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>
Теперь посмотрим, например, 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);
}
}
Как это работает?
Предположим, вы делаете запрос на http://127.0.0.1:8080/ar/moduleA/account/form.html
Spring извлечет путь между контекстным путем и расширением файла - выделено выше. Прочитаем извлеченный путь справа Налево
что переводится как
br.com.ar.web.moduleA.AccountController.form
Ok. Но как Spring узнает, какое представление показывать? Смотрите здесь
И о persistence связанных с этим вопросах ???
Прежде всего, смотрите здесь как мы реализуем репозиторий. Обратите внимание, что каждый запрос связанного модуля хранится в связанном с ним пакете хранилища. См. скелет выше. Здесь показан ar-persistence.xml Обратите внимание на mappingLocations и packagesToScan свойства
<?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>
Обратите внимание, я использую Hibernate. JPA должна быть правильно настроена.
Управление транзакциями и сканирование компонентов ar-service.xml Обратите внимание на две точки после br.com.ar в атрибуте выражения aop:pointcut, что означает
Любой пакет и подпакет пакета br.com.ar
<?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>
Тестирование
Для тестирования аннотированного метода @Controller смотрите здесь как
Кроме веб слоя. Обратите внимание, как я конфигурирую JNDI dataSource в методе @Before
@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);
}
}
Если вам нужен набор тестов, сделайте следующее
@RunWith(Suite.class)
@Suite.SuiteClasses(value={AccountRepositoryIntegrationTest.class})
public void ModuleASuiteTest {}
web.xml показан следующим образом
<?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>
Надеюсь, это может быть полезно. Обновите схему до версии Spring 3.0. См. справочную документацию Spring. Схема mvc, насколько я знаю, поддерживается только в Spring 3.0. Имейте это в виду