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.