Spring Data投影中@Value调用UserUtil方法报错求助
需求与实现代码
我想在Spring Data投影接口Quiz中添加isOwner字段,用于判断当前登录用户ID与对应记录的用户ID是否相等,相关实现代码如下:
投影接口Quiz
public interface Quiz { @Value("#{userUtil.equalsLoggedInUser(target.appUser)}") Boolean isOwner(); }
UserUtil服务类
专门编写UserUtil服务类,用于获取当前登录用户并完成ID比较:
@Service public class UserUtil { private final AppUserRepository appUserRepository; @Autowired public UserUtil( AppUserRepository appUserRepository) { this.appUserRepository = appUserRepository; } public AppUserEntity getLoggedInAppUserEntity() { String uid = ServiceUtil.getUidFromPrincipal(); return this.appUserRepository.findUserByFirebaseUuid(uid) .orElseThrow(() -> new UsernameNotFoundException("Invalid or unknown \"uid\".")); } public Boolean equalsLoggedInUser(AppUserEntity appUserEntity) { return Objects.equals(appUserEntity.getId(), this.getLoggedInAppUserEntity().getId()); } }
配置类
@Configuration public class UtilBeans { @Bean public UserUtil getUserUtil(AppUserRepository appUserRepository) { return new UserUtil(appUserRepository); } }
报错信息
运行代码时出现找不到userUtil的错误:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: EL1008E: Property or field 'userUtil' cannot be found on object of type 'org.springframework.data.projection.SpelEvaluatingMethodInterceptor$TargetWrapper' - maybe not public or not valid?]
尝试改用静态调用方式,依然报相同错误:
@Value("#{T(server.service.UserUtil).equalsLoggedInUser(target.appUser)}") Boolean isOwner();
解决方案
问题根源是Spring Data投影的SpEL默认上下文仅能访问target(实体对象)和projection(投影对象),无法直接引用Spring容器中的Bean。以下两种方式可解决该问题:
方式一:通过@Bean名称引用Spring容器中的Bean
修改投影接口的@Value注解,使用@userUtil直接引用容器中的Bean(Bean名称默认与@Bean方法名对应,此处为userUtil):
@Value("#{@userUtil.equalsLoggedInUser(target.appUser)}") Boolean isOwner();
方式二:将UserUtil方法改为静态实现(不推荐)
若要使用T()方式调用,需将UserUtil中的相关方法改为静态方法,同时确保依赖的AppUserRepository能被静态访问:
@Service public class UserUtil { private static AppUserRepository appUserRepository; @Autowired public UserUtil(AppUserRepository appUserRepository) { UserUtil.appUserRepository = appUserRepository; } public static AppUserEntity getLoggedInAppUserEntity() { String uid = ServiceUtil.getUidFromPrincipal(); return appUserRepository.findUserByFirebaseUuid(uid) .orElseThrow(() -> new UsernameNotFoundException("Invalid or unknown \"uid\".")); } public static Boolean equalsLoggedInUser(AppUserEntity appUserEntity) { return Objects.equals(appUserEntity.getId(), getLoggedInAppUserEntity().getId()); } }
之后投影接口的SpEL即可正常使用:
@Value("#{T(server.service.UserUtil).equalsLoggedInUser(target.appUser)}") Boolean isOwner();
注意:第二种方式属于静态依赖注入,并非Spring推荐的最佳实践,优先选择第一种方式。
内容的提问来源于stack exchange,提问作者Stefan Falk

