简体   繁体   中英

Autowiring for @Service field failed

I keep looking at my codes all day along, I can't find the cause. The repository (@Repository) is working just fine, it's the @Service field that I keep failing get it autowired, I keep struggling since everything looks fine

在此处输入图片说明

The dispatched servlet:

<mvc:annotation-driven />
<context:component-scan base-package="com" /> 

com.repository

Repo.java

package com.repository;
import com.domain.Student;
import java.util.List;

public interface Repo { public List<Student> getAllSiswa();  }

RepoImplement.java

import com.domain.Student;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;

@Repository
public class RepoImplement implements Repo {
 List<Student> ls = new ArrayList<>();

public RepoImplement(){  
        Student s1 = new Student();
        s1.setNama("paul");

        Student s2 = new Student();
        s2.setNama("robert");

        ls.add(s1);
        ls.add(s2);
}

    @Override
    public List<Student> getAllSiswa() {    
       return this.ls;
    }
}

package com.service;

Serve.java

import com.domain.Student;
import java.util.List;

public interface Serve {
    void changeName(String namaBaru);
    public List<Student> newList();
}

I'm suspecting there's something wrong over here

ServeImplement.java

import com.domain.Student;
import com.repository.Repo;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ServeImplement implements Serve {
    @Autowired
    public Repo repo;  
    List<Student> s = repo.getAllSiswa(); // THIS IS SUSPECTING ME.

    @Override
    public void changeName(String namaBaru) {
        s.get(0).setNama(namaBaru); // get first Student, then update its name.
      }
    @Override
    public List<Student> newList() {
        return this.s;
    }
}

controller2.java ( it's the mapping request for serving student )

package com.controlller;
import com.domain.Student;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.service.Serve;

@Controller
public class controller2 {

    @Autowired
    public Serve serv;

    @RequestMapping("/changename")
    public ModelAndView sdaf() {
        serv.changeName("New name");
        List<Student> list = serv.newList();
        return new ModelAndView("page2", "out", list);
    }
}

The error:

Error creating bean with name 'controller2': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: public com.service.Serve com.controlller.controller2.serv; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serveImplement' defined in file [C:\\Users\\hans\\Documents\\NetBeansProjects\\WebApplication2\\build\\web\\WEB-INF\\classes\\com\\service\\ServeImplement.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.service.ServeImplement]: Constructor threw exception; nested exception is java.lang.NullPointerException

You need to understand how autowiring, or in fact Java in general, works.

Spring must create an instance of your ServImpl class, and populate the field repo with the repository bean. It does that using reflection, but what it does is basically equivalent to the following code:

ServImpl s = new ServImpl();
s.repo = theRepoBean;

So, you see that s.repo becomes non-null after the constructor has been executed. And, while executing the constructor, the following line of code is executed:

List<Student> s = repo.getAllSiswa();

At that moment, repo hasn't been initialized yet. So it's null. So you get a NullPointerException.

Use constructor injection instead of field injection, or use a @PostConstruct annotated method.

And please, make your fields private instead of public.

That said, the goal of a repository is normally to get data from a database. And the students can be modified,deleted or created in the database. So initializing a field of your service and returning always the same list defeats the whole point of having a repo. You should call repo.getAllSiswa() from newList(), every time it's called, to get the latest, up-to-date, list of students.

Because Autowired on field happens right after construction, you need to change your code in order to get it to work.

This should works:

@Service
public class ServeImplement implements Serve {

    public Repo repo;
    List<Student> s;

    @Autowired
    public ServeImplement(Repo repo) {
        this.repo = repo;
        s = repo.getAllSiswa();
    }

    @Override
    public void changeName(String namaBaru) {
        s.get(0).setNama(namaBaru); // get first Student, then update its name.
    }

    @Override
    public List<Student> newList() {
        return this.s;
    }

}

Moreover, using Autorwired annotation on constructor allow you to mark your field as final if the instance shouldn't have to change.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM