Reputation: 1257
A guy ask me for help him resolve a strange bug in security service of his spring boot app, after hours of trail I manage to fix the bug but I really have no idea what happen. Please look at these classes:
Class User
: user infomation in database
@Getter
@Setter
@Entity
@Table(name = "user", uniqueConstraints = {@UniqueConstraint(columnNames = {"user_name"})})
public class User extends DateAudit implements Serializable {
// Id, username, password and constructor... not really important
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> role = new HashSet<>();
}
Class UserDto
: implement Spring's interface for manage user credential
@Getter
@Setter
@AllArgsConstructor
public class UserDto implements org.springframework.security.core.userdetails.UserDetails {
private long id;
private String Username;
@JsonIgnore
private String password;
private Collection<? extends GrantedAuthority> authorities;
public static UserDto create(User user) {
List<GrantedAuthority> authorities = user.getRole().stream()
.map(role -> new SimpleGrantedAuthority(role.getName().name())).collect(Collectors.toList());
return new UserDto(user.getId(), user.getUserName(), user.getPassword(), authorities);
}
// implement interface's methods, only getters, not important
}
Class CustomUserService
: Auth service
@Service
public class CustomUserService implements org.springframework.security.core.userdetails.UserDetailsService {
private final UserRepository userRepository;
@Autowired
public CustomUserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
@Transactional
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user = userRepository.findUserByUserName(userName).orElseThrow(() -> {
return null;
});
return UserDto.create(user);
}
}
Full source code can be found here (it is only a simple spring boot app with some classes) : https://github.com/raizo000/admin_project (his repo uses embedded tomcat, I've tried to change it to jetty but it is not the cause)
When I run code the first time with these 3 classes. The line
return UserDto.create(user);
Give me a NoClassDefFoundError
:
WARN 7252 --- [qtp223566397-22] org.eclipse.jetty.server.HttpChannel : /login
java.lang.NoClassDefFoundError: com/example/admin/dto/UserDto
at com.example.admin.services.CustomUserService.loadUserByUsername(CustomUserService.java:28) ~[classes/:na]
at com.example.admin.services.CustomUserService$$FastClassBySpringCGLIB$$b9234a59.invoke(<generated>) ~[classes/:na]
....
I have checked the jar file, there is a UserDto.class file in the right directory.
Remove the @Transactional
help fix the error but cause another lazily initialize error and I end up change fetch = FetchType.LAZY
in User
class to fetch = FetchType.EAGER
as a quick fix.
Why adding Transactional can cause a NoClassDefFoundError? Do remove Transactional is the right solution or there is a better fix?
Upvotes: 0
Views: 214
Reputation: 5968
When a class is not found, the first thing that you should make is clean and build. Because, the last build is saving some dependency. So the @Transactional @interface was not found in some corrupt package. A rebuild(clean build) solves it.
Upvotes: 1