[Jersey] Using HK2 to inject services into background tasks

Jersey採用HK2作為Dependency Injection framework,可以透過簡單的設定將實作的程式inject到interface.有使用過這樣功能的使用者會發現到,僅限於Restful layer可以做inject,非Restful layer就不能inject.

例如:

Interface:

1
2
3
4
@Contract
public interface Bar {
void bar();
}

Implement Bar

1
2
3
4
5
6
7
@Service
public class BarImpl implements Bar {
@Override
public void bar() {
log.info("Bar");
}
}

Background task

1
2
3
4
5
6
7
8
9
10
11
12
public class CrazyThread {
@Inject
Bar bar;

public void run() {
new Thread(() -> {
while (true) {
bar.bar();
}
}).start();
}
}

Inject BarImpl into Bar

1
2
3
4
5
6
7
8
9
10
11
12
public class MyApplication extends ResourceConfig {
public MyApplication() {

this.register(new AbstractBinder() {
@Override
protected void configure() {
bind(BarImpl.class).to(Bar.class).in(Singleton.class);
});

new CrazyThread().run();
}
}

會發現沒有辦法順利將BarImpl注射到Bar,因為CraztThread並不在container中,所以沒有辦法順利注射.

首先,我們可以透過HK2 Metadata Generator幫我們達成,它會產生注射的設定檔到META-INF/hk2-locator/default下.要確認Jersey使用的hk2版本,再匯入相關的jar檔.例如:jersey 2.22.1使用的hk2版本為2.4.0-b31,hk2-utils,hk2-api,hk2-metadata-generator一定要為2.4.0-b31

1
2
3
4
compile 'javax.inject:javax.inject:1'
compile group: 'org.glassfish.hk2', name: 'hk2-utils', version: '2.4.0-b31'
compile group: 'org.glassfish.hk2', name: 'hk2-api', version: '2.4.0-b31'
compile group: 'org.glassfish.hk2', name: 'hk2-metadata-generator', version: '2.4.0-b31'

修改CrazyThread,加入@Service

1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class CrazyThread {
@Inject
Bar bar;

public void run() {
new Thread(() -> {
while (true) {
bar.bar();
}
}).start();
}
}

最後修改MyApplication,透過ServiceLocator取得CrazyThread

1
2
3
4
5
6
7
public class MyApplication extends ResourceConfig {
public MyApplication() {
ServiceLocator serviceLocator = ServiceLocatorUtilities.createAndPopulateServiceLocator();
final CrazyThread crazyThread = serviceLocator.getService(CrazyThread.class);
crazyThread.run();
}
}

大功告成!!!

後記

在Jersey中使用HK2這個問題困惱我很久,很多沒有在resource層使用到的程式,就沒有辦法透過hk2注入.Using HK2 with Jersey (with auto-scanning!)這篇文章有提到怎麼解決這個問題,可能是早期版本的hk2必須手動去hack,嘗試他的做法不能順利執行.後來參考了jersey + grizzly + hk2: Dependency injection, but not into resourcekhasunuma/hk2-sample才解決這個問題…

參考資料:

  1. Using HK2 with Jersey (with auto-scanning!)
  2. jersey + grizzly + hk2: Dependency injection, but not into resource
  3. khasunuma/hk2-sample
  4. HK2 Metadata Generator