Spring Boot Autowired null - java

I have several classes in a Spring Boot project, some work with #Autowired, some do not. Here my code follows:
Application.java (#Autowired works):
package com.example.myproject;
#ComponentScan(basePackages = {"com.example.myproject"})
#Configuration
#EnableAutoConfiguration
#EnableJpaRepositories(basePackages = "com.example.myproject.repository")
#PropertySource({"classpath:db.properties", "classpath:soap.properties"})
public class Application {
#Autowired
private Environment environment;
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Bean
public SOAPConfiguration soapConfiguration() {
SOAPConfiguration SOAPConfiguration = new SOAPConfiguration();
SOAPConfiguration.setUsername(environment.getProperty("SOAP.username"));
SOAPConfiguration.setPassword(environment.getProperty("SOAP.password"));
SOAPConfiguration.setUrl(environment.getProperty("SOAP.root"));
return SOAPConfiguration;
}
HomeController (#Autowired works):
package com.example.myproject.controller;
#Controller
class HomeController {
#Resource
MyRepository myRepository;
MyService (#Autowired does not work):
package com.example.myproject.service;
#Service
public class MyServiceImpl implements MyService {
#Autowired
public SOAPConfiguration soapConfiguration; // is null
private void init() {
log = LogFactory.getLog(MyServiceImpl.class);
log.info("starting init, soapConfiguration: " + soapConfiguration);
url = soapConfiguration.getUrl(); // booom -> NullPointerException
I do not get the SOAPConfiguration but my application breaks with a null pointer exception when I try to access it.
I have already read many Threads here and googled around, but did not find a solution yet. I tried to deliver all necessary information, please let me know if anything misses.

I guess you call init() before the autowiring takes place. Annotate init() with #PostConstruct to make it call automatically after all the spring autowiring.
EDIT: after seeing your comment, I guess you are creating it using new MyServiceImpl(). This takes away the control of the MyServiceImpl from Spring and gives it to you. Autowiring won't work in those case

Did you created a bean for the class SOAPConfiguration in any of your configuration classes? If you want to autowire a class in your project, you need to create a bean for it. For example,
#Configuration
public class SomeConfiguration{
#Bean
public SOAPConfiguration createSOAPConfiguration(){
return new SOAPConfiguration();
}
}
public class SomeOtherClass{
#Autowired
private SOAPConfiguration soapConfiguration;
}

Related

How to handle an error when a bean does not exist and could not inject in Spring-boot?

I have a standard rest-api spring-boot application.
Controller with injected service
#RestController
#RequestMapping("/foo")
#AllArgsConstructor
public class SomeController {
private final SomeService someService;
public void someMethod(){
someService.toDoSomething();
}
}
and service with injected other beans
#Setter
public class SomeService {
private AnotherVeryImportantBean anotherVeryImportantBean;
private RestTemplateBuilder restTemplate;
public void toDoSomething() {
anotherVeryImportantBean.someAction();
}
In my case, the bean AnotherVeryImportantBean is created in another dependency, which I connect to the spring-boot application. Whether to create a bean or not is decided by a variable in the application.yml file.
like this:
another.service.enabled: true
Of course I have config class for service
#Configuration
#RequiredArgsConstructor
public class SomeConfig {
private final AnotherVeryImportantBean anotherVeryImportantBean;
#Bean
public SomeService someService(RestTemplateBuilder restTemplate) {
SomeService foo = new SomeService();
foo.setAnotherVeryImportantBean(anotherVeryImportantBean);
foo.setRestTemplate(restTemplate);
return foo;
}
The problem is that this controller and service are not the only ones in the application. I would like the application not to crash completely if this particular controller and service are not formed. If the bean is not created for some reason, I just don't use that functionality (this controller).
At this point, the application crashes because AnotherService cannot be injected into someService (In case where, for some reason, it was not created).
I tried adding an annotation to config class
#ConditionalOnBean(AnotherVeryImportantBean.class)
like this:
#ConditionalOnBean(AnotherVeryImportantBean.class)
#Configuration
#RequiredArgsConstructor
public class SomeConfig {
private AnotherVeryImportantBean anotherVeryImportantBean;
#Bean
public SomeService someService(RestTemplateBuilder restTemplate) {
SomeService foo = new SomeService();
foo.setAnotherVeryImportantBean(anotherVeryImportantBean);
foo.setRestTemplate(restTemplate);
return foo;
}
But the problem is that conditional in SomeConfig checks if the bean is in the container before it is created.
How can I handle an error when a bean cannot inject another dependency into itself?

Creating an object of an #Service bean, is it possible?

i have an restful application with all the #Service,#RestController,#Repository beans. and im autowiring required beans.
Now i want to use the #service class in another class that is not managed by spring, is that possible?
These 2 classes are also in 2 diffrent maven projects if that makes any diffrence
i have tried creating a new object , as expected to no awail.
i have also tried creating diffrent constructors also to no awail.
i have googled for some anwsers but havent found any so now i turn to you experts ;)
The class i want to use!
#Service
public class ProductService {
ProductRepository repository;
#Autowired
public ProductService(ProductRepository repository){
this.repository = repository;
}
}
the Restcontroller
#RestController
#RequestMapping(path = "/product")
public class ProductResource {
#Autowired
ProductService service;
}
Repo
public interface ProductRepository extends JpaRepository<Product,Long> {
}
Here is where i want to create the service.
public static void main(String[] args) {
String chromeDriver = args[0];
String method = args[1];
String domainName = args[2];
ProductService service = new ProductService();
System.setProperty("webdriver.chrome.driver", chromeDriver);
Runner runner = new Runner(method,domainName);
runner.run();
}
As far as I know, you can instantiate a Spring-managed class in a non-Spring-managed class as long as the Spring-managed class (in this case the service) doesn't contain any Spring dependencies in it, as those will remain null (non initialized) because they are never gonna be injected by Spring.
Your service looks good to me because the ProductRepository field is not #autowired, but in your code you're creating the ProductService like this:
ProductService service = new ProductService();
While this parameterless constructor doesn't exist in the ProductService class:
#Service
public class ProductService
{
ProductRepository repository;
#Autowired
public ProductService(ProductRepository repository)
{
this.repository = repository;
}
}
I think this is not possible (I mean, it technically is, but it is not going to work).
To have access to your service class your second application needs to have access to the same Spring Context.
This is because Spring Context in your base application defines necessary dependencies that your second application doesn't have access to. One of the examples for this is ProductRepository repository (which is a way Spring Boot app will talk with the database), have no Spring Context and thus information on DB location and connection URL.
In your case you need to replicate the class and mechanism to connect to DB with DB configuration in your second project.
There's a workaround to "inject" dependencies manually. Create a managed component that stores the reference to the ApplicationContext in a static variable, so that you can access from non-managed classes:
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext applicationContext() {
return applicationContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
ApplicationContextProvider.applicationContext = applicationContext;
}
}
public class NoManagedClass {
private final ManagedClass bean;
public NoManagedClass() {
this.bean = ApplicationContextProvider.applicationContext().getBean(ManagedClass.class);
}
}
However, be aware that your non-managed objects should be created after the application context is set. You still have to start your Spring application in the main method so that the beans are initialized.

Excluding an ApplicationListener #Component in Spring Boot during tests

I am trying to have my test unit up and running, and I have encountered a weird issue. My application uses an ApplicationListener class annotated as a #Component to perform an operation during startup.
During tests I have mocked the service that contains the logic, but I found that even though Mockito's when instructions work well in controller scope, the bean is not initialized for this ApplicationListener class: instead of returning what I define in the test unit, it returns either false or null - depending on the data type returned by each method in the service.
Since I have not found any way to initialize the mocked service from the test unit for the ApplicationListener class, I have decided to exclude it. To do so I have tried different approaches, being the one most often used that of creating a test application context and change its configuration. Unfortunately, nothing I have seen is working - so I am here asking for help. If possible, I would prefer not touching the ApplicationListener class and do all related coding in the test code.
I am interested in any of the two possible solutions, if they can be done:
1.- Get the mocked behaviour during the ApplicationListener execution, but I have read somewhere that this cannot be done
2.- Exclude the #Component from the test unit somehow.
TestUnit.Java:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
public class TestConfigurationService {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private MockService mockService;
private void initMockBean () throws Exception {
when(mockService.isDoingSomething()).thenReturn(true);
}
#Before
public void setup() throws Exception {
// Spring mock context application setup
this.mockMvc = webAppContextSetup(webApplicationContext).build();
// Initialize ConsulService mock bean
initMockBean ();
}
}
TestApplication.java
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackages="my.base.package", excludeFilters = #Filter(type = FilterType.ASSIGNABLE_TYPE, classes = StartupConfiguration.class))
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
Besides what is shown in the code, I have also tried this annotation in file TestApplication.java:
#SpringBootApplication(exclude={StartupConfiguration.class})
StartupConfiguration.java
#Component
public class StartupConfiguration implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private ConfigurationService configurationService;
#Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
try {
configurationService.updateConfiguration();
} catch (Exception e) {
throw new RuntimeException ("Error", e);
}
}
}
ConfigurationService.java
public interface ConfigurationService {
public void updateConfiguration () throws Exception;
}
ConfigurationServiceImpl.java
#Service
#Transactional
public class ConfigurationServiceImpl implements ConfigurationService {
#Autowired
private MService mockService;
#Override
public void updateConfiguration() throws Exception {
if (mockService.isDoingSomething()==false)
throw new Exception ("Something went wrong");
}
}
Versions:
Spring Boot 1.5.4.RELEASE,
Java 1.8
You can create mock bean of the same type and mark it with #Primary annotation to replace real bean. You can achieve this by having test such configuration:
#Configuration
#Import(TestApplication.class)
public class TestConfiguration {
#Bean
#Primary
public ConfigurationService configurationService() {
return Mockito.mock(ConfigurationService.class);
}
}
then get this mock in test:
...
public class TestConfigurationService {
...
#Autowired
ConfigurationService configurationService;
#Before
public void setUp() {
when(mockService.isDoingSomething()).thenReturn(true);
}
}
Thanks, araxn1d. Your answer gave me the clue to solve this issue.
I mocked the StartupConfiguration class in TestUnit.java:
#MockBean
private StartupConfiguration startupConfiguration;
Though in this case I was lucky: application listeners don't have returning methods, so they don't need when test configuration. If I had required that some method there returned for example true or a value, this method would not apply.
But at least for application listeners, this is enough.

Service Component object is not loaded in Spring Batch processor

Service Component object is not loaded in Spring Batch processor.But which is working fine with Spring Testing.Please help me to find the solution for this.
public class PersonJobProcessor implements ItemProcessor<Person,Person> {
#Autowired
PersonService service;
#Override
public Person process(final Person person) throws Exception {
//user service variable here
}
}
Error Message:-
Action:Consider defining a bean of type 'PersonService' in your configuration.
Below configuration is working fine
#SpringBootTest
#RunWith(SpringRunner.class)
public class PersonServiceTest {
#Autowired
PersonService service;
public void testmethod(){
service.method();// works without issues
}
}
Looks like missing SpringContext inside PersonJobProcessor. Did you add #Component on this class?

How to disable Spring autowiring in unit tests for #Configuration/#Bean usage

I want configure a component test using spring-test configuration inner class (#Configuration). Tested components has some services which I'd like to mock for the test. These services are classes (no interface used) and have spring annotations (#Autowired) in them. Mockito can easily mock them, however, I found no way of disabling spring autowiring.
Example how I can easily reproduce:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Test
public void test() {
}
#Configuration
#ImportResource("/spring/component-config.xml")
static class Beans {
#Bean
ThirdPartyService createThirdPartyService() {
return mock(ThirdPartyService.class);
}
}
}
public class ThirdPartyService {
#Autowired
Foo bar;
}
public class TestedBean {
#Autowired
private ThirdPartyService service;
}
In this example "TestBean" represents the service to be mocked. I would NOT like "bar" to be injected by spring! #Bean(autowire = NO) does not help (in fact, that's the default value).
(Please save me from "use interfaces!" comments - the mocked service can be 3rd party which I can't do anything with.)
UPDATE
Springockito partially solves the problem, as long as you don't have to have anything else to configure (so you can't use configuration class with Springockito - it does not support it), but use mocks only.
Still looking for pure spring solution, if there's any...
Here is my solution to your problem:
import static org.mockito.Mockito.mockingDetails;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MockitoSkipAutowireConfiguration {
#Bean MockBeanFactory mockBeanFactory() {
return new MockBeanFactory();
}
private static class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
#Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockingDetails(bean).isMock();
}
}
}
and then just
#Import(MockitoSkipAutowireConfiguration.class)
in your test #Configuration and you are all set
I solved it by creating FactoryBean for my bean instead of just mocking bean. At this way Spring don't try to autowire fields.
Factory bean helping class:
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private final Class<T> clazz;
public MockitoFactoryBean(Class<T> clazz) {
this.clazz = clazz;
}
#Override public T getObject() throws Exception {
return mock(clazz);
}
#Override public Class<T> getObjectType() {
return clazz;
}
#Override public boolean isSingleton() {
return true;
}
}
Actual test context part:
#Configuration
public class TestContext {
#Bean
public FactoryBean<MockingService> mockingService() {
return new MockitoFactoryBean<>(MockingService.class);
}
}
Check Spring profiles. You don't need to disable auto wiring, you need to inject different beans for different configuration.
You could add the mocked service manually to the spring application context via org.springframework.beans.factory.config.SingletonBeanRegistry#registerSingleton. This way the mock is not post-processed by spring and spring does not attempt to autowire the mock. The mock itself will be injected into your tested bean.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Autowired
private ThirdPartyService thirdPartyServiceMock;
#Test
public void test() {
}
#Configuration
static class Beans {
#Autowired
private GenericApplicationContext ctx;
#Bean
TestedBean testedBean() {
ctx.getBeanFactory().registerSingleton("thirdPartyService", mock(ThirdPartyService.class));
return new TestedBean();
}
}
public static class ThirdPartyService {
#Autowired
Object bar;
}
public static class TestedBean {
#Autowired
private ThirdPartyService service;
}
}
I am in quite the same situation.
What I found that if you do not set the context loader by #ContextConfiguration annotation on your test class, the default context loader will be used, which derived from AbstractGenericContextLoader. I had a look at its source and turned out it registers all the bean post processors which are responsible for reading annotations such #Autowired. In other words, annotation config is enabled by default.
So the main problem is that there are two configurations which are in conflict: in the java config we said that autowiring is not needed, while the autowired annotation tells the opposite. The real question is how to disable the annotation processing in order to eliminate the undesired configuration.
As far as I know there is no such spring implementation of ContextLoader which would not be derived from AbstractGenericContextLoader so I guess the only we can do is to write our own. It would be something like this:
public static class SimpleContextLoader implements ContextLoader {
#Override
public String[] processLocations(Class<?> type, String... locations) {
return strings;
}
#Override
public ApplicationContext loadContext(String... locations) throws Exception {
// in case of xml configuration
return new ClassPathXmlApplicationContext(strings);
// in case of java configuration (but its name is quite misleading)
// return new AnnotationConfigApplicationContext(TestConfig.class);
}
}
Of course it would be worth to spend more time to find out how to implement ContextLoader properly.
Cheers,
Robert
There are so many ways of doing this, I'm pretty sure that this answer will be incomplete, but here are a few options...
As currently seems to be recommended practice, use constructor injection for your services rather than autowiring the fields directly. This makes testing like this so much easier.
public class SomeTest {
#Mock
private ThirdPartyService mockedBean;
#Before
public void init() {
initMocks(this);
}
#Test
public void test() {
BeanUnderTest bean = new BeanUnderTest(mockedBean);
// ...
}
}
public class BeanUnderTest{
private ThirdPartyService service;
#Autowired
public BeanUnderTest(ThirdPartyService ThirdPartyService) {
this.thirdPartyService = thirdPartyService;
}
}
By doing that, you can also mix up autowired and mocked services by autowiring into the test itself and then constructing the beans under test with the most useful mix of autowired and mocked beans.
A reasonable alternative is to use Spring profiles to define stub services. This is particularly useful when wish to use the same stubbed features in multiple tests:
#Service
#Primary
#Profile("test")
public class MyServiceStub implements MyService {
// ...
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
#ActiveProfiles({"test"})
public class SomeTest {
// ...
}
By using the #Primary annotation, it ensures that this stub bean will be used instead of any other bean implementing the MyService interface. I tend to use this approach for things like email services, where by changing profile, I'm able to switch between a real mail server and Wiser.

Categories