Spring CGlib autowairing 的奇怪行为?

Strange behavior with Spring CGlib autowairing?

我正在使用 Spring MVC 应用程序。

我想创建新活动:

fill data at page > controller handle this info > saving new event to DB

我已经创建了 DAO 层和存储库层。但在某些地方,它的行为非常奇怪。我以相同的方式为所有存储库创建了 Java 配置。

这是存储库配置片段:

@Configuration
@ComponentScan({ "net.lelyak.edu.repository", "net.lelyak.edu.service" })
@Import({ DatabaseDAOConfiguration.class })
public class ApplicationConfiguration {

    @Autowired
    private UserDAO userDAO;

    @Autowired
    private EventDAO eventDAO;

    @Autowired
    private TicketDAO ticketDAO;

    @Autowired
    private AuditoriumDAO auditoriumDAO;

    @Bean
    public AuditoriumRepository auditoriumRepository() {
        AuditoriumRepository auditoriumRepository = new AuditoriumRepository();
        auditoriumRepository.setDao(auditoriumDAO);
        return auditoriumRepository;
    }

    @Bean
    public EventRepository eventRepository() {
        EventRepository eventRepository = new EventRepository();
        eventRepository.setDao(eventDAO);
        return eventRepository;
    }

我在控制器级别遇到了奇怪的行为。

这是控制器代码片段:

@Controller
@RequestMapping("events")
public class EventsController {

    @Autowired
    private AuditoriumRepository auditoriumRepository;
    @Autowired
    private EventRepository eventRepository;

    @RequestMapping(path = "add", method = RequestMethod.POST)
    public String addEvent(@RequestParam Map<String, String> allRequestParams) {
       Event newEvent = new Event();
       // get auditorium id from request
       String auditoryIdString = allRequestParams.get("auditorium");
       Long auditoryId = Long.parseLong(auditoryIdString);
       Auditorium auditorium = auditoriumRepository.getById(auditoryId);
       newEvent.setAuditorium(auditorium);

AuditoriumRepository 自动连接正常。
这是调试视图的片段:

但是 EventRepository 不是:

两者的配置相同。一个存储库自动连接正常,第二个存储库失败。我刚到 Spring。我不知道为什么会这样?

这是来自 EventRepository 的代码片段:

public class EventRepository extends BaseRepository<Event, EventDAO> {

    @Autowired
    private AuditoriumRepository auditoriumRepository;

    @Override
    public int put(Event entity) {
        auditoriumRepository.put(entity.getAuditorium());
        return super.put(entity);
    }

AuditoriumRepository 代码片段:

public class AuditoriumRepository extends BaseRepository<Auditorium, AuditoriumDAO> {

    @Override
    public Auditorium preSave(Auditorium entity) {
        return entity;
    }

为了将新事件保存到数据库,我必须完全使用 EventRepository。当然,它失败了,堆栈跟踪如下:

java.lang.NullPointerException 
at net.lelyak.edu.repository.BaseRepository.put(BaseRepository.java:23) 
at net.lelyak.edu.repository.EventRepository.put(EventRepository.java:20) 
at net.lelyak.edu.repository.EventRepository$$FastClassBySpringCGLIB$de8d2a5.invoke() 
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)

我在 Windows 10 上使用 spring 4.2.4.RELEASE

更新:

BaseRepository 代码片段:

public abstract class BaseRepository<T extends BaseEntity, E extends BaseDAO<T>> {    
    private E dao;

    public E getDao() {
        return dao;
    }

    public void setDao(E dao) {
        this.dao = dao;
    }

    public int put(T entity) {
        return dao.save(preSave(entity));
    }

我不明白为什么要使用相同的配置和存储库结构。一个实例由 Spring 自动连接正常,但第二个实例失败。如何找到这个问题的根源?和一些解决方案。

更新 2:

我尝试了推荐的解决方案,并将下一个 setter 添加到 EventRepository:

public void setAuditoriumRepository(AuditoriumRepository auditoriumRepository) {
    System.out.println("EventRepository.setAuditoriumRepository");
    this.auditoriumRepository = auditoriumRepository;
}

如日志消息所示,此 setter 已执行。但是因为同样的原因一直失败。

如何解决这个问题?

尝试在 Java @Configuration class 中自己注入依赖项。更新您的 EventRepository,使其具有 AuditoriumRepository 依赖项的 setter 方法,如下所示

public class EventRepository extends BaseRepository<Event, EventDAO> {

    private AuditoriumRepository auditoriumRepository;

    @Override
    public int put(Event entity) {
        auditoriumRepository.put(entity.getAuditorium());
        return super.put(entity);
    }

    public void setAuditoriumRepository(AuditoriumRepository auditoriumRepository){
        this.auditoriumRepository=auditoriumRepository;
    } 
}

在您的 A​​pplicationConfiguration 中更新如下:

        @Bean
        public EventRepository eventRepository() {
            EventRepository eventRepository = new EventRepository();
            eventRepository.setDao(eventDAO);
            eventRepository.setAuditoriumRepository(auditoriumRepository());
            return eventRepository;
        }

我看到了你的代码,问题与 Spring 和自动装配过程无关。问题出在 BaseDAO class:

中的 save 方法中
@Override
public Integer save(ENTITY entity) {
    if (entity.getId() == null) {
        insert(entity);
    } else {
        update(entity);
    }
    return null;
}

如果您将方法更改为 return 新 inserted/updated 实体 ID,它将正常工作。

尝试对其进行硬编码以进行测试:

@Override
public Integer save(ENTITY entity) {
    if (entity.getId() == null) {
        insert(entity);
    } else {
        update(entity);
    }
    return 1;
}

当您从 save 方法 return null 然后您在 EventsController class 中进行赋值时,问题就出现了:

int eventId = eventRepository.put(newEvent); //this is null 并抛出 NullPointerException 因为您正试图将 null 分配给原始变量。

空指针异常是因为eventRepository bean中没有设置auditoriumRepository

因此在 ApplicationConfiguration.java 中创建 EventRepository bean 时,我们还需要设置 auditoriumRepository

为此,我更新了 ApplicationConfiguration 如下:

public EventRepository eventRepository() {
    EventRepository eventRepository = new EventRepository();
    eventRepository.setDao(eventDAO);
    eventRepository.setAuditoriumRepository(auditoriumRepository());
    return eventRepository;
}

并在 EventRepository class

中为 auditoriumRepository 添加了 setter
public AuditoriumRepository getAuditoriumRepository() {
    return auditoriumRepository;
}

public void setAuditoriumRepository(AuditoriumRepository auditoriumRepository) {
    this.auditoriumRepository = auditoriumRepository;
}

这将确保您看到的 NPE 得到解决。

但随后我们仍然会在不同的时间点获得 NPE,即 BaseDAO class。 save 方法是 return 一个空对象,这导致了这个 NPE。

为了解决这个问题,我已将 BaseDAOsave 方法更新为 return 虚拟值 1。您可能希望 return 根据您的应用程序需要适当的值。

public Integer save(ENTITY entity) {
    if (entity.getId() == null) {
        insert(entity);
    } else {
        update(entity);
    }
    return 1;
}

进行这些更改后,我们将能够保存活动并进入下一个屏幕。