Here are my classes:
public interface MyService {
// ...
}
public class MyServiceImpl implements MyService {
private MyCommand myCommand;
}
public interface MyCommand {
// ...
}
public class MyCommandImpl implements MyCommand {
private MyDAO myDAO;
}
public interface MyDAO {
// ...
}
public class MyDAOImpl implements MyDAO {
// ...
}
public class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(MyService.class).to(MyServiceImpl.class)
}
}
public class MyDriver {
#Inject
private MyService myService;
public static void main(String[] args) {
MyModule module = new MyModule();
Injector injector = Guice.createInjector(module);
MyDriver myDriver = injector.getInstance(MyDriver.class);
// Should have been injected with a MyServiceImpl,
// Which should have been injected with a MyCommandImpl,
// Which should have been injected with a MyDAOImpl.
myDriver.getMyService().doSomething();
}
}
So this takes care of injecting requests for MyServices with instances of MyServiceImpl. But I can't figure out how to tell Guice to configure MyServiceImpls with MyCommandImpl, and how to bind MyCommandImpls with MyDAOImpls.
The other bindings and injections you need should be set up just like the first one. Use #Inject wherever the instance is needed, and bind the interface to an impl in your module. I've added 4 lines below (annotated 2 injection sites and defined 2 more bindings):
public interface MyService {
// ...
}
public class MyServiceImpl implements MyService {
#Inject
private MyCommand myCommand;
}
public interface MyCommand {
// ...
}
public class MyCommandImpl implements MyCommand {
#Inject
private MyDAO myDAO;
}
public interface MyDAO {
// ...
}
public class MyDAOImpl implements MyDAO {
// ...
}
public class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(MyService.class).to(MyServiceImpl.class);
bind(MyCommand.class).to(MyCommandImpl.class);
bind(MyDAO.class).to(MyDAOImpl.class);
}
}
public class MyDriver {
#Inject
private MyService myService;
public static void main(String[] args) {
MyModule module = new MyModule();
Injector injector = Guice.createInjector(module);
MyDriver myDriver = injector.getInstance(MyDriver.class);
// Should have been injected with a MyServiceImpl,
// Which should have been injected with a MyCommandImpl,
// Which should have been injected with a MyDAOImpl.
myDriver.getMyService().doSomething();
}
}
Related
Is there a (clean) way to provide an initialized Spring Bean as a service implementation in a Java 9 module ?
The best solution I think of right now is :
module-info.java
module my.module {
provides Service with ServiceWrapper;
}
Service.java
public interface Service {
void doSomething();
}
ServiceImpl.java
#Component
public class ServiceImpl implements Service, InitializingBean {
#Autowired SomeDependency dep;
public void doSomething() {
...
}
#Override
public void afterPropertiesSet() throws Exception {
ServiceWrapper.INSTANCE.set(() -> this);
}
}
ServiceWrapper.java
public class ServiceWrapper implements Service {
static final ServiceWrapper INSTANCE = new ServiceWrapper();
private Supplier<Service> supplier;
void set(Supplier<Service> supplier) {
this.supplier = supplier;
}
public void doSomething() {
this.supplier.get().doSomething();
}
public static Service provider() {
return ServiceWrapper.INSTANCE;
}
}
Say I have the following
public abstract class MyClass {
#Resource
protected MyService myService;
doSomething() {
return myService.doSomething();
}
}
public class MyServiceV1 implements MyService {}
public class MyServiceV2 implements MyService {}
public class MyClassV1 extends MyClass {
//WANT TO USE MyServiceV1 implementation
}
public class MyClassV2 extends MyClass {
//WANT TO USE MyServiceV2 implementation
}
I am unable to specify the service implementation that I want to use in each subclass. I have considered using #Qualifier but I would have to re-declare the property in each child class and use it there, and hope that it overrides the parent.
The purpose of these classes is to provide two versions of an API at the same time. So both versions will be active simultaneously.
It does feel partially that this is an anti pattern in terms of how spring is meant to inject beans, so I am open to other approaches.
I think you can try to use Constructor injection to set a particular service in your classes. Smth like this:
public abstract class MyClass {
protected MyService myService;
doSomething() {
return myService.doSomething();
}
}
class MyClassV1 extends MyClass {
MyClassV1(MyService myService) {
this.myService = myService;
}
}
class MyClassV2 extends MyClass {
MyClassV2(MyService myService) {
this.myService = myService;
}
}
#Bean
MyClassV1 myClassV1() {
return new MyClassV1(myServiceV1());
}
#Bean
MyClassV1 myClassV2() {
return new MyClassV2(myServiceV2());
}
#Bean
MyServiceV1 myServiceV1() {
return new MyServiceV1();
}
#Bean
MyServiceV2 myServiceV2() {
return new MyServiceV2();
}
or setter injection:
public abstract class MyClass {
private MyService myService;
public void setMyService(MyService myService) {
this.myService = myService;
}
}
#Component
public class MyClass1 extends MyClass {
#Autowired #Qualifier("myService1")
#Override
public void setMyService(MyService myService) {
super.setMyService(myService);
}
}
#Component
public class MyClass2 extends MyClass {
#Autowired #Qualifier("myService2")
#Override
public void setMyService(MyService myService) {
super.setMyService(myService);
}
}
I just started looking at Guice for a new project. I have something like this
the ConfigImpl class ans Config interface
interface Config{...}
class ConfigImpl implements Config {
private static final Map<> propMap;
public ConfigImpl(Map<> propMap) {
this.propMap = someProps;
}
}
Guice injection I came up with
public class MyInjector extends AbstractModule {
protected void configure() {
bind(Config.class).to(ConfigImpl.class)
}
}
and finally
public SomeClass {
Config someConfig;
Injector injector = Guice.createInjector(new MyInjector());
someConfig = injector.getInstance(Config.class);
}
Now I am very confused as I can't find a way to pass propMap into ConfigImpl class. I'd like to know the proper way of doing it in Guice. Thanks!
You should inject propMaps from your module:
public class MyInjector extends AbstractModule {
private final Map<String,String> mapProps;
public MyInjector(Map<String,String> mapProps) {
this.mapProps = mapProps;
}
protected void configure() {
bind(Config.class).to(ConfigImpl.class).in(Scope.SINGLETON); // You most than likely want this
bind(new TypeLiteral<Map<String,String>>() {}).toInstance(mapProps); // binding for the map.
}
}
And use it like this:
public class SomeClass {
void doSomething() {
Map<String,String> mapProps = ... ;
Injector injector = Guice.createInjector(new MyInjector(mapProps));
Config someConfig = injector.getInstance(Config.class);
}
}
Also, you should fix your ConfigImpl class:
class ConfigImpl implements Config {
private final Map<String,String> propMap;
#Inject // mandatory since you use a non-default constructor
public ConfigImpl(Map<String,String> propMap) { // add the generic type of the map
this.propMap = propMap;
}
}
I want to inject MyService directly into my JerseyTest using CDI. Is it possible? MyService is succcefull injected into MyResource but I get NullPointerException when I try to access it from MyJerseyTest.
public class MyResourceTest extends JerseyTest {
#Inject
MyService myService;
private Weld weld;
#Override
protected Application configure() {
Properties props = System.getProperties();
props.setProperty("org.jboss.weld.se.archive.isolation", "false");
weld = new Weld();
weld.initialize();
return new ResourceConfig(MyResource.class);
}
#Override
public void tearDown() throws Exception {
weld.shutdown();
super.tearDown();
}
#Test
public void testGetPersonsCount() {
myService.doSomething(); // NullPointerException here
// ...
}
}
I think you need to provide an instance of org.junit.runner.Runner where you will do the weld initialization. This runner should also be responsible for providing an instance of your Test class with necessary dependencies injected. An example is shown below
public class WeldJUnit4Runner extends BlockJUnit4ClassRunner {
private final Class<?> clazz;
private final Weld weld;
private final WeldContainer container;
public WeldJUnit4Runner(final Class<Object> clazz) throws InitializationError {
super(clazz);
this.clazz = clazz;
// Do weld initialization here. You should remove your weld initialization code from your Test class.
this.weld = new Weld();
this.container = weld.initialize();
}
#Override
protected Object createTest() throws Exception {
return container.instance().select(clazz).get();
}
}
And your Test class should be annotated with #RunWith(WeldJUnit4Runner.class) as shown below.
#RunWith(WeldJUnit4Runner.class)
public class MyResourceTest extends JerseyTest {
#Inject
MyService myService;
// Test Methods follow
}
I have a controller like this
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class AccountController implements CRUDController<Long, Account> {
private AccountDao accountDao;
private AccountService accountService;
#Inject
public AccountController(AccountDao accountDao, AccountService accountService) {
this.accountDao = accountDao;
this.accountService = accountService;
}
...
I'm injecting AccountDao and AccountService using
ResourceConfig config = new ResourceConfig()
.packages("controller", "exception")
.register(new MyDIBinder());
Where MyDIBinder is contains all the bindings (e.g
AccountDaoImpl accountDaoImpl = new AccountDaoImpl();
bind(accountDaoImpl).to(AccountDao.class);
)
Now I want to write a unit test for this controller, is it possible to inject the whole AccountController instance with all of it's transitive dependencies into the test?
Something like
#Inject
AccountController accountController;
You can use the main IoC container, and just explicitly inject the test class. Jersey uses HK2 as its DI framework, and its IoC container is the ServiceLocator, which has a method inject(anyObject) that can inject any objects with dependencies that are in its registry.
For example you could do something like
public class InjectionTest {
#Inject
private TestController controller;
#Before
public void setUp() {
final Binder b = new AbstractBinder() {
#Override
public void configure() {
bindAsContract(TestController.class);
}
};
final ServiceLocator locator = ServiceLocatorUtilities.bind(new TestBinder(), b);
locator.inject(this);
}
#Test
public void doTest() {
assertNotNull(controller);
String response = controller.get();
assertEquals("Hello Tests", response);
}
}
The ServiceLocatorUtilities class is a helper class that allows us to easily create the ServiceLocator, and then we just call inject(this) to inject the InjectionTest.
If it seems repetitive to do this for all your controller tests, you may want to create an abstract base test class. Maybe something like
public abstract class AbstractControllerTest {
protected ServiceLocator locator;
private final Class<?> controllerClass;
protected AbstractControllerTest(Class<?> controllerClass) {
this.controllerClass = controllerClass;
}
#Before
public void setUp() {
final AbstractBinder binder = new AbstractBinder() {
#Override
public void configure() {
bindAsContract(controllerClass);
}
};
locator = ServiceLocatorUtilities.bind(new TestBinder(), binder);
locator.inject(this);
}
#After
public void tearDown() {
if (locator != null) {
locator.shutdown();
}
}
}
Then in your concrete class
public class TestControllerTest extends AbstractControllerTest {
public TestControllerTest() {
super(TestController.class);
}
#Inject
private TestController controller;
#Test
public void doTest() {
assertNotNull(controller);
assertEquals("Hello Tests", controller.get());
}
}
If you spent some more time, I'm sure you could come up with a better abstract test class design. It was the first thing that came to mind for me.
Note: For anything request scoped, you mayb need to just mock it. When running the unit tests, there is no request context, so the test will fail.
See Also:
Using Jersey's Dependency Injection in a Standalone application
HK2 documentation
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
#BeforeClass
public static void doTest() {
ServiceLocator serviceLocator = ServiceLocatorUtilities.bind(new AbstractBinder() {
#Override
protected void configure() {
bindAsContract(YourClass1.class);
bindAsContract(YourClass2.class);
bindAsContract(YourClass3.class);
}
});
YourClass1 yourClass1 = serviceLocator.getService(YourClass1.class);
...