You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2019/01/29 12:17:53 UTC
[isis] branch 2033-IoC updated: ISIS-2033: adds and enables spring
demo module
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch 2033-IoC
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/2033-IoC by this push:
new 5270b93 ISIS-2033: adds and enables spring demo module
5270b93 is described below
commit 5270b9394f995a74b6de18ff0548aa08d3f9fbb8
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Jan 29 13:17:46 2019 +0100
ISIS-2033: adds and enables spring demo module
Task-Url: https://issues.apache.org/jira/browse/ISIS-2033
---
.../java/org/apache/isis/applib/AppManifest.java | 23 ++++++--
.../classdiscovery/ClassDiscoveryPlugin.java | 4 +-
.../org/apache/isis/config/AppConfigLocator.java | 12 ++--
.../config/builder/IsisConfigurationDefault.java | 16 +++---
.../isis/config/builder/ModulePackageHelper.java | 65 +++++++++++++++-------
.../ClassDiscoveryPluginUsingReflections.java | 9 ++-
.../isis/core/plugins/ioc/weld/WeldFactory.java | 6 --
.../application/manifest/DomainAppAppManifest.java | 7 +++
.../modules/spring/SpringContextListener.java | 26 +++++++++
.../modules/spring/SpringModuleCDIBridge.java | 17 ++++++
.../modules/spring/dom/customer/Customer.java | 32 +++++++++++
.../modules/spring/dom/customer/CustomerMenu.java | 30 ++++++++++
.../spring/dom/customer/CustomerRepository.java | 9 +++
13 files changed, 206 insertions(+), 50 deletions(-)
diff --git a/core/applib/src/main/java/org/apache/isis/applib/AppManifest.java b/core/applib/src/main/java/org/apache/isis/applib/AppManifest.java
index 52a8bc4..bb490c3 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/AppManifest.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/AppManifest.java
@@ -19,9 +19,7 @@
package org.apache.isis.applib;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -160,7 +158,7 @@ public interface AppManifest {
*/
public final static class Registry {
- public final static List<String> FRAMEWORK_PROVIDED_SERVICE_PACKAGES = Collections.unmodifiableList(Arrays.asList(
+ public final static List<String> FRAMEWORK_PROVIDED_SERVICE_PACKAGES = _Lists.of(
"org.apache.isis.applib",
"org.apache.isis.core.wrapper" ,
"org.apache.isis.core.metamodel.services" ,
@@ -171,7 +169,24 @@ public interface AppManifest {
"org.apache.isis.objectstore.jdo.datanucleus.service.support" ,
"org.apache.isis.objectstore.jdo.datanucleus.service.eventbus" ,
"org.apache.isis.viewer.wicket.viewer.services",
- "org.apache.isis.core.integtestsupport.components"));
+ "org.apache.isis.core.integtestsupport.components");
+
+ public final static List<String> FRAMEWORK_PROVIDED_TYPES_FOR_SCANNING = _Lists.of(
+ "org.apache.isis.config.AppConfig",
+ "org.apache.isis.applib.IsisApplibModule",
+ "org.apache.isis.core.wrapper.WrapperFactoryDefault",
+ "org.apache.isis.core.metamodel.JdoMetamodelUtil",
+ "org.apache.isis.core.runtime.RuntimeModule",
+ "org.apache.isis.viewer.restfulobjects.rendering.RendererContext"
+
+// "org.apache.isis.objectstore.jdo.applib.service" ,
+// "org.apache.isis.objectstore.jdo.datanucleus.service.support" ,
+// "org.apache.isis.objectstore.jdo.datanucleus.service.eventbus" ,
+// "org.apache.isis.viewer.wicket.viewer.services",
+// "org.apache.isis.core.integtestsupport.components"
+
+
+ );
private static Registry instance = new Registry();
public static Registry instance() {
diff --git a/core/commons/src/main/java/org/apache/isis/core/plugins/classdiscovery/ClassDiscoveryPlugin.java b/core/commons/src/main/java/org/apache/isis/core/plugins/classdiscovery/ClassDiscoveryPlugin.java
index 0ca1f6d..edf39c2 100644
--- a/core/commons/src/main/java/org/apache/isis/core/plugins/classdiscovery/ClassDiscoveryPlugin.java
+++ b/core/commons/src/main/java/org/apache/isis/core/plugins/classdiscovery/ClassDiscoveryPlugin.java
@@ -19,7 +19,7 @@
package org.apache.isis.core.plugins.classdiscovery;
-import java.util.List;
+import java.util.Collection;
import org.apache.isis.commons.internal.context._Plugin;
@@ -29,7 +29,7 @@ public interface ClassDiscoveryPlugin {
public ClassDiscovery discover(String packageNamePrefix);
//TODO missing java-doc
- public ClassDiscovery discover(List<String> packageNamePrefixes);
+ public ClassDiscovery discover(Collection<String> packageNamePrefixes);
//TODO missing java-doc
//TODO [ahuber] REVIEW how is this different from discover(String)
diff --git a/core/config/src/main/java/org/apache/isis/config/AppConfigLocator.java b/core/config/src/main/java/org/apache/isis/config/AppConfigLocator.java
index c7a6ca9..1ecdfee 100644
--- a/core/config/src/main/java/org/apache/isis/config/AppConfigLocator.java
+++ b/core/config/src/main/java/org/apache/isis/config/AppConfigLocator.java
@@ -52,6 +52,9 @@ public final class AppConfigLocator {
// for sanity check
private final static Set<String> criticalServices() {
+
+ //TODO [2033] use classes from AppManifest.Registry instead
+
return _Sets.newLinkedHashSet(_Lists.of(
"org.apache.isis.applib.services.registry.ServiceRegistry",
"org.apache.isis.config.IsisConfiguration",
@@ -82,8 +85,6 @@ public final class AppConfigLocator {
"org.apache.isis.applib.services.homepage.HomePageProviderService"
- //"org.jboss.seam.conversation.spi.SeamConversationContext"
-
));
}
@@ -166,9 +167,10 @@ public final class AppConfigLocator {
LOG.info(String.format("Located AppConfig '%s' via ServiceLoader.", appConfigClass.getName()));
- Supplier<Stream<Class<?>>> onDiscover = () -> Stream.concat(
- Stream.of(appConfigClass),
- appConfigImpl.isisConfiguration().streamClassesToDiscover());
+ Supplier<Stream<Class<?>>> onDiscover = appConfigImpl.isisConfiguration()::streamClassesToDiscover;
+
+ onDiscover.get()
+ .forEach(type->probe.println("on discover include '%s'", type));
// as we are in a non-managed environment, we need to bootstrap CDI ourself
_CDI.init(onDiscover);
diff --git a/core/config/src/main/java/org/apache/isis/config/builder/IsisConfigurationDefault.java b/core/config/src/main/java/org/apache/isis/config/builder/IsisConfigurationDefault.java
index a45ecfe..99c6494 100644
--- a/core/config/src/main/java/org/apache/isis/config/builder/IsisConfigurationDefault.java
+++ b/core/config/src/main/java/org/apache/isis/config/builder/IsisConfigurationDefault.java
@@ -19,6 +19,8 @@
package org.apache.isis.config.builder;
+import static org.apache.isis.commons.internal.base._With.requires;
+
import java.awt.Color;
import java.awt.Font;
import java.util.Enumeration;
@@ -26,14 +28,12 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
import java.util.StringTokenizer;
import java.util.stream.Stream;
import javax.enterprise.inject.Vetoed;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import org.apache.isis.applib.AppManifest;
import org.apache.isis.commons.internal.base._Lazy;
import org.apache.isis.commons.internal.base._Strings;
@@ -44,8 +44,8 @@ import org.apache.isis.config.IsisConfiguration;
import org.apache.isis.config.IsisConfigurationException;
import org.apache.isis.config.resource.ResourceStreamSource;
import org.apache.isis.core.commons.exceptions.IsisException;
-
-import static org.apache.isis.commons.internal.base._With.requires;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
@@ -98,7 +98,8 @@ class IsisConfigurationDefault implements IsisConfiguration {
// Type Discovery
// ////////////////////////////////////////////////
- _Lazy<Integer> typeDiscovery = _Lazy.threadSafe(()->ModulePackageHelper.runTypeDiscovery(this.getAppManifest()));
+ _Lazy<Set<Class<?>>> typeDiscovery = _Lazy.threadSafe(()->
+ ModulePackageHelper.runTypeDiscovery(this.getAppManifest()));
public void triggerTypeDiscovery() {
typeDiscovery.get();
@@ -106,8 +107,7 @@ class IsisConfigurationDefault implements IsisConfiguration {
@Override
public Stream<Class<?>> streamClassesToDiscover() {
- triggerTypeDiscovery();
- return AppManifest.Registry.instance().streamAllTypes();
+ return typeDiscovery.get().stream();
}
diff --git a/core/config/src/main/java/org/apache/isis/config/builder/ModulePackageHelper.java b/core/config/src/main/java/org/apache/isis/config/builder/ModulePackageHelper.java
index 4fb5d5c..75294dd 100644
--- a/core/config/src/main/java/org/apache/isis/config/builder/ModulePackageHelper.java
+++ b/core/config/src/main/java/org/apache/isis/config/builder/ModulePackageHelper.java
@@ -19,7 +19,10 @@
package org.apache.isis.config.builder;
+import static org.apache.isis.commons.internal.base._With.requires;
+
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -27,9 +30,6 @@ import java.util.stream.Stream;
import javax.xml.bind.annotation.XmlElement;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import org.apache.isis.applib.AppManifest;
import org.apache.isis.applib.annotation.DomainObject;
import org.apache.isis.applib.annotation.DomainObjectLayout;
@@ -42,33 +42,34 @@ import org.apache.isis.applib.annotation.ViewModel;
import org.apache.isis.applib.annotation.ViewModelLayout;
import org.apache.isis.applib.fixturescripts.DiscoverableFixtureScript;
import org.apache.isis.applib.fixturescripts.FixtureScript;
-import org.apache.isis.commons.internal.collections._Lists;
import org.apache.isis.commons.internal.collections._Sets;
+import org.apache.isis.commons.internal.context._Context;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
import org.apache.isis.commons.internal.reflection._Reflect;
import org.apache.isis.core.plugins.classdiscovery.ClassDiscovery;
import org.apache.isis.core.plugins.classdiscovery.ClassDiscoveryPlugin;
-import static org.apache.isis.commons.internal.base._With.requires;
+import lombok.val;
+import lombok.extern.slf4j.Slf4j;
/**
* @since 2.0.0-M2
*/
+@Slf4j
class ModulePackageHelper {
- private static final Logger LOG = LoggerFactory.getLogger(ModulePackageHelper.class);
-
- public static int runTypeDiscovery(final AppManifest appManifest) {
+ public static Set<Class<?>> runTypeDiscovery(final AppManifest appManifest) {
- final List<String> moduleAndFrameworkPackages =
+ final Set<Class<?>> moduleAndFrameworkTypesForScanning =
findAndRegisterTypes(appManifest);
- return moduleAndFrameworkPackages.size();
+ return moduleAndFrameworkTypesForScanning;
}
// -- HELPER
-
- private static Stream<String> modulePackageNamesFrom(final AppManifest appManifest) {
+
+ private static Stream<Class<?>> modulesFrom(final AppManifest appManifest) {
final List<Class<?>> modules = appManifest.getModules();
@@ -77,27 +78,51 @@ class ModulePackageHelper {
"If an appManifest is provided then it must return a non-empty set of modules");
}
- return modules.stream()
+ return modules.stream();
+ }
+
+ private static Stream<String> modulePackageNamesFrom(final AppManifest appManifest) {
+ return modulesFrom(appManifest)
.map(Class::getPackage)
.map(Package::getName);
}
- private static List<String> findAndRegisterTypes(final AppManifest appManifest) {
+ private static Set<Class<?>> findAndRegisterTypes(final AppManifest appManifest) {
requires(appManifest, "appManifest");
- LOG.info(String.format(
+ log.info(String.format(
"Discover the application's domain and register all types using manifest '%s' ...",
appManifest.getClass().getName()) );
+
+ val typesForScanning = new HashSet<Class<?>>();
+ AppManifest.Registry.FRAMEWORK_PROVIDED_TYPES_FOR_SCANNING.stream()
+ .map(name -> {
+ try {
+ return _Context.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ throw _Exceptions.unrecoverable(e);
+ }
+ })
+ .forEach(typesForScanning::add);
+
+ modulesFrom(appManifest)
+ .forEach(typesForScanning::add);
+
+ typesForScanning.add(appManifest.getClass());
+
+ //FIXME [2033] at this point we should have all we need, let CDI take over
+ // and let then CDI Bean intercepter make entries into the registry
+
final AppManifest.Registry registry = AppManifest.Registry.instance();
- final List<String> moduleAndFrameworkPackages = _Lists.newArrayList();
+ final Set<String> moduleAndFrameworkPackages = new HashSet<>();
moduleAndFrameworkPackages.addAll(AppManifest.Registry.FRAMEWORK_PROVIDED_SERVICE_PACKAGES);
-
modulePackageNamesFrom(appManifest)
.forEach(moduleAndFrameworkPackages::add);
-
+ moduleAndFrameworkPackages.add(appManifest.getClass().getPackage().getName());
+
final ClassDiscovery discovery = ClassDiscoveryPlugin.get().discover(moduleAndFrameworkPackages);
final Set<Class<?>> domainServiceTypes = _Sets.newLinkedHashSet();
@@ -162,7 +187,7 @@ class ModulePackageHelper {
registry.setViewModelTypes(withinPackageAndNotAnonymous(packagesWithDotSuffix, viewModelTypes));
registry.setXmlElementTypes(withinPackageAndNotAnonymous(packagesWithDotSuffix, xmlElementTypes));
- return moduleAndFrameworkPackages;
+ return typesForScanning;
}
static <T> Set<Class<? extends T>> withinPackageAndNotAnonymous(
@@ -186,7 +211,7 @@ class ModulePackageHelper {
}
}
//TODO [2039] we may need to re-think this policy, there should not be surprising use-cases
- LOG.warn("Skipping a service for registration because due to not being part of the packagess to include: " + className);
+ log.warn("Skipping a service for registration because due to not being part of the packagess to include: " + className);
return false;
}
diff --git a/core/plugins/discovery-reflections/src/main/java/org/apache/isis/core/plugins/classdiscovery/reflections/ClassDiscoveryPluginUsingReflections.java b/core/plugins/discovery-reflections/src/main/java/org/apache/isis/core/plugins/classdiscovery/reflections/ClassDiscoveryPluginUsingReflections.java
index 43726da..3b226e4 100644
--- a/core/plugins/discovery-reflections/src/main/java/org/apache/isis/core/plugins/classdiscovery/reflections/ClassDiscoveryPluginUsingReflections.java
+++ b/core/plugins/discovery-reflections/src/main/java/org/apache/isis/core/plugins/classdiscovery/reflections/ClassDiscoveryPluginUsingReflections.java
@@ -16,14 +16,13 @@
*/
package org.apache.isis.core.plugins.classdiscovery.reflections;
-import java.util.List;
-
-import org.reflections.scanners.SubTypesScanner;
-import org.reflections.util.ClasspathHelper;
+import java.util.Collection;
import org.apache.isis.commons.internal.context._Context;
import org.apache.isis.core.plugins.classdiscovery.ClassDiscovery;
import org.apache.isis.core.plugins.classdiscovery.ClassDiscoveryPlugin;
+import org.reflections.scanners.SubTypesScanner;
+import org.reflections.util.ClasspathHelper;
public class ClassDiscoveryPluginUsingReflections implements ClassDiscoveryPlugin {
@@ -34,7 +33,7 @@ public class ClassDiscoveryPluginUsingReflections implements ClassDiscoveryPlugi
}
@Override
- public ClassDiscovery discover(List<String> packageNamePrefixes) {
+ public ClassDiscovery discover(Collection<String> packageNamePrefixes) {
ReflectManifest.prepareDiscovery(); //TODO [ahuber] REVIEW why is this required?
return ReflectDiscovery.of(packageNamePrefixes);
}
diff --git a/core/plugins/ioc-weld/src/main/java/org/apache/isis/core/plugins/ioc/weld/WeldFactory.java b/core/plugins/ioc-weld/src/main/java/org/apache/isis/core/plugins/ioc/weld/WeldFactory.java
index 336dff8..4627805 100644
--- a/core/plugins/ioc-weld/src/main/java/org/apache/isis/core/plugins/ioc/weld/WeldFactory.java
+++ b/core/plugins/ioc-weld/src/main/java/org/apache/isis/core/plugins/ioc/weld/WeldFactory.java
@@ -59,12 +59,6 @@ public class WeldFactory {
"org.jboss.weld.module.web.WeldWebModule",
- //"domainapp.application.HelloWorldAppManifest", // specific to the app
-
- "org.apache.isis.config.AppConfig",
- "org.apache.isis.applib.AppManifest",
- "org.apache.isis.core.runtime.RuntimeModule",
- "org.apache.isis.core.metamodel.JdoMetamodelUtil",
"org.apache.isis.core.wrapper.WrapperFactoryDefault",
"org.apache.isis.viewer.wicket.viewer.IsisWicketModule",
"org.apache.isis.applib.services.jdosupport.IsisJdoSupportDN5",
diff --git a/example/application/simpleapp/application/src/main/java/domainapp/application/manifest/DomainAppAppManifest.java b/example/application/simpleapp/application/src/main/java/domainapp/application/manifest/DomainAppAppManifest.java
index a85bb2d..1e6f50f 100644
--- a/example/application/simpleapp/application/src/main/java/domainapp/application/manifest/DomainAppAppManifest.java
+++ b/example/application/simpleapp/application/src/main/java/domainapp/application/manifest/DomainAppAppManifest.java
@@ -55,6 +55,13 @@ public class DomainAppAppManifest extends AppManifestAbstract2 implements AppCon
ThreadPoolSupport.HIGHEST_CONCURRENCY_EXECUTION_MODE_ALLOWED =
ThreadPoolExecutionMode.SEQUENTIAL_WITHIN_CALLING_THREAD;
+
+ //FIXME [2033] since introducing spring, debug logging seems enabled by default, we overrule here
+ ch.qos.logback.classic.Level level = ch.qos.logback.classic.Level.WARN;
+ ch.qos.logback.classic.Logger root =
+ (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
+ root.setLevel(level);
+
}
// Implementing AppConfig, to tell the framework how to bootstrap the configuration.
diff --git a/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/SpringContextListener.java b/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/SpringContextListener.java
new file mode 100644
index 0000000..7d266aa
--- /dev/null
+++ b/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/SpringContextListener.java
@@ -0,0 +1,26 @@
+package domainapp.modules.spring;
+
+import java.util.NoSuchElementException;
+
+import javax.enterprise.inject.Vetoed;
+
+import org.apache.isis.commons.internal.context._Context;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+@Vetoed // must not be managed by CDI
+public class SpringContextListener implements ApplicationContextAware {
+
+ @Override
+ public void setApplicationContext(ApplicationContext springContext) throws BeansException {
+ _Context.putSingleton(ApplicationContext.class, springContext);
+ }
+
+ public static ApplicationContext currentContext() {
+ return _Context.getOrThrow(ApplicationContext.class,
+ ()-> new NoSuchElementException(
+ "There is no Spring ApplicationContext stored on framework's _Context."));
+ }
+
+}
diff --git a/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/SpringModuleCDIBridge.java b/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/SpringModuleCDIBridge.java
new file mode 100644
index 0000000..f0b4ba4
--- /dev/null
+++ b/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/SpringModuleCDIBridge.java
@@ -0,0 +1,17 @@
+package domainapp.modules.spring;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Produces;
+import javax.inject.Singleton;
+
+import domainapp.modules.spring.dom.customer.CustomerRepository;
+
+@ApplicationScoped // not to be managed by Spring
+public class SpringModuleCDIBridge {
+
+ @Produces @Singleton
+ public CustomerRepository getCustomerRepository() {
+ return SpringContextListener.currentContext().getBean(CustomerRepository.class);
+ }
+
+}
diff --git a/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/dom/customer/Customer.java b/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/dom/customer/Customer.java
new file mode 100644
index 0000000..24e4043
--- /dev/null
+++ b/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/dom/customer/Customer.java
@@ -0,0 +1,32 @@
+package domainapp.modules.spring.dom.customer;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Nature;
+
+import lombok.Getter;
+import lombok.ToString;
+
+@DomainObject(nature=Nature.EXTERNAL_ENTITY)
+@Entity @ToString
+public class Customer {
+
+ @Id
+ @GeneratedValue(strategy=GenerationType.AUTO)
+ @Getter private Long id;
+
+ @Getter private String firstName;
+ @Getter private String lastName;
+
+ protected Customer() {}
+
+ public Customer(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+}
diff --git a/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/dom/customer/CustomerMenu.java b/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/dom/customer/CustomerMenu.java
new file mode 100644
index 0000000..1af74d8
--- /dev/null
+++ b/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/dom/customer/CustomerMenu.java
@@ -0,0 +1,30 @@
+package domainapp.modules.spring.dom.customer;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.NatureOfService;
+
+@DomainService(
+ nature = NatureOfService.VIEW_MENU_ONLY,
+ objectType = "simple.CustomerMenu"
+)
+@DomainObjectLayout(named="Customer (Spring Demo)")
+@Singleton
+public class CustomerMenu {
+
+ @Inject private CustomerRepository customerRepository;
+
+ @Action
+ @ActionLayout(cssClassFa="fa-leaf")
+ public List<Customer> findByLastName(String lastName) {
+ return customerRepository.findByLastName(lastName);
+ }
+
+}
diff --git a/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/dom/customer/CustomerRepository.java b/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/dom/customer/CustomerRepository.java
new file mode 100644
index 0000000..d4c5555
--- /dev/null
+++ b/example/application/simpleapp/module-spring/src/main/java/domainapp/modules/spring/dom/customer/CustomerRepository.java
@@ -0,0 +1,9 @@
+package domainapp.modules.spring.dom.customer;
+
+import java.util.List;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface CustomerRepository extends CrudRepository<Customer, Long> {
+ List<Customer> findByLastName(String lastName);
+}