The Spring/HK2 Bridge

The Spring/HK2 Bridge can be used to inject Spring services into HK2 services or inject HK2 services into Spring services.

Definitions

  • A Spring service is a service that is instantiated (created) by Spring
  • An HK2 service is a service that is instantiated (created) by HK2

Injecting Spring services into HK2 services

Spring services can be injected into any injection point in HK2. In order to do this you must tell HK2 about the Spring BeanFactory which has the Spring bean definitions. This is accomplished in two steps.

In the first step you should have the ServiceLocator that contains services you wish to be injected with Spring services. You must initialize this ServiceLocator with some required Spring/HK2 bridge services. You can do this using the utility class SpringBridge. This is a code snippet that initializes a ServiceLocator:

  SpringBridge.getSpringBridge().initializeSpringBridge(aServiceLocator);

In the second step you must tell your initialized ServiceLocator about the specific Spring BeanFactory that you want it to look for services in. You do this with the SpringIntoHK2Bridge service that was added in the previous step. The following code snippet adds a Spring BeanFactory to be searched for services when injecting into HK2 services:

  public void tieBeanFactoryToLocator(ServiceLocator aServiceLocator, BeanFactory springFactory) {
      SpringIntoHK2Bridge springBridge = aServiceLocator.getService(SpringIntoHK2Bridge.class);
      springBridge.bridgeSpringBeanFactory(springFactory);
  }

Any Spring BeanFactory added with the bridgeSpringBeanFactory method will be searched for services that HK2 cannot otherwise find.

For example, if you have a service called SpringService that is created by Spring, you can inject it into an HK2 service (called HK2Service) like this:

  @Service
  public class HK2Service {
      @Inject
      private SpringService springService;
  }

Injecting HK2 services into Spring services

HK2 services can be injected into Spring services. A HK2 service can be injected into any place a normal Spring Bean can be injected. For example, if you have an HK2 service named HK2Service that is to be injected into a Spring service (called SpringService) your code could look like this:

  public class SpringService {
      private HK2Service hk2Service;
      
      public void setHK2Service(HK2Service hk2Service) {
          this.hk2Service = hk2Service;
      }
  }

All HK2 services are in a Spring Scope that is usually named "hk2". In order to do this we have provided an implementation of Scope called SpringScopeImpl. SpringScopeImpl is a Spring service that either takes a ServiceLocator instance or the name of a ServiceLocator as attributes.

This implementation of Scope can be given to any Spring ConfigurableBeanFactory. The usual way this is done is in a Spring beans.xml. The following stanza adds a SpringScopeImpl for a ServiceLocator named HK2ToSpringTest into a Spring beans.xml:

  <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
      <property name="scopes">
          <map>
              <entry key="hk2">
                  <bean class="org.jvnet.hk2.spring.bridge.api.SpringScopeImpl" >
                          <property name="ServiceLocatorName" value="HK2ToSpringTest" />
                      </bean>
              </entry>
          </map>
      </property>
  </bean>

An HK2 service is then defined by adding it to the Spring beans.xml by setting its scope to "hk2". The id of the HK2 service is formatted as per the utility API BuilderHelper.createTokenizedFilter. In the usual case this means that the id of the bean is the Contract to be looked up (though other things can be specified such as name or qualifiers). The following is an example Spring beans.xml stanza that defines an HK2 service.

    <bean id="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service"
              class="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service"
              scope="hk2" />

In the stanza above the scope was set to "hk2," implying that the HK2 SpringScopeImpl will be used to lookup the bean. This bean can then be injected into any other Spring bean in the normal way. The following stanza injects HK2Service into SpringService:

    <bean id="SpringService" class="org.jvnet.hk2.spring.bridge.test.hk2tospring.SpringService">
          <property name="HK2Service" ref="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service" />
        </bean>

To make it clear, the following is the entire Spring beans.xml which injects HK2Service into SpringService:

  <?xml version="1.0" encoding="UTF-8"?>
  <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
        
        <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="hk2">
                    <bean class="org.jvnet.hk2.spring.bridge.api.SpringScopeImpl" >
                          <property name="ServiceLocatorName" value="HK2ToSpringTest" />
                        </bean>
                </entry>
            </map>
        </property>
    </bean>
        
        <bean id="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service"
              class="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service"
              scope="hk2" />
 
        <bean id="SpringService" class="org.jvnet.hk2.spring.bridge.test.hk2tospring.SpringService">
          <property name="HK2Service" ref="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service" />
        </bean>
 
  </beans>

Bi-Directional Spring/HK2 Bridge

When using Spring and HK2 bi-directionally it is important to remember that Spring instantiates beans as soon as they are satisfied (i.e., as soon as all references are available). This can make bootstrapping difficult as Spring may try to instantiate beans before they are available in HK2. In order to avoid this you can use any of the methods Spring allows for lazy initialization. The following Spring beans.xml file uses the lazy-init mechanism for this, but other mechanism (such as the use of proxies) can also be used.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
        
        <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="hk2">
                    <bean class="org.jvnet.hk2.spring.bridge.api.SpringScopeImpl" >
                          <property name="ServiceLocatorName" value="BiDirectionalSpringBridge" />
                        </bean>
                </entry>
            </map>
        </property>
    </bean>
        
        <bean id="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_0"
              class="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_0"
              scope="hk2" 
              lazy-init="true"/>
 
        <bean id="SpringService1_1"
              class="org.jvnet.hk2.spring.bridge.test.bidirectional.SpringService1_1"
              lazy-init="true">
          <property name="HK2Service1_0" ref="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_0" />
        </bean>
        
        <bean id="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_2"
              class="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_2"
              scope="hk2" 
              lazy-init="true"/>
              
        <bean id="SpringService1_3"
              class="org.jvnet.hk2.spring.bridge.test.bidirectional.SpringService1_3"
              lazy-init="true">
          <property name="HK2Service1_2" ref="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_2" />
        </bean>
 
</beans>

In the above example SpringService1_3 has HK2Service1_2 injected into it, while HK2Service1_2 has SpringService1_1 injected into it (though you can't see that in the XML stanza), and SpringService1_1 has HK2Service1_0 injected into it. Since everything in the beans.xml is lazily initialized you can start the Spring BeanFactory either before or after you initialize the ServiceLocator.