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;
}
}
在您的 ApplicationConfiguration 中更新如下:
@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。
为了解决这个问题,我已将 BaseDAO
的 save
方法更新为 return 虚拟值 1
。您可能希望 return 根据您的应用程序需要适当的值。
public Integer save(ENTITY entity) {
if (entity.getId() == null) {
insert(entity);
} else {
update(entity);
}
return 1;
}
进行这些更改后,我们将能够保存活动并进入下一个屏幕。
我正在使用 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;
}
}
在您的 ApplicationConfiguration 中更新如下:
@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。
为了解决这个问题,我已将 BaseDAO
的 save
方法更新为 return 虚拟值 1
。您可能希望 return 根据您的应用程序需要适当的值。
public Integer save(ENTITY entity) {
if (entity.getId() == null) {
insert(entity);
} else {
update(entity);
}
return 1;
}
进行这些更改后,我们将能够保存活动并进入下一个屏幕。