Я пытаюсь реализовать функцию уведомления в своем приложении.
Вариант использования:
Проблема:
NullPointerException
за NotificationRepository
, хотя я пробовал такие вещи, как аннотация @Autowired
, передача параметра метода и т. д., чтобы это работало, но это не работает вообще. Я не знаю, в чем основная причина.У меня есть класс ChapterService
, который уведомляет пользователей о создании новой главы.
@Service
public class ChapterService extends BaseService<Chapter, ObjectId, ChapterDTO>{
@Autowired
protected Mapper<ChapterDTO, Chapter> mapper;
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
public ChapterDTO create(ChapterDTO chapterDTO) {
Chapter chapter = mapper.convertToEntity(chapterDTO);
Chapter savedChapter = repository.save(chapter);
notifySubscribers(savedChapter, savedChapter.getNovelName());
return mapper.convertToDTO(savedChapter);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
private void notifySubscribers(Chapter newChapter, String novelName) {
for (PropertyChangeListener listener : propertyChangeSupport.getPropertyChangeListeners()) {
if (listener instanceof NovelSubscriber && ((NovelSubscriber) listener).getNovelName().equals(novelName)) {
listener.propertyChange(new PropertyChangeEvent(this, "chapter", null, newChapter));
}
}
}
У меня есть класс NovelSubscriber
, который определяет отношения между романом и пользователем. Новая запись была создана, когда пользователь подписался на этот роман, и приведенная ниже логика изменится и отправит уведомление соответствующим пользователям.
@Document(collection = "subscribers")
@Data
public class NovelSubscriber implements PropertyChangeListener{
@Id
private ObjectId id;
private ObjectId userId;
private String novelName;
private NotificationService notificationService;
protected static final Logger logger = LoggerFactory.getLogger(NovelSubscriber.class);
public NovelSubscriber(ObjectId userId, String novelName, NotificationService notificationService) {
this.userId = userId;
this.novelName = novelName;
this.notificationService = notificationService;
}
@Override
public void propertyChange(PropertyChangeEvent event) {
if (event.getPropertyName().equals("chapter")) {
Chapter newChapter = (Chapter) event.getNewValue();
sendNotification(newChapter);
}
}
private void sendNotification(Chapter newChapter) {
notificationService.sendNotification(userId, novelName + " have 1 new chapter: " + newChapter.getChapterTitle());
}
}
Вот NovelSubscriberService
@Service
public class NovelSubscriberService{
@Autowired
ChapterService chapterService;
@Autowired
NotificationService notificationService;
@Autowired
NovelSubscriberRepository subscriberRepository;
private List<NovelSubscriber> subscribers = new ArrayList<>();
@PostConstruct
public void init() {
loadSubscribers();
}
private void loadSubscribers(){
subscribers = subscriberRepository.findAll();
for (NovelSubscriber subscriber : subscribers) {
chapterService.addPropertyChangeListener(subscriber);
}
}
public void subscribe(String userId, String novelName) {
NovelSubscriber subscriber = new NovelSubscriber(new ObjectId(userId), novelName, notificationService);
NovelSubscriber savedNovelSubscriber = subscriberRepository.save(subscriber);
chapterService.addPropertyChangeListener(savedNovelSubscriber);
}
public void unsubscribe(String userId, String novelName) throws ItemNotFoundException {
NovelSubscriber subscriber = findByUserIdAndNovelName(new ObjectId(userId), novelName);
chapterService.removePropertyChangeListener(subscriber);
subscriberRepository.delete(subscriber);
}
private NovelSubscriber findByUserIdAndNovelName(ObjectId userId, String novelName) throws ItemNotFoundException {
return subscriberRepository
.findByUserIdAndNovelName(userId, novelName)
.orElseThrow(() -> new ItemNotFoundException("Subscriber not found"));
}
}
Вот NotificationService
@Service
public class NotificationService{
@Autowired
protected MongoRepository<Notification, ObjectId> repository; // Use MongoRepository
@Autowired
Mapper<NotificationDTO, Notification> mapper;
protected static final Logger logger = LoggerFactory.getLogger(NotificationService.class);
public void sendNotification(ObjectId userId, String content) {
if (repository == null) {
logger.error("repository is null new");
}
Notification notification = makeNewNotification(userId, content);
repository.save(notification);
}
public List<NotificationDTO> findByUserIdAndIsSeenFalse(ObjectId userId)
throws InterruptedException{
logger.debug("Finding Notifications");
List<Notification> notifications = ((NotificationRepository) repository).findByUserIdAndIsSeenFalse(userId);
logger.debug("Found Notifications: {}", notifications);
return convertToDTOs(notifications);
}
public NotificationDTO update(String id) throws ItemNotFoundException {
logger.debug("change notifcation state");
Notification notification = changeNoticationState(id);
Notification updatedNotification = repository.save(notification);
logger.debug("Updated Notification successfully: {}", updatedNotification);
return mapper.convertToDTO(updatedNotification);
}
private Notification changeNoticationState(String id) throws ItemNotFoundException {
Notification notification = findById(new ObjectId(id));
notification.setSeen(true);
return notification;
}
private List<NotificationDTO> convertToDTOs(List<Notification> notifications) {
return notifications.stream()
.map(mapper::convertToDTO)
.collect(Collectors.toList());
}
private Notification makeNewNotification(ObjectId userId, String content) {
return Notification.builder()
.userId(userId)
.content(content)
.sentAt(LocalDate.now())
.build();
}
private Notification findById(ObjectId id) throws ItemNotFoundException {
return repository.findById(id).orElseThrow(() -> new ItemNotFoundException("Notification not found"));
}
}
Вот NotificationRepository
@Repository
public interface NotificationRepository extends MongoRepository<Notification, ObjectId> {
@Query(NotificationQueries.FIND_BY_USER_ID_AND_IS_SEEN_FALSE)
List<Notification> findByUserIdAndIsSeenFalse(ObjectId userId);
}
Вот журнал ошибок:
java.lang.NullPointerException: Cannot invoke \"org.springframework.data.mongodb.repository.MongoRepository.save(Object)\" because \"this.repository\" is null\r
at com.example.novelWebsite.service.serviceimpl.NotificationService.sendNotification(NotificationService.java:37)\r
at com.example.novelWebsite.model.NovelSubscriber.sendNotification(NovelSubscriber.java:49)\r
at com.example.novelWebsite.model.NovelSubscriber.propertyChange(NovelSubscriber.java:44)\r
at com.example.novelWebsite.service.serviceimpl.ChapterService.notifySubscribers(ChapterService.java:48)\r
at com.example.novelWebsite.service.serviceimpl.ChapterService.create(ChapterService.java:33)\r
at com.example.novelWebsite.controller.controllerimpl.ChapterController.create(ChapterController.java:65)
Вот NovelSubscriberController
@RestController
@RequestMapping("/api/subscribers")
public class NovelSubscriberController {
private static final Logger logger = LoggerFactory.getLogger(NovelSubscriberController.class);
@Autowired
private NovelSubscriberService novelSubscriberService;
@PostMapping("/{userId}/{novelName}")
public ResponseEntity<?> subscribe(@PathVariable String userId, @PathVariable String novelName) {
try {
novelSubscriberService.subscribe(userId, novelName);
logger.info("User {} subscribed to novel {}", userId, novelName);
return new ResponseEntity<>(HttpStatus.OK);
} catch (Exception e) {
logger.error("Error when subscribing user to novel", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
@DeleteMapping
public ResponseEntity<?> unsubscribe(@RequestParam String userId, @RequestParam String novelName) {
try {
novelSubscriberService.unsubscribe(userId, novelName);
logger.info("User {} unsubscribed from novel {}", userId, novelName);
return new ResponseEntity<>(HttpStatus.OK);
} catch (ItemNotFoundException e) {
logger.error("Error when unsubscribing user from novel", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
}
Вот ChapterController
@RestController
@RequestMapping("/api/chapters")
public class ChapterController implements BaseController{
private static final Logger logger = LoggerFactory.getLogger(ChapterController.class);
@Autowired
private BaseService<Chapter, ObjectId, ChapterDTO> chapterService;
@GetMapping("/{novelName}/{chapterNumber}")
public ResponseEntity<?> findByNovelNameAndChapterNumber(@PathVariable String novelName, @PathVariable Integer chapterNumber){
try {
ChapterDTO chapterDTO = ((ChapterService) chapterService)
.findByNovelNameAndChapterNumber(novelName, chapterNumber);
logger.info("finding chapter");
return new ResponseEntity<>(chapterDTO, HttpStatus.OK);
}
catch (InterruptedException | ItemNotFoundException e) {
logger.error("Error when finding chapter", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
@GetMapping("/{novelName}")
public ResponseEntity<?> findOverviewsByNovelName(@PathVariable String novelName){
try {
List<ChapterOverviewDTO> chapterOverviewDTOs = ((ChapterService) chapterService)
.findOverviewsByNovelName(novelName);
logger.info("finding chapter overview");
return new ResponseEntity<>(chapterOverviewDTOs, HttpStatus.OK);
}
catch (InterruptedException e) {
logger.error("Error when finding chapter", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
@PostMapping
public ResponseEntity<?> create(@RequestBody ChapterDTO chapterDTO) {
((ChapterService) chapterService).create(chapterDTO);
logger.info("creating chapter");
return new ResponseEntity<>(HttpStatus.OK);
}
@PutMapping
public ResponseEntity<?> update(@RequestBody ChapterDTO chapterDTO) {
try {
((ChapterService) chapterService).update(chapterDTO);
logger.info("Updating chapter: {}", chapterDTO);
return new ResponseEntity<>(HttpStatus.OK);
}
catch (ItemNotFoundException e) {
logger.error("Error when updating chapter");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
@PutMapping("/enable/{id}")
public ResponseEntity<?> enableChapter(@PathVariable String id) {
try {
((ChapterService) chapterService).enableChapter(id);
logger.info("Enabling chapter");
return new ResponseEntity<>(HttpStatus.OK);
}
catch (ItemNotFoundException e) {
logger.error("Error when enabling chapter", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
@PutMapping("/disable/{id}")
public ResponseEntity<?> disableChapter(@PathVariable String id){
try {
((ChapterService) chapterService).disableChapter(id);
logger.info("disabling chapter");
return new ResponseEntity<>(HttpStatus.OK);
}
catch (ItemNotFoundException e) {
logger.error("Error when disabling chapter", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
}
К вашему сведению, я уже пробовал такие вещи, как @Autowired
, передавать по параметру notificationRepository
везде в notificationService
, novelSubscriber
, но журнал продолжает показывать null
для репозитория, хотя глава была создана.
Я был бы признателен за любую помощь в этом вопросе.
я только что сделал, спасибо за помощь
Вы передаете управляемый Spring bean-компонент в bean-компонент, не управляемый Spring, что и вызывает проблему.
Эта строка представляет собой проблему, приведенную ниже.
NovelSubscriber subscriber = new NovelSubscriber(new ObjectId(userId), novelName, notificationService);
Spring DI не будет работать в таком сценарии.
Чтобы Spring DI работал, каждый объект должен управляться Spring.
Спасибо, это сработало! Мне удалось это исправить, передав NotificationService в качестве параметра в propertyChangeEvent. Извините за поздний ответ; Я был занят и не смог вчера проверить ошибку
Добавьте свой контроллер.