I have the following class, but Spring and MyBatis-Spring-Boot-Starter will not autowire my mapper.
When I run the request, I get the output from the println()
sourceMapper = null
Model
public class Source {
#Autowired
public static SourceMapper sourceMapper; #### Why isn't this set?
public static Source findOrCreate(String url) {
...
System.out.println("sourceMapper = " + sourceMapper);
source = sourceMapper.findByHost(host);
...
}
}
I followed the examples as closely as possible.
http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/
The other #Autowired Mappers in the main #Controller class that handles requests work, even though they are private.
This is the Mapper class
package ...mapper;
#Mapper
public interface SourceMapper {
...
I ran into this issue again with a new model and mapper. I tried to follow Why is my Spring #Autowired field null? and the code sample, but it was still null! I tried #Configurable, #Service, #Component.
Model
#Configurable
public class Domain {
#Autowired
private static DomainMapper domainMapper;
public static void incrementCounter(String host) {
...
Domain d = getDomainMapper().find(host, thisMonth);
public static DomainMapper getDomainMapper() {
return domainMapper;
public static void setDomainMapper(DomainMapper domainMapper) {
Domain.domainMapper = domainMapper;
Mapper
#Mapper
public interface DomainMapper {
MyBatis 3.4.5, MyBatis Spring 1.3.1, MyBatis Spring Boot Autoconfigure 1.3.1, MyBatis Spring Boot Starter 1.3.1
I fixed it with
private static DomainMapper getDomainMapper() {
// https://stackoverflow.com/a/52997701/148844
if (domainMapper == null)
domainMapper = MyApplication.getApplicationContext().getBean(DomainMapper.class);
return domainMapper;
And
MyApplication
#Autowired // for AWS
private static ApplicationContext context;
// I believe this only runs during an embedded Tomcat with `mvn spring-boot:run`.
// I don't believe it runs when deploying to Tomcat on AWS.
public static void main(String[] args) {
context = SpringApplication.run(MyApplication.class, args);
But I don't like it!
Spring will only try to inject a bean for you if another bean requires it.
Your class Source is just a normal class with bunch of static methods.
Hence it's not under creation control of Spring.
If you want to inject SourceMapper to your Source, you should mark Source with #Component or #Service so that the container will know it should create a bean of Source type for you and give you an instance of SourceMapper.
Moreover, the SourceMapper should be declared non-static, to prevent if a class access the variable before injected. And static field can be injected only if it use field setter injection.
Related
I have a component Login that depends on ValidatorService. ValidatorService is being injected/autowired in the Login constructor. ValidationServiceImpl is provided by an external API, so I can't just annotate it as #Service.
#Component
class Login {
#Autowire
public Login (ValidatorService validator) {
}
}
#SpringBootApplication
public class Starter {
public static void main(String[] args)
{
SpringApplication.run(Starter.class, args);
}
}
I'm looking for a way to register ValidatorService as a bean before #Components get scanned. Is there a way to get ApplicationContext instance before starting the application?
SpringBoot 2.0.4.RELEASE
UPDATE
I need to pass a validationId that I'll get from main(args) to this external API.
public static void main(String[] args) {
String validationId = args[0];
ValidatorService service = ExternalValidationAPI.getValidationServiceImp(validationId);
}
You should be able to declare it as a bean as such in one of your configuration classes:
#Bean
public ValidatorService validatorService(){
return new ValidatorServiceImpl();
}
This will then autowire in the ValidatorService implementation class at the point it is needed. This method needs to go in an #Configuration class (your Starter class is one).
There's a good example of how to do this here.
I believe you can solve your problem with the help of the #Configurable annotation.
Annotate your Login class with #Configurable instead of #Componenet, and when the ValidatorService object becomes available, you can initiate the Login object with it.
You need to define a ValidationService bean :
#Configuration
public class ValidationServiceConfig {
#Bean
public ValidationService validationService(#Value("${validationId}") String validationId) {
return new ValidationServiceImpl(validationId);
}
}
and run the program this way : java -jar program.jar --validationId=xxx
I solved my problem creating a Configuration class and declaring a Bean to handle the instantiation of the external service that will be injected later (as some people have suggested). In order to retrieve the program arguments I autowired DefaultApplicationArguments to retrieve program arguments with getSourceArgs():
#Configuration
public class ValidatorConfig {
#Autowired
DefaultApplicationArguments applicationArguments;
#Bean
public ValidatorService validatorService()
{
String validationId = applicationArguments.getSourceArgs()[0];
return ExternalValidationAPI.getValidationServiceImp(validationId);
}
I have a simple Spring boot application that initialize a CLI game.
import com.rpg.game.rpggame.client.RpgGameClient;
#SpringBootApplication
public class RpgGameApplication {
public static void main(String[] args) {
SpringApplication.run(RpgGameApplication.class, args);
RpgGameClient.runGame();
}
}
The runGame() uses some of my services (spring beans).
public class RpgGameClient {
#Autowired
private static GameService gameService;
public static void runGame() {
gameService.createNewGame();
}
}
But I have a NullPointerException when using my service, since Spring can not successfully inject it on my RpgGameClient class.
How can I solve it?
1) RpgGameClient is not declared as a bean candidate. So Spring ignores it.
You can do it by annotating the class with #Component for example (the simplest way) or by declaring a #Bean method that returns the instance of the class.
2) Even with that, it will not work still as #Autowired doesn't work for static fields. Spring injects dependencies into a bean and a bean is an instance of a class.
I think that runGame() should also be non static. Make all static for a bean spring makes no sense. It is the same thing for the Spring boot application class.
3) Constructor injection should be favored to field injection.
So it should be better :
#Component
public class RpgGameClient {
private GameService gameService;
public RpgGameClient(GameService gameService){
this.gameService = gameService;
}
public void runGame() {
gameService.createNewGame();
}
}
And change the Spring Boot app class to use injection dependency such as :
#SpringBootApplication
public class RpgGameApplication {
#Autowired
RpgGameClient rpgGameClient;
#PostConstruct
public void postConstruct(){
rpgGameClient.runGame();
}
public static void main(String[] args) {
SpringApplication.run(RpgGameApplication.class, args);
}
}
As far as I know spring provides some ways to inject beans into non-managed classes.
It can be done explicitly with AutowireCapableBeanFactory. (How to inject dependencies into a self-instantiated object in Spring?)
But I've faced strange (IMHO) behavior, when spring performs such injection automatically.
Here is an example with spring batch,
Configuration:
#SpringBootConfiguration
public class ProcessorJobConfig {
//.....
#Bean(name = "pullRestTemplate")
public RestTemplate createPullRestTemplate() {
RestTemplate restTemplate = restTemplateBuilder.build();
return restTemplate;
}
#Bean(name = "step")
public Step step(#Autowired ItemReader<Measurement> itemReader,
#Autowired ItemProcessor<Measurement, Event> itemProcessor,
#Autowired ItemWriter<Event> itemWriter) {
return stepBuilderFactory.get("step")
.<Measurement, Event>chunk(Integer.MAX_VALUE)
.reader(itemReader)
.processor(itemProcessor)
.writer(itemWriter)
.build();
}
#Bean(name = "restProcessorJob")
public Job job(#Qualifier("step") Step step) throws Exception {
return jobBuilderFactory.get("restProcessorJob")
.start(step)
.build();
}
#Bean
public ItemReader<Measurement> itemReader() {
RestMeasureReader restMeasureReader = new RestMeasureReader(); // Use new() explicitly
return restMeasureReader;
}
//.....
}
Reader:
public class RestMeasureReader implements ItemReader<Measurement> {
private static final Logger LOGGER = LoggerFactory.getLogger(RestMeasureReader.class);
/**
* NOTE: This field will be injected automatically by spring, even we are using new() to create instance of this class.
*/
#Autowired
#Qualifier("pullRestTemplate")
private RestTemplate restTemplate;
#Override
public Measurement read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
// do some stuff
}
}
And application itself
#EnableBatchProcessing
#EnableTask
#SpringBootApplication
public class TestAutowiredTaskApplication {
public static void main(String[] args) {
SpringApplication.run(TestAutowiredTaskApplication.class, args);
}
}
Even I use explicit new() to instantiate RestMeasureReader, its RestTemplate field will be injected afterwards.
Is it normal behavior? I do not expect spring to automatically inject fields when creating object with new().
If you are talking about using new inside of your #Configuration class, then yes it is normal behavior. This is you Spring java configs. So it's is Spring managed context. You are not going to call itemReader() in your code explicitly.
So, when you are going to do this:
#Autowired
private ItemReader<Measurement> iterReader;
you will get instance of your RestMeasureReader from Spring's IoC.
But if you will try to do explicitly call new RestMesureReader() inside of your code, you will get a new instance of RestMesureReader not a Spring Proxy with injected #Autowired fields.
Try to remove #Bean from your itemReader() method declaration and won't event be able to autowire RestMesureReader.
So basically #Configuration classes are just a Spring configuration, not a real java code. Even though you call new Spring will still return you a proxy class.
For more information check this guide.
Spring processes beans returned by methods that are annotated with #Bean
This allows you to use autowiring or livecycle callbacks when using Java configuration.
A more minimalistic example:
#Configuration
public class MyConfiguration {
#Bean
public A a() {
return new A();
}
static class A {
#Autowired
private B b;
#PostConstruct
public void onPostConstruct() {
System.out.println("postConstruct: " + b);
}
}
#Component
static class B {
}
}
Here, even if the bean named a is created manually, Spring will inject dependencies (b) and call #PostConstruct callbacks.
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.
I am using lazy loading with hibernate in my web app.
I would like to load some objects from the database at the parsing stage of the server response
#Component
public class DesignSerializer extends JsonSerializer<Design> {
#Autowired
IDesignService designService; <-- is null
}
Which is totally understandable because DesignSerializer is being instantiated with the "new" operator for each object.
I am sure there is a way to inject my bean into that serializer when ever it is created, I just don't know how.
Can you guys help me or point me in the right direction.
Solution is SpringBeanAutowiringSupport if you are using Spring Framework 2.5+.
public class DesignSerializer extends JsonSerializer<Design> {
#Autowired
IDesignService designService;
}
public DesignSerializer(){
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
...
}
I Hope that help you
We had the same problem with JsonSerializer and Spring autowiring. The solution that worked for us was to make two constructors. One for Spring which sets the dependency as a static field, and another one that is used by the Jackson initialisation.
This works because the Spring dependency injection (autowiring) happens before Jackson initialises the serializer.
#Component
public class MyCustomSerializer extends JsonSerializer<String> {
private static IDesignService designService;
// Required by Jackson annotation to instantiate the serializer
public MyCustomSerializer() { }
#Autowired
public MyCustomSerializer(IDesignService designService) {
this.designService = designService;
}
#Override
public void serialize(String m, JsonGenerator gen, SerializerProvider s) {
gen.writeObject(MyCustomSerializer.designService.method(..));
}
}
I Solved the problem by creating a static field in a different bean and then #Autowire its setter method.
#Service("ToolBox")
#Transactional
public class ToolBox
{
static Logger logger = Logger.getLogger(ToolBox.class);
private static IService service;
#Autowired
public void setService(IService service)
{
ToolBox.service = service;
}
public static IService getService()
{
return ToolBox.service;
}}
like shown in this thread: Can you use #Autowired with static fields?