Configured: Service Providers

In Configured, a Service Provider is a class which realises some interface. You tell Configured that a given class is a Service Provider for an interface using the @ServiceProvider annotation. It's possible to have multiple Service Provider's for a given interface: you may specify a name property in the annotation to identify a given service provider. If you don't specify a name in the annotation, it is taken that the given class is the "default" Service Provider for that interface.

The idea behind ServiceProviders is similar to Spring's native @Service or @Component annotations; the difference is that @ServiceProvider is really just some metadata which allows Configured (or more specifically one of Configured's standard rules) to map an abstract type to a concrete type so it can continue building its current object graph without delegating to the outside world to resolve the dependency.

Configured's default rule for resolving ServiceProvider dependencies only resolves the concrete class which has been annotated as the Provider of the given dependency (e.g. the default Provider if none was indicated for the dependency, or otherwise the named Provider if it could be found). The dependency is then reprocessed by the rulebase with this additional information, in an attempt to locate the correct object, this could be resolved - for example - by instantiating the service provider (InstanitationRule) or (more usefully) if the Service Provider was also annotated as a Spring component, by looking up the Spring component (SpringComponentDependencyResolutionRule ).

Example

As a simple example, we're going to initialize the following set of classes:

Example Class Diagram

Here we have an ActionBean (i.e. a Stripes class) which references a pojo service: MyUserCaseController, the implementation of which references two more pojo services. We're going to tell Configured how to wire this up using 2 rules in this case.

Note that in our application context we have a bean called "serviceProviderMetadata" with a property "basePackages" on initialization it will scan the named packages and their children looking for ServiceProvider annotations, which will then subsequently use when attempting to resolve dependencies by rules referencing this bean.

One final thing to point out is that this example is a simple case - only the default Service Providers get used in each and each Service Provider only providers one Service - it's possible to annotate as Service as a ServiceProvider for more than one Service - see the ServiceProvider javadocs for more information.

  
  <!-- snippet -->
    <bean id="autowiringService"
        class="net.sourceforge.configured.spring.factory.SpringSimpleBeanGraphAutowiringServiceFactory">
        <property name="rulebase">
            <set>                
                <bean
                    class="net.sourceforge.configured.spring.rules.SpringServiceProviderDependencyResolutionRule">
                    <property name="precedence" value="1" />
                    <property name="providerMetadata" ref="serviceProviderMetadata" />                   
                </bean>
                
                <!-- If we wanted to instantiate all service providers automatically (once globally), we could do this -->
                <!-- <bean 
                    class="net.sourceforge.configured.rules.standard.InstantiationRule">
                    <property name="precedence" value="2" />                
                    <property name="instantiatedTypes">
                        <util:property-path path="&amp;serviceProviderMetadata.serviceProviderTypes"/>                      
                    </property>                 
                </bean> -->
                
                <!-- instead, we just want to lookup them up as Spring beans -->
                <bean
                    class="net.sourceforge.configured.spring.rules.SpringServiceProviderDependencyResolutionRule">
                    <property name="precedence" value="2" />
                    <property name="providerMetadata" ref="serviceProviderMetadata" />
                </bean>
            </set>
        </property>
        <property name="ignoredDependencyTypes">
            <set>
                <value>org.springframework.context.ApplicationContext</value>
            </set>
        </property>
    </bean>
    
    <!-- this bean scans the classpath for ServiceProvider annotations -->
    <bean id="serviceProviderMetadata"
        class="net.sourceforge.configured.spring.factory.ServiceProviderMetadataFactoryBean">
        <property name="basePackages">
            <list>
                <value>net.sourceforge.configured</value>
            </list>
        </property>
    </bean>
    
    <bean class="net.sourceforge.configured.spring.InjectByRulesAnnotationPostProcessor">
                <property name="autowiringService" ref="autowiringService" />
        </bean>       
        
    <!-- end snippet -->
          

And now, here's the code listing for the beans being wired. Note that there is nothing special (i.e. no annotations etc) on the interfaces, so we're leaving them out of this listing.

// This is our MVC Framework bean
public class MyActionBean {
    
    // this is how the MVC framework may integrate with Spring (e.g. if it's 
    // Stripes or Wicket)
    @SpringBean("myUseCaseController")    
    private MyUseCaseController myUseCaseController;
    
    // getters, setters and other methods ommitted.
}


@Service("myUseCaseController")
@Transactional
@ConfiguredByRules(rulebase="default")
public class MyUseCaseControllerImpl implements MyUseCaseController {
    
    private ServiceA serviceA;
    private ServiceB serviceB; 
    
    // getters, setters and other methods ommitted. 
    
}

@Service
@ServiceProvider(service=ServiceB.class)
public class ServiceBImpl implements ServiceB {

    // class body ommitted. 

}

@Service
@ServiceProvider(service=ServiceA.class)
public class ServiceAImpl implements ServiceA {

    // class body ommitted.
}

In case you're wondering what the point of that was, take a look again at the body of MyUseCaseControllerImpl - there is nothing in there about how or where ServiceA and ServiceB should come from, neither have we had to spell this out explicitly in the xml. As it happens, ServiceA and ServiceB are both Spring components (i.e. beans that Spring automatically creates for us), but this detail is only need by our rulebase - if we wanted to change where the implementation of ServiceA comes from, it's only our rulebase that needs changing.

The same would go for any dependencies of ServiceAImpl, which we've not changed here - and furthermore, because of Configured's rulebase feature, we can (if we want to) nicely separate the rules which wire ServiceAImpl from MyUseCaseControllerImpl by using a separate rulebase, which we do by adding an annoation like @ConfiguredByRules(rulebase="other_rulebase") to ServiceAImpl, but this is covered in more detail in the Rulebase Modules section.