1

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

Chloe
  • 25,162
  • 40
  • 190
  • 357

2 Answers2

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!

Community
  • 1
  • 1
Chloe
  • 25,162
  • 40
  • 190
  • 357
  • This doesn't work with unit tests and I don't think it works when deploying to Elastic Beanstalk Tomcat instance. It complains about `java.lang.NullPointerException` in `getDomainMapper()` ostensibly because `getApplicationContext()` is null. – Chloe Nov 28 '18 at 20:31
  • I manually injected the `DomainMapper` from the `Controller` before I call the static function. I don't like this, but have no other choice (other than passing the `DomainMapper` as an argument). – Chloe Nov 29 '18 at 01:41
  • Hello I'm facing this issue. What is "MyApplication" in this fix? Is there some dependency I need to use this object. – Kamarudeen Ayankunbi Nov 09 '20 at 19:02
0

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.

Mạnh Quyết Nguyễn
  • 17,677
  • 1
  • 23
  • 51
  • Is there a way to instantiate a `SourceMapper` manually? It is an interface and I don't know how it is/will be created by Spring/MyBatis. `public interface SourceMapper`. It doesn't make a lot of sense to create a `Source` bean first the way it is written, but I suppose I could re-write it if I had to. – Chloe May 11 '18 at 17:20
  • You must declare your `Source` to become a component/ bean. – Mạnh Quyết Nguyễn May 11 '18 at 17:25
  • Add `@Component / @Service / @Repository` your `@Mapper` class dude – Mạnh Quyết Nguyễn May 11 '18 at 17:27
  • Not a dude. I added `@Autowired` instances in the entry `@Controller` class and passed them down into the static methods. It worked and `SourceMapper` is set but is jumping to `InvocableHandlerMethod` in the debugger when it tries to call `sourceMapper.findByHost(host)`. But that's for another question. – Chloe May 11 '18 at 17:41
  • Your setup is very unusual. If you need help contact me, I can debug via teamview haha. – Mạnh Quyết Nguyễn May 11 '18 at 17:43
  • I encountered this problem again with a new model. I tagged the `Domain` model with `@Component` and I added getters/setters for the `DomainMapper`. I left it static because I only need one instance and it's accessed from a static `Domain.increment()`. However, it is still null, so this doesn't work. I also tried `@Configurable`. – Chloe Oct 25 '18 at 19:36
  • Forget the static methods and classes. Only utility methods should be static. Use singleton beans instead. If you need a bean in a method then that method should be non-static. And then you can use the `@Autowired` annotation to inject that bean and if you annotate your class with `@Component` annotation then you can inject that class into each of your other classes where you want to use that method. – Selindek Oct 25 '18 at 21:05