I'm getting NullPointerException when trying to inject a CDI bean into an EJB.
My ejb is
#Stateless(mappedName = "myEjb")
#TransactionManagement(TransactionManagementType.CONTAINER)
public class MyEjb implements MyEjbLocal, MyEjbRemote {
private static final Logger logger = Logger.getLogger(MyEjb .class);
#Inject
#Named("myService")
private MyService myService;
public MyEjb () {
}
public MyEjb (MyService myService) {
this.myService= myService;
}
#PostConstruct
public void postConstruct() {
logger.info("Injected myService" + myService+ " into this " + this);
}
And actually myService is always null.
Here is how I initialize myService by using CDI and a Factory
#ApplicationScoped
public class ServiceFactory {
private MyService myService;
#Inject
#Named("myDao")
private MyDao dao;
#PostConstruct
public void afterCreate() {
myService= new MyServiceImpl(dao);
}
#Produces
#ApplicationScoped
#Named("myService")
public MyService myService() {
return myService;
}
}
And MyService is just without annotations
public class MyServiceImpl implements MyService {
private final MyDao myDao;
public MyServiceImpl(MyDao dao) {
this.myDao = dao;
}
Well, if I invoke the EJB then myService is always null....what I'm missing? (bean.xml is there)
Am I missing something around the interaction ejb/cdi?
Related
Factory injection in Spring
//XML
<bean id="bar" factory-bean="barFactory" factory-method="getInstance"/>
//Java
How do you do dependency injection with only interfaces and factory classes and no configuration classes ?
//Service
public interface MyService {
void doSomething();
}
//ServiceFactory
public class MyServiceFactory {
MyService getInstance() {
//...
}
}
//Controller
#Controller
public class MyController {
#Autowired
private MyService myService;
}
you can do like this without adding the xml coding.
#Configuration
public class MyServiceFactory {
#Bean
MyService myService() {
return new MyService() {
#Override
public void doSomething() {
}
}
}
}
#Controller
public class MyController {
#Autowired
private MyService myService;
public void execute() {
myService.doSomething();
}
}
You can manually ask Spring to Autowire it.
Have your factory implement ApplicationContextAware. Then provide the following implementation in your factory:#Override public void setApplicationContext(final ApplicationContext applicationContext) { this.applicationContext = applicationContext; }
and then do the following after creating your bean:
YourBean bean = new YourBean();
applicationContext.getAutowireCapableBeanFactory().autowireBean(bean);
bean.init(); //If it has an init() method.
Is it possible to inject a mock service into a prototype bean using the #Autowired constructor? I realize I could switch to setter injection but I would prefer to use the constructor if possible.
#Component
#Scope(value = "prototype")
public class Prototype {
private DependantService dependantService;
#Autowired
public Prototype(DependantService dependantService) {
this.dependantService = dependantService;
}
}
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
// How can I inject the mock service?
ctx.getBean(Prototype.class);
}
}
Turns out there is an overloaded version of the getBean method that accepts arguments. I would downvote my on question if I could.
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
Prototype p = ctx.getBean(Prototype.class, dependantService);
// Test p
}
}
If you want to speed up your unit tests, [and do true isolated unit testing,] I suggest taking a look at the #InjectMocks mockito annotation. #SpringBootTest fires up the Spring container which is pretty cumbersome.
#Controller
public class MyController {
#Inject
private Logger log;
public methodThatNeedsTesting(){
log.info("hey this was called");
}
}
#TestInstance(Lifecycle.PER_CLASS)
#ExtendWith({ MockitoExtension.class })
class MyControllerTest {
#Mock
private Logger log;
#InjectMocks
private MyController myController;
#Test
void test_methodThatNeedsTesting() throws Exception {
myController.methodThatNeedsTesting();
// myController will not throw an NPE above because the log field has been injected with a mock
}
I am new in writing junit test for spring boot applications. Could anybody help me to understand the situation?
I have a service I’d like to test:
#Service
public class MyService {
private final JdbcTemplate jdbcTemplate;
…
#Autowired
public MyService(JdbcTemplate jdbcTemplate){
this.jdbcTemplate = jdbcTemplate;
…
}
#Async
public SomeType myMethod(SomeDTO request) {
DataSource dataSource = this.jdbcTemplate.getDataSource();
…
}
…
}
When I start my application and call service through REST API then my dataSource is correct and based on parameters from application.yml.
However, when I call it from my unit tests then this.jdbcTemplate.getDataSource() is always null.
Here my test classes:
SpringBootTestApplication:
#RunWith(SpringRunner.class)
#SpringBootTest(
classes = TestConfigurator.class
)
public abstract class SpringBootTestApplication {
}
MyServiceTest:
public class MyServiceTest extends SpringBootTestApplication {
#MockBean
private JdbcTemplate jdbcTemplate;
#Autowired
#InjectMocks
private MyService myService;
#Test
public void Test_1(){
DataSource dataSource = this.jdbcTemplate.getDataSource();
myService.getSomething(dataSource, ...)
…
}
}
Should I add something special to my TestConfigurator.class?
public class MyServiceTest extends
SpringBootTestApplication {
#MockBean
private JdbcTemplate jdbcTemplate;
private MyService myService;
#Before
public void init(){
myService = new MyService(this.jdbcTemplate);
}
#Test
public void Test_1(){
DataSource datasource = new DataSource());
when.jdbcTemplate.getDatasource()).thenReturn(datasource);
myService.getSomething(dataSource, ...)
…
}
}
I can autowire a list of service beans in spring boot but I need a way to pick the one I need using the name they have given.
#Service("myService")
public class DefaultService implements MyService {
}
#Service("myService2")
public class DefaultService2 implements MyService {
}
#Autowire
List<MyService> services;
is it possible to get DefaultService2 and DefaultService separately from the list.
If you want to get your services by name , implement the pattern factory as below:
Your service implementations :
public interface MyService {
void sayHello();
}
public class DefaultService implements MyService {
private static Logger log = LoggerFactory.getLogger(DefaultService.class);
#Override
public void sayHello() {
log.info("Hello from DefaultService");
}
}
public class DefaultService2 implements MyService {
private static Logger log = LoggerFactory.getLogger(DefaultService2.class);
#Override
public void sayHello() {
log.info("Hello from DefaultService2");
}
}
The factory interface :
public interface MyServiceFactory {
public MyService getMyServiceByName(String name);
}
The factory beans :
#Configuration
public class MyServiceFactoryBean {
#Bean
public FactoryBean serviceLocatorFactoryBean(){
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
factoryBean.setServiceLocatorInterface(MyServiceFactory.class);
return factoryBean;
}
#Bean("myService")
#Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public DefaultService defaultService(){
return new DefaultService();
}
#Bean("myService2")
#Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public DefaultService2 defaultService2(){
return new DefaultService2();
}
}
Usage :
#Autowired
MyServiceFactory myServiceFactory;
#Override
public void run(String... strings) throws Exception {
myServiceFactory.getMyServiceByName("myService").sayHello();
myServiceFactory.getMyServiceByName("myService2").sayHello();
}
Results :
2017-08-11 11:32:31.126 INFO 12827 --- [ restartedMain] c.m.test.DefaultService : Hello from DefaultService
2017-08-11 11:32:31.129 INFO 12827 --- [ restartedMain] c.m.test.DefaultService2 : Hello from DefaultService2
Add #Qualifier annotation:
#Autowired
#Qualifier("myService")
MyService myService;
Just a guess, but have you tried this?:
#Autowire
MyService myService;
#Autowire
MyService myService2;
The field name should be hint enough to Spring. Otherwise use #Qualifier as explained by albert_nil.
You can use :
#Autowired
#Qualifier("myService")
MyService myService;
Or use resource annotation. It should work.
#Resource
MyService myService;
#Resource
MyService myService2;
Try to iterate your bean in the List and identify each bean using reflection api .getClass().getSimpleName(). This will return the String name of your class.
for(MyService service : services) {
if(service.getClass().getSimpleName().equals("DefaultService")) {
MyService defaultService = service; // This is DefaultService class
}
if(service.getClass().getSimpleName().equals("DefaultService2")) {
MyService defaultService2 = service; // This is DefaultService2 class
}
}
With this, you will know what would be the Service you are using.
i can take out the beans seperately by sorting autowire order.naming is not needed now.
#Order(value=1)
#Service
public class DefaultService implements MyService {
}
#Service
#Order(value=2)
public class DefaultService2 implements MyService {
}
I'm doing unit test using spring mvc test framework.
The following is my source code:
com.exmple.main
MyController.java
#Controller
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public Map<Object, Object> myControllerFunction(#RequestBody final Object jsonRequest) {
/* do something */
return response;
}
}
MyRepository.java
#Repository
public interface MyRepository extends JpaRepository<My, String> {
#Query(value="select * from my d where (d.start_date<to_date(:date,'YYYY/DD/MM')) and (d.end_date>to_date(:date,'YYYY/DD/MM'))", nativeQuery=true)
List<My> findByDate(#Param("date") String date);
}
MyService.java
public interface MyService {
List<My> findByDate(String date);
}
MyServiceImpl.java
#Service
public class MyServiceImpl implements MyService {
#Autowired
MyRepository destRepo;
#Override
public List<My> findByDate(String date) {
List<My> listDest = destRepo.findByDate(date);
return listDest;
}
}
com.example.test
MyControllerTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={TestConfig.class})
#WebAppConfiguration
public class MyControllerTest {
private MockMvc mockMvc;
#Autowired
MyService myService;
#Autowired
protected WebApplicationContext webApplicationContext;
#Before
public void setup() throws Exception {
// this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void listAllMy() throws Exception {
}
}
TestConfig.java
#Configuration
public class TestConfig {
#Bean
public MyService myService() {
// set properties, etc.
return new MyServiceImpl();
}
}
When I run test, the following error is displayed
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException
I know the exception occurred because MyService didn't find any bean of MyRepository.
But I don't know how to create a bean of repository.
Please teach me how to create a bean of repository class using Java (not xml).
You need to enable the JPA repositories in your config class, specify the package that contains the repositories as below
#Configuration
#EnableJpaRepositories(basePackages = {
"com.example.repository"
})
public class TestConfig {
#Bean
public MyService myService() {
// set properties, etc.
return new DestinationServiceImpl();
}
}
Edit: looks like you haven't defined entityManager, and dataSource. Refer to a tutorial here and also answer to similar question here