Issue
I'm creating an electronic diary app, using java spring, but I faced with a small problem. I decided to create role system with a state pattern. So the logic of this system should look like this:
- In controller, after accepting request, it creates the object of context class. Constructor of context class gets concrete RoleClass in depends to user role.(it uses the factory)
- In controller we call the method of context we want to be executed
- In context we call the method of role we want to be executed.
- In role class we call the method of service we want to be executed.
But here's the problem. When I start the program, all fields of service, which were autowired are null. I added @Service
annotation to the context class, but it was useless. I also tried to create bean for the context class, but nothing changed.
That's what my program prints after starting:
Caused by: java.lang.NullPointerException: Cannot invoke "com.diary.diary.service.UserService.getCurrentUser()" because "this.userService" is null
So here's my code:
- It's my UserController:
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private UserContext userContext;
@GetMapping("/marks")
public ResponseEntity<Object> getMarks() {
try {
return ResponseEntity.ok(userContext.getMarks());
} catch (Exception e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
}
- It's my UserContext class:
@Service
public class UserContext {
private UserRole userRole;
@Autowired
private UserService userService;
public UserContext() {
userRole = RoleFactory.getUserRole(userService.getCurrentUser().getName());
}
public List<HomeworkGetModel> getHomework() throws UserNotFoundException {
return userRole.getHomework();
}
public List<MarkGetModel> getMarks() throws UserNotFoundException {
return userRole.getMarks();
}
}
- It's my RoleFactory class:
public class RoleFactory {
private static UserRole userRole;
public static UserRole getUserRole(String roleName) {
if(userRole == null) {
buildUserRole(roleName);
}
return userRole;
}
private static void buildUserRole(String roleString) {
switch (roleString) {
case RoleNames.DEFAULT -> userRole = new DefaultRole();
case RoleNames.STUDENT -> userRole = new StudentRole();
case RoleNames.TEACHER -> userRole = new TeacherRole();
case RoleNames.ADMIN -> userRole = new AdminRole();
}
}
}
- Here's the interface of Role:
public interface UserRole {
default UserGetModel getUser(long id) throws UserNotFoundException {
throw new NotImplementedException();
}
default List<UserGetModel> getAllUsers() {
throw new NotImplementedException();
}
default UserEntity updateUser(UserUpdateModel newUserData) throws UserNotFoundException {
throw new NotImplementedException();
}
default UserEntity deleteUser() throws UserNotFoundException {
throw new NotImplementedException();
}
default List<MarkGetModel> getMarks() throws UserNotFoundException {
throw new NotImplementedException();
}
default List<MarkGetModel> getMarksByDate(String date) throws UserNotFoundException, ParseException {
throw new NotImplementedException();
}
default List<MarkGetModel> getMarksBySubject(String subjectName) throws UserNotFoundException {
throw new NotImplementedException();
}
default List<MarkGetModel> getMarksByDateAndSubject(DateAndSubjectModel dateAndSubject) throws UserNotFoundException, ParseException {
throw new NotImplementedException();
}
default SubjectGetModel getSubject(long id) throws SubjectNotFoundException {
throw new NotImplementedException();
}
default SubjectGetModel getSubject(String name) throws SubjectNotFoundException {
throw new NotImplementedException();
}
default SchoolGetModel getSchoolById(long schoolId) throws SchoolNotFoundException {
throw new NotImplementedException();
}
default SchoolGetModel getSchoolByNumber(int schoolNumber) throws SchoolNotFoundException {
throw new NotImplementedException();
}
default List<SchoolGetModel> getSchools() {
throw new NotImplementedException();
}
default ClassGetModel getSchoolClass(ClassGetByNumberModel classData) throws com.diary.diary.exception.school_class.ClassNotFoundException, SchoolNotFoundException {
throw new NotImplementedException();
}
default List<ClassGetModel> getClasses() {
throw new NotImplementedException();
}
default List<HomeworkGetModel> getHomework() throws UserNotFoundException {
throw new NotImplementedException();
}
default List<HomeworkGetModel> getHomeworkByDate(String date) throws UserNotFoundException, ParseException {
throw new NotImplementedException();
}
default List<HomeworkGetModel> getHomeworkBySubject(String subjectName) {
throw new NotImplementedException();
}
default ClassEntity addClass(ClassAddModel classData) {
throw new NotImplementedException();
}
default ClassEntity addUserToClass(AdminAddUserToClassModel userAndClassModel) {
throw new NotImplementedException();
}
default ClassEntity deleteClass(long id) {
throw new NotImplementedException();
}
default UserEntity removeUserFromClass(AdminRemoveUserFromClassModel userClassModel) {
throw new NotImplementedException();
}
default SchoolEntity addSchool(SchoolAddModel schoolData) {
throw new NotImplementedException();
}
default SchoolEntity deleteSchool(long id) {
throw new NotImplementedException();
}
default UserEntity removeUserFromSchool(AdminRemoveUserFromSchoolModel userSchoolModel) {
throw new NotImplementedException();
}
default SubjectEntity addSubject(SubjectAddModel subjectData) {
throw new NotImplementedException();
}
default SubjectEntity updateSubject(SubjectUpdateModel newSubjectModel) {
throw new NotImplementedException();
}
default SubjectEntity deleteSubject(SubjectDeleteModel subjectDeleteData) {
throw new NotImplementedException();
}
default TimetableEntity addTimetable(TimetableAddModel timetableData) {
throw new NotImplementedException();
}
default TimetableEntity updateTimetable(TimeTableUpdateModel newTimetableData) {
throw new NotImplementedException();
}
default TimetableEntity deleteTimetable(long id) {
throw new NotImplementedException();
}
default ClassEntity addTimetableToClass(TimetableClassModel timetableClass) {
throw new NotImplementedException();
}
default ClassEntity deleteTimetableFromClass(long classId) {
throw new NotImplementedException();
}
}
- Here's the UserService class:
@Service @Configurable
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepo;
@Autowired
private RoleRepository roleRepo;
@Autowired
private MarkMethods markMethods;
private final BCryptPasswordEncoder bCryptPasswordEncoder
= new BCryptPasswordEncoder();
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity user = userRepo.findByLogin(username);
if(user == null) {
throw new UsernameNotFoundException("user with such login not found");
}
SimpleGrantedAuthority userRole = new SimpleGrantedAuthority(user.getRole().toString());
return new User(Long.toString(user.getId()), user.getPassword(), List.of(userRole));
}
public List<MarkGetModel> getMarks() throws UserNotFoundException {
checkUserRoleOrThrow(RoleNames.ADMIN, getCurrentUser());
UserEntity student = getCurrentUser();
return convertToMarkGetModelList(student.getMarks());
}
So what can be the problem? If you know, please tell me, I'd really appreciate it.
Solution
Use Constructor Autowiring here:
public UserContext(@Autowire UserService userService) {
this.userService = userService;
userRole = RoleFactory.getUserRole(userService.getCurrentUser().getName());
}
I think when spring is autowiring beans with private fields, it first creates the object via empty constructor and then fills in the fields via reflection (this makes sense). But you do the whole execution in the empty constructor, so at this point the field is not autowired.
Answered By - Loading
Answer Checked By - Gilberto Lyons (JavaFixing Admin)