Announcement Announcement Module
Collapse
No announcement yet.
@Configurable extension to load context files as well. Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • @Configurable extension to load context files as well.

    I love the idea of the @Configurable aspect. I think it is especially useful at the boundaries of application modules where there is some interaction between two disparate domains. I want developers to be able to instantiate classes from my domain in the most intuitive way possible (e.g. without getting them from a bean factory).

    I am trying to understand why this @Configurable aspect didn't go one step further and allow for the spring context file to be loaded as part of the configuration as well so that the end user of my particular domain doesn't have to know which spring context bundled in my module to load.

    So before I plow ahead with a separate but similar annotation, I am looking for advice. Here is part of the documentation for the similar annotation we wrote:

    Bootstrapping Spring-Injected Domain Objects

    Problem. We want to use Spring to inject dependencies into our domain objects, but we do not want a consumer to have to configure the Spring application context in order for injection to take place. This couples the consumer to the Spring framework and requires the consumer to have knowledge of how the Spring application context is loaded. Some consumers of our code may be discouraged by this requirement if they are unfamiliar with Spring.

    Furthermore, consumers will have to “instantiate” Spring-injected domain objects by asking the Spring Bean Factory for an instance rather than through the more intuitive Java new keyword.

    Solution. Provide a common bootstrap technique that will decouple consumers from Spring and allow “new” instantiation of Spring-injected domain objects. Domain objects will reference a concrete implementation of the BootstrapDefinition interface via the @Bootstrap annotation which contains a callback with instructions to bootstrap the object on construction. @Bootstrap annotated objects are advised by an AspectJ aspect that invokes the callback post-construction. By adding the following bean definition to the Spring application context, Spring indirectly dependency-injects all @Bootstrap annotated objects:

    Code:
    	<bean class="com.shelter.spring.aspects.BootstrappingBeanConfigurerAspect"
    		factory-method="aspectOf" />
    Thus, loading the application context in the usual way results in the objects being injected by Spring. Alternatively, instantiating the BootstrapDefinition implementation written to a Spring application causes the application context to be loaded and also @Bootstrap annotated objects will be injected.

    Example. We will add bootstrap capability to a service layer class.

    First we add a bean to our application context:

    springContext.xml

    Code:
    <bean class="com.shelter.spring.aspects.BootstrappingBeanConfigurerAspect"
    		factory-method="aspectOf" />
    HomePolicyStoreService.java

    Code:
    @Bootstrap(definitionClass="com.shelter.policy.config.store.PolicyStoreBootstrap")
    public class HomePolicyStoreService
    {
       protected HomePolicyStoreRepository _repo;
       protected PolicyWipInquiryService _wipInquiryService;
       protected Mapper _mapper;
    
    ...
    }
    Notice the required definitionClass attribute of the @Bootstrap annotation. This tells the bootstrapping aspect which bootstrapper to use to inject this class. Let’s take a look at the bootstrapper itself.

    PolicyStoreBootstrap.java

    Code:
    public class PolicyStoreBootstrap extends SpringBootstrapDefinition
    {   
       /**
        * Allows for a higher-level bootstrapper to load a DataSource
        */
       public PolicyStoreBootstrap()
       {      
          if (parentContext.getBean("dataSource") == null)
          {
             throw new RuntimeException("You must provide a data source");
          }
       }
     
       public PolicyStoreBootstrap(DataSource ds)
       {
          registerSingleton("dataSource", ds);
          bootstrap();
       }
    
       @Override
       public String getAppContext()
       {
          return "/com/shelter/policy/config/store/springContext.xml";
       }
    }

    The PolicyStoreBootstrap extends SpringBootstrapDefinition, an abstract implementation of BootstrapDefinition that is tailored to loading Spring application contexts as the means to dependency-inject an advised object. Note that there is nothing in HomePolicyStoreService that couples it to Spring directly. By providing a different Bootstrap class, we could inject the service with anoter IoC framework like Google Guice or simply by hand if we chose.

    Notice also the way that PolicyStoreBootstrap defines requirements for the service to function. In this case, the service requires a DataSource instance to function. It would not make any sense to build a “default” DataSource into the Spring context for this service, as we cannot predict how a consumer will use the service. Instead, we require the consumer to provide one to use the service. We provide two options in this case:

    1. Instantiate PolicyStoreBootstrap with the DataSource constructor. This causes a Spring application context to be created on-the-fly which then becomes the parent context for the service’s application context (i.e. the dataSource bean is visible to the service’s context).

    Code:
    public class TestHomePolicyStoreService extends TestCase
    {
       @Override
       protected void setUp() throws Exception
       {
          super.setUp();
          
          DataSource dataSource = // … create datasource here
          
          new PolicyStoreBootstrap(dataSource); // it’s magic
       }
       
       public void testInjectionWiring() {
          HomePolicyStoreService service = new HomePolicyStoreService();
          assertNotNull(service._mapper);
          assertNotNull(service._repo);
          assertNotNull(service._wipInquiryService);
       }
    }
    2. The consumer class is @Bootstrap annotated itself and its bootstrapper loads a datasource into a Spring application context. By default, the SpringBootstrapDefinition class creates new application contexts for each instance that is bootstrapped (where the context file has not already been loaded by another bootstrapper) and chains them together through a parent-child relationship to form a larger context.

    Code:
    @Bootstrap(definitionClass="test.shelter.policy.config.store.TestPolicyStoreBootstrap")
    public class TestHomePolicyStoreService extends TestCase
    {
       public void testInjectionWiring() {
          HomePolicyStoreService service = new HomePolicyStoreService();
          assertNotNull(service._mapper);
          assertNotNull(service._repo);
          assertNotNull(service._wipInquiryService);
       }
    }
    References TestPolicyStoreBootstrap.java:

    Code:
    public class TestPolicyStoreBootstrap extends SpringBootstrapDefinition
    {  
       @Override
       public String getAppContext()
       {
          return "/test/shelter/policy/config/store/springContext.xml";
       }
    }
    References springContext.xml:

    Code:
    <beans>
    	<!--  Provides a datasource to test cases -->
    	<bean id="dataSource" class="com.shelter.unittesting.TestDatabase"/>
    </beans>

  • #2
    Idea

    This seems like an interesting idea. I've had the same issues as he is describing. I want to use Spring but I don't want my clients to be dependent on it or even aware of it for most things.

    I'll be interested in any replies.

    Comment

    Working...
    X