Another spock Mock is not injected than the one defined in a Spring Boot Test - spring-boot

I have written a Spring Boot Test in Spock, where the injected dependency needs to do something in the constructor of a bean.
I have created a mock and annotated it with #SpringBean in my spec and defined some behavior for the mock, and then I expected it to be injected into the bean, which is dependent on the mocked class. Some mocked instance is injected into the bean however I can not define the behavior, which I expected.
I can see from (https://spockframework.org/spock/docs/1.3/module_spring.html):
Spock’s #SpringBean actually creates a proxy in the ApplicationContext which forwards everything to the current mock instance. The type of the proxy is determined by the type of the annotated field.
The proxy attaches itself to the current mock in the setup phase, that is why the mock must be created when the field is initialized
It seems like that instance is not the same, which I guess the quote in the documentation says, but I expected to be able to define the behavior, and have that in the bean, but the methods do only return the default values, which suggest that the mock injected has not has it behavior redefined.
I can see from another extended test I have implemented, that it works as I expected i.e. one can define behavior in the spec and injected as expected, but for the purpose of the example I have isolated the issue.
A workaround is that to create the class explicitly and inject the dependency, but in the test where this issue occurs the dependency has to be injected in a dependency quite deep in the dependency hierarchy.
can anybody pinpoint what I am missing?
The test is as follows:
package com.test.springmockspock
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import spock.lang.Specification
#SpringBootTest
class SpringspockmockApplicationSpec extends Specification {
#Autowired
SpringspockmockApplication sut
#SpringBean
Factory factory = Mock() {
getInt() >> 99
}
#Autowired
Factory autowiredFactory
void 'hello'() {
when:
def insideSUTFactory = sut.factory
then:
factory.getInt() == 99
// sut.intFromFactory == 42 // this should have been 99
autowiredFactory == insideSUTFactory // true
factory == insideSUTFactory // false (but should have been true)
}
}
The Factory class is:
package com.test.springmockspock;
import org.springframework.stereotype.Component;
#Component
public class Factory {
public Producer get() {
return new Producer();
}
public int getInt() {
return 45;
}
}
package com.test.springmockspock;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#ComponentScan("com.test.springmockspock")
#SpringBootApplication
public class SpringspockmockApplication implements CommandLineRunner {
private final Factory factory;
private final Producer producer;
public SpringspockmockApplication(Factory factory) {
this.factory = factory;
this.producer = factory.get();
return;
}
public static void main(String[] args) {
SpringApplication.run(SpringspockmockApplication.class, args);
}
public Factory getFactory() {
return factory;
}
#Override
public void run(String... args) throws Exception {
System.out.println("Hello world");
}
public int getIntFromFactory() {
return factory.getInt();
}
}
The build.gradle.kts
plugins {
groovy
application
id("org.springframework.boot") version "2.5.0"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
}
repositories {
mavenCentral()
}
dependencies {
// Use the latest Groovy version for Spock testing
testImplementation("org.codehaus.groovy:groovy:3.0.7")
// Use the awesome Spock testing and specification framework even with Java
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.spockframework:spock-spring:2.0-M4-groovy-3.0")
testImplementation("org.spockframework:spock-core:2.0-M4-groovy-3.0")
testImplementation("junit:junit:4.13.1")
testRuntimeOnly("net.bytebuddy:byte-buddy:1.11.0") // allows mocking of classes (in addition to interfaces)
testRuntimeOnly("org.objenesis:objenesis:3.2") // allows mocking of classes without default constructor (together with ByteBuddy or CGLIB)
// This dependency is used by the application.
implementation("com.google.guava:guava:30.0-jre")
implementation("org.springframework.boot:spring-boot-starter")
}
application {
mainClass.set("com.test.springmockspock.App")
}

You have already quoted the exact issue. The #SpringBean will create a proxy, that is later attached to the specification, as in the Spock world, the interactions are managed by the specification not the individual mocks.
Now, the spring context will be initialized before the mock is attached to the specification, so you only get the proxy instance without any interactions. Then this proxy is injected into the constructor, where it will answer with it's default behavior (returning null or 0). After the context is initialized, the mocks will be attached to the specification and can now handle interactions.

Related

How does Spring know where to search for Components or Beans?

In an interview i was asked by the interviewer that "How does Spring know where to search for Components or Beans?".
As I was not aware about the internal flow details I was not able to answer the question properly.
I said through #Component and #Bean we can find. But the interviewer was not happy with the question.
If anybody knows please share your knowledge. TIA
I love to nswer interview questions. Read below...
#ComponentScan
If you understand Component Scan, you understand Spring.
Spring is a dependency injection framework. It is all about beans and wiring in dependencies.
The first step of defining Spring Beans is by adding the right annotation — #Component or #Service or #Repository.
However, Spring does not know about the bean unless it knows where to search for it.
This part of “telling Spring where to search” is called a Component Scan.
You define the packages that have to be scanned.
Once you define a Component Scan for a package, Spring would search the package and all its sub packages for components/beans.
Defining a Component Scan
If you are using Spring Boot, check the configuration in Approach 1.
If you are doing a JSP/Servlet or a Spring MVC application without
using Spring Boot, use Approach 2.
Approach 1: Component Scan in a Spring Boot Project
If your other package hierarchies are below your main app with the #SpringBootApplication annotation, you’re covered by the implicit Component Scan.
If there are beans/components in other packages that are not sub-packages of the main package, you should manually add them as #ComponentScan
Consider below class
package com.in28minutes.springboot.basics.springbootin10steps;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
#SpringBootApplication
public class SpringbootIn10StepsApplication {
public static void main(String[] args) {
ApplicationContext applicationContext =
SpringApplication.run(SpringbootIn10StepsApplication.class, args);
for (String name: applicationContext.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
#SpringBootApplication is defined in the SpringbootIn10StepsApplication class which is in the package com.in28minutes.springboot.basics.springbootin10steps
#SpringBootApplication defines an automatic Component Scan on the package com.in28minutes.springboot.basics.springbootin10steps.
You are fine if all your components are defined in the above package or a sub-package of it.
However, let’s say one of the components is defined in package com.in28minutes.springboot.somethingelse
In this case, you would need to add the new package into Component Scan.
You have two options:
Option 1:
#ComponentScan(“com.in28minutes.springboot”)
#SpringBootApplication
public class SpringbootIn10StepsApplication {...}
Option 2:: Define as array
#ComponentScan({"com.in28minutes.springboot.basics.springbootin10steps","com.in28minutes.springboot.somethingelse"})
#SpringBootApplication
public class SpringbootIn10StepsApplication {...}
Approach 2: Non-Spring Boot Project
Option 1:
#ComponentScan(“com.in28minutes)
#Configuration
public class SpringConfiguration {...}
Option 2:
#ComponentScan({"com.in28minutes.package1","com.in28minutes.package2"})
#Configuration
public class SpringConfiguration {...}
XML application context:
<context:component-scan base-package="com.in28minutes" />
Specific multiple packages:
<context:component-scan base-package="com.in28minutes.package1, com.in28minutes.package2" />
The IoC (Inversion of Control) container, represented in Spring by the class ApplicationContext, is the brain behind all of it. It all comes down to using reflection in a really powerful way.
To simplify, let's consider the following steps (all done through reflection):
Search all classes in the classpath
From those classes, get all classes annotated with #Component
For each class annotated with #Component, create a new instance of that class
Check for dependencies, i.e, for each created instance, check all fields annotated with #Autowired and create an instance for each one of them.
Keep everything in the context so they can be used later.
The remaining of this answer is an oversimplified version of how this happens as if we did it ourselves. Thankfully, Spring exists and we don't need to do this ourselves.
The annotations
#Retention(RetentionPolicy.RUNTIME)
public #interface Node {}
#Retention(RetentionPolicy.RUNTIME)
public #interface Wire { }
Some annotated classes for testing
#Node
public class ServiceA {
#Wire
private ServiceB serviceB;
public void doAStuff() {
System.out.println("A stuff");
serviceB.doBStuff();
}
}
#Node
public class ServiceB {
public void doBStuff() {
System.out.println("B stuff");
}
}
The IoC Container
import org.reflections.Reflections;
/* dependency org.reflections:reflections:0.9.12 */
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class IoC {
private final Map<Class<?>, Object> allNodes = new HashMap<>();
public void start() {
Reflections reflections = new Reflections(IoC.class.getPackageName());
Set<Class<?>> nodeClasses = reflections.getTypesAnnotatedWith(Node.class);
try {
for (Class<?> c : nodeClasses) {
Object thisInstance = c.getDeclaredConstructor().newInstance();
for (Field f : c.getDeclaredFields()) {
f.setAccessible(true);
if (f.getDeclaredAnnotation(Wire.class) != null) {
Object o = f.getType().getDeclaredConstructor().newInstance();
f.set(thisInstance, f.getType().cast(o));
}
}
allNodes.put(c, thisInstance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public <T> T getNodeByType(Class<T> cls) {
return cls.cast(allNodes.get(cls));
}
}
And the main class to get it all started.
public class Application {
public static void main(String[] args) {
IoC ioc = new IoC();
ioc.start();
ServiceA serviceA = ioc.getNodeByType(ServiceA.class);
serviceA.doAStuff();
}
}
This will output:
A stuff
B stuff
Of course, Spring is a lot more powerful (and robust) than this. It allows for custom package scanning using #ComponentScan, beans of the same type with different names, singleton/prototype scoped beans, constructor wiring, properties files injection, amongst many other things. When it comes to Spring Boot, the #SpringBootApplication annotation make sure it finds and wire all #Controller annotated classes and set up a Netty/Jetty/Tomcat embedded server to listen to the requests and redirect to the proper controller based on the annotated types.
Well where to search for the beans is defined by the #ComponentScan which can be annotated on the #Configuration class that is used to bootstrap Spring.
For example , it has an attribute called scanBasePackages which tells Spring to scan the beans (A class that is annotated with #Component or its sterotypes such as #Service , #Repository , #Controller etc. ) from certain packages and its sub-packages only.
Then for each bean that are registered , it goes on see if there are any methods annotation with #Bean.If yes, also register them as beans.

Is it possible to parameterize a JUnit Jupiter test with beans from a Spring ApplicationContext?

I would like to write a unit test which is executed for every Spring bean of a given type. JUnit5's parameterized tests offer a lot of possibilities, but I don't know how to inject beans into a method source as it has to be a static method.
Is there a way to determine the parameters of a JUnit5 test based on Spring's application context?
For starters, a factory method configured via #MethodSource does not have to be static. The second sentence in the User Guide explains that.
Factory methods within the test class must be static unless the test class is annotated with #TestInstance(Lifecycle.PER_CLASS); whereas, factory methods in external classes must always be static.
Thus, if you use #TestInstance(PER_CLASS) semantics, your #MethodSource factory method can be non-static and can therefore access the ApplicationContext injected into the test instance.
Here's an example that demonstrates that for beans of type String, with an intentional failure for the bar bean.
import java.util.stream.Stream;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
#SpringJUnitConfig
#TestInstance(PER_CLASS)
class SpringBeansParameterizedTests {
#Autowired
ApplicationContext applicationContext;
#ParameterizedTest
#MethodSource
void stringBeans(String bean) {
assertEquals(3, bean.length());
}
Stream<String> stringBeans() {
return applicationContext.getBeansOfType(String.class).values().stream();
}
#Configuration
static class Config {
#Bean
String foo() {
return "foo";
}
#Bean
String bar() {
return "barf";
}
}
}
If you don't want to work directly with the ApplicationContext, you can simplify the solution by having the collection of all such beans of a given type (String in this example) injected directly, as follows.
#SpringJUnitConfig
#TestInstance(PER_CLASS)
class SpringBeansParameterizedTests {
#Autowired
List<String> stringBeans;
#ParameterizedTest
#MethodSource
void stringBeans(String bean) {
assertEquals(3, bean.length());
}
Stream<String> stringBeans() {
return this.stringBeans.stream();
}
#Configuration
static class Config {
#Bean
String foo() {
return "foo";
}
#Bean
String bar() {
return "barf";
}
}
}
The usage of the #TestFactory might help.
Actually I stumbled across a post that does a pretty similar (or the same) thing as you do on github.
Let your Test run with the SpringExtenion and use the injected Beans as parameters for our Test.

How do I post-process beans of #Configuration classes that define more #Beans in JavaConfig?

In Spring XML, I can define a bean that instantiates a class annotated with #Configuration. When I do, that bean is post-processed. Any methods inside that class with #Bean are also added to the container. How do I perform a similar post-processing in JavaConfig?
Here's the XML version:
<bean id="test" class="com.so.Test">
<property name="prop" value="set before instantiating #Beans defined in Test"/>
</bean>
The associated Test class:
#Configuration
class Test {
private String prop;
void setProp(final String prop) {
this.prop = prop;
}
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
If I use Spring XML Config, both test and needThisBeanToo are available in the container. needThisBeanToo is added via a BeanPostProcessor, though I can't recall which one. If I use JavaConfig, only test is available in the container. How do I make needThisBeanToo available to the container? #Import would work, except that prop being set is required for needThisBeanToo to be initialized correctly.
The part that makes all of this complicated is that Test is vended from a library I'm consuming. I don't control Test, nor can I change it. If I drive it from JavaConfig, it would look like this:
#Configuration
class MyConfiguration
{
#Bean
Test test() {
Test test = new Test();
test.setProp("needed to init `needThisBeanToo` and others");
return test;
}
}
The JavaConfig example does not instantiate needThisBeanToo despite it being defined in Test. I need to get needThisBeanToo defined, preferably without doing it myself, since I don't want to copy code I don't own. Delegation isn't attractive, since there are a number of subsequent annotations/scopes defined on needThisBeanToo (and others defined inside Test).
Your problem is is that you're ignoring the #Configuration annotation completely. Why is that?
When code reaches this line Test test = new Test(); it just doesn't do anything with #Configuration. Why? Because annotation is not something that a constructor is aware of. Annotation only marks some meta-data for the class. When spring loads classes it searches for annotations, when you call a constructor of a class, you don't. So the #Configuration is just ignored because you instantiate Test with new Test() and not through spring.
What you need to do is to import Test as a spring bean. Either via XML as you showed in your question OR using #Import. You problem with prop is that the setter isn't called because that's just not the way to do it. What you need to be doing is either do something like that:
#Configuration
class Test {
private String prop = "set before instantiating #Beans defined in Test";
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
Or to create a property in spring (this is a different subject) and inject the value:
#Configuration
class Test {
#Autowired
#Value("${some.property.to.inject}") // You can also use SPeL syntax with #{... expression ...}
private String prop;
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
You can also create a bean of type String and inject it as follows:
#Configuration
class Test {
#Autowired
#Qualifer("nameOfBeanToInject")
private String prop;
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
In the last case you can define your original MyConfiguration with this bean:
#Configuration
#Import(Test.class)
class MyConfiguration
{
#Bean(name = "nameOfBeanToInject")
String test() {
return "needed to init `needThisBeanToo` and others";
}
}
In any case you have to import Test either using #Import or as a normal XML bean. It won't work by calling the constructor explicitly.
Here's a way to handle vended #Configuration classes that require some properties to be set prior to creating their #Beans:
Vended #Configuration class:
#Configuration
class Test {
private String property;
public setProperty(final String property) {
this.property = property;
}
#Bean
PropertyUser propertyUser() {
return new PropertyUser(property);
}
#Bean
SomeBean someBean() {
// other instantiation logic
return new SomeBeanImpl();
}
}
Here's the consuming #Configuration class:
#Configuration
class MyConfig {
#Bean
static String myProperty() {
// Create myProperty
}
/**
* Extending Test allows Spring JavaConfig to create
* the beans provided by Test. Declaring
* Test as a #Bean does not provide the #Beans defined
* within it.
*/
#Configuration
static class ModifiedTest extends Test {
ModifiedTest() {
this.setProperty(myProperty());
}
#Override
#Bean
SomeBean someBean() {
return new SomeBeanCustomImpl(this.propertyUser());
}
}

How to Initialize Jersey Application (ResourceConfig) With Spring?

I'm using Jersey 2 and Spring, and I'm trying to initialize my Jersey application (i.e. the class derived from ResourceConfig) with parameters from the Spring context.
Background: I have a single Jersey application that I build (i.e. a single WAR) and I deploy it across a server cluster with different Spring configurations on different servers to enable or disable different parts of the server, e.g. some of the servers have /search resources turned on, etc. This was really easy in Jersey 1.0: I just put,
<context:component-scan base-package="com.mycompany.resources.search"/>
in a Spring config to have Jersey scan that particular package and enable the JAX-RS resource providers in it.
Now in Jersey 2.0 the Spring <context:component-scan ... /> doesn't work, so resources have to be programmatically registered in a startup class derived from ResourceConfig:
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("com.mycompany.resources.search");
}
}
So far so good, but I need to conditionally scan that package, and I can't figure out how to get any Spring configuration into the MyApplication class. I thought that constructor injection might work:
public class MyApplication extends ResourceConfig {
#Autowired
public MyApplication(#Qualifier("my-config") MyConfiguration myConfiguration) {
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.search");
}
}
}
However HK2 complains that it can't find a default constructor to use... so this indicates to me that DI is in play in the construction of this class, but that the DI isn't using Spring.
Similarly, using the the Spring bean lifecycle doesn't work:
public class MyApplication extends ResourceConfig implements InitializingBean {
#Autowired
private MyConfiguration myConfiguration;
public MyApplication() {
}
#Override
public void afterPropertiesSet() throws Exception {
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.search");
}
}
}
(The afterPropertiesSet method isn't called.)
So now I'm stuck: is there any way to configure a Jersey ResourceConfig application object using Spring?
UPDATE:
I accepted #JohnR's answer below but I'll also include my eventual solution which I think is a bit cleaner. #JohnR's answer was to have the object initialized twice: first by Spring and then by Jersey/HK2. When Spring initializes the object you cache the dependencies in a static member, and then when Jersey/HK2 initializes it later you can retrieve the dependencies.
I ended up doing this:
public class MyApplication extends ResourceConfig {
public MyApplication() {
ApplicationContext rootCtx = ContextLoader.getCurrentWebApplicationContext();
MyConfiguration myConfiguration = rootCtx.getBean(MyConfiguration.class);
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.whatever");
}
}
}
Rather than having the object initialized twice, we let Jersey/HK2 initialize it but then we retrieve the dependencies from Spring.
Both solutions are vulnerable to timing: they both assume that Spring is initialized before Jersey/HK2.
Expanding on my previous comment:
Trying to extend ResourceConfig is dangerous if you don't know what you're doing. Jersey becomes unpredictable, and if you try to subclass it into an Abstract class, Jersey crashes.
Instead, the JAX-RS specification provides us with a very useful interface called Feature: It allows you to register any classes you want as if you were configuring your own application. Furthermore, you don't need to use the awkward AbstractBinder, you just specify what contracts you register your classes with.
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
// Don't use #Component here, we need to inject the Spring context manually.
public class MySpringFeature implements Feature {
#Context
private ServletContext servletContext;
private ApplicationContext applicationContext;
#Autowired
private MySecurityDAO mySecurityDAO;
#Autowired
private MySpringResponseFilter myResponseFilter;
#Override
public boolean configure(FeatureContext context) {
if(this.servletContext == null) {
return false; // ERROR!
}
this.applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
if(this.applicationContext == null) {
return false; // ERROR!
}
// This is where the magic happens!
AutowireCapableBeanFactory bf = applicationContext.getAutowireCapableBeanFactory();
bf.autowireBean(this);
// From here you can get all the beans you need
// Now we take a Spring bean instance,
// and register it with its appropriate JAX-RS contract
context.register(myResponseFilter, ContainerResponseFilter.class);
// Or, we could do this instead:
SomeSecurityFilter mySecurityFilter = new SomeSecurityFilter();
mySecurityFilter.setSecurityDAO(mySecurityDAO);
context.register(mySegurityFilter, ContainerRequestFilter.class);
// Or even this:
SomeOtherSpringBean someOtherBean = applicationContext.getBean(SomeOtherSpringBean.class);
context.register(someOtherBean, SomeOtherJerseyContract.class);
// Success!
return true;
}
}
And in your ResourceConfig:
public class MyApplication extends ResourceConfig() {
public MyApplication() {
register(MySpringFeature.class);
}
}
Ta-da!
So now I'm stuck: is there any way to configure a Jersey
ResourceConfig application object using Spring?
I don't think you can configure Jersey to obtain your ResourceConfig from Spring as a Spring managed bean. It's a bit hackish, but you could do something like this. Note that you'll end up with two instance of your ResourceConfig: one managed by Spring and another by Jersey:
public class MyApplication extends ResourceConfig {
// static, available to all instances
private static MyConfiguration myConfiguration;
public MyApplication() {
// when Spring creates the first instance of MyApplication, myConfiguration
// will be null because the setter wasn't called yet
if (myConfiguration != null)
{
// second instance created by jersey... Spring will have autowired
// the first instance, and myConfiguration is static
if (myConfiguration.isEnabled())
packages("com.mycompany.resources.search");
}
}
#Autowired
public void setMyConfiguration(MyConfiguration config)
{
// instance level setter saves to a static variable to make it available for
// future instances (i.e. the one created by jersey)
MyApplication.myConfiguration = config;
}
}
Again, this is fairly hackish. You'll want to make sure Spring is initialized before Jersey and look closely at any threading issues that could occur during initialization.

Inject multiple Spring beans, that inherit from shared interface, into an array within a service

I came across a situation that I had in another project that I'm not exactly sure the best way to do within Grails. To set it up, this is what I'm doing in a plain Spring project.
I have two classes that inherit from the same interface:
public interface BaseInterface {
void doSomething();
}
public class Impl1 implements BaseInterface {
public void doSomething(){
System.out.println("doing impl 1");
}
}
public class Impl2 implements BaseInterface {
public void doSomething(){
System.out.println("doing impl 2");
}
}
So far pretty standard, I have N beans that I want to call sequentially to do work. (The example is obviously trivial). Within another Java class I can then do some magic to get all the beans injected(autowired) as an array.
#Autowired(required=false)
private BaseInterface[] theWorkers;
This will give me an array of worker beans as long as I have added them to the bean container in the configuration.
Now I'm trying to do the same thing in Grails. The same formula doesn't work. Putting the #Autowired portion in a service, and creating Impl1 and Impl2 within resources.groovy does not seem to do the job. So I'm wondering what the best solution is:
1) I'm missing something simple that will make this work very easily.
2) Do something similar to what's suggested by duffymo here. I'd create a named bean in resources.groovy that used a custom factory. That factory would emit a class that would contain all the classes implementing a certain interface. I'd use something similar to the suggestion to pull the services/classes matching the criteria then have that service allow someone to iterate over it's subclasses to do work.
3) Create a named bean for each of the Impl# classes within resources.groovy and then just use their distinct names and inject all of them into the classes individually. This option would not really scale or give much dynamism, but would work.
If you get access to the Spring application context you can call getBeansOfType which returns all known beans that implement a specified interface or extend a specified base class. So I'd register each bean in resources.groovy but also a manager class that gets a reference to the application context and finds the interface implementations for you. You said you want to call them sequentially, so you should implement the Ordered interface too.
Here's the manager class (put it in src/groovy/ in the correct folder for the package and rename it to whatever you want):
package com.foo
import org.springframework.beans.factory.InitializingBean
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
class BaseInterfaceManager implements ApplicationContextAware, InitializingBean {
ApplicationContext applicationContext
List<BaseInterface> orderedImpls
void afterPropertiesSet() {
orderedImpls = applicationContext.getBeansOfType(BaseInterface).values().sort { it.order }
}
}
Then change the beans so they implement Ordered:
import org.springframework.core.Ordered;
public class Impl1 implements BaseInterface, Ordered {
public void doSomething(){
System.out.println("doing impl 1");
}
public int getOrder() {
return 42;
}
}
and
import org.springframework.core.Ordered;
public class Impl2 implements BaseInterface, Ordered {
public void doSomething(){
System.out.println("doing impl 2");
}
public int getOrder() {
return 666;
}
}
Register all three in resources.groovy (use whatever bean names you want):
beans = {
impl1(Impl1)
impl2(Impl2)
baseInterfaceManager(BaseInterfaceManager)
}
And then you can add a dependency injection in a service or controller or whatever for the baseInterfaceManager bean and use it to loop through the implementation classes in order:
class FooService {
def baseInterfaceManager
void someMethod() {
for (impl in baseInterfaceManager.orderedImpls) {
impl.doSomething()
}
}
}

Resources