Evg*_*hin 4 java stack-overflow spring spring-data spring-data-jpa
使用 Spring Data Repositories 时发现了一些奇怪的行为。
我写了这些类和接口:
@Transactional(readOnly = true)
public interface UserRepository extends Repository<User, Integer> {
@Transactional
@Modifying
@Query("DELETE FROM User u WHERE u.id=:id")
int delete(@Param("id") int id);
@Transactional
User save(User user);
User findOne(Integer id);
List<User> findAll();
}
public interface AbstractRepository<T> {
T save(T user);
// false if not found
boolean delete(int id);
// null if not found
T get(int id);
List<T> getAll();
}
@Repository
public class UserRepositoryImpl implements AbstractRepository<User> {
@Autowired
private JpaUserRepository repository;
@Override
public User save(User user) {
return repository.save(user);
}
@Override
public boolean delete(int id) {
return repository.delete(id) != 0;
}
@Override
public User get(int id) {
return repository.findOne(id);
}
@Override
public List<User> getAll() {
return repository.findAll();
}
}
Run Code Online (Sandbox Code Playgroud)
当我尝试测试 UserRepositoryImpl 时,抛出 java.lang.StackOverflowError
Caused by: java.lang.StackOverflowError
at org.springframework.data.repository.util.ClassUtils.unwrapReflectionException(ClassUtils.java:166)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:505)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:478)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:115)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy55.save(Unknown Source)
at ru.emitrohin.votingsystem.repository.UserRepositoryImpl.save(RestaurantRepositoryImpl.java:24)
at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
Run Code Online (Sandbox Code Playgroud)
我看到 save() 方法存在一些问题。在 delete() 方法上也抛出了 stackoverflow。
我已经找到了解决方案。当我将扩展 Repository 接口的接口名称更改为(例如)JpaUserRepository 时,我的问题就消失了。
所以,问题是“发生了什么”。为什么实现spring数据存储库的接口名称与模式Classname Repository匹配时会抛出stackoverflow ?
我的 pom.xml
Caused by: java.lang.StackOverflowError
at org.springframework.data.repository.util.ClassUtils.unwrapReflectionException(ClassUtils.java:166)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:505)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:478)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:115)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy55.save(Unknown Source)
at ru.emitrohin.votingsystem.repository.UserRepositoryImpl.save(RestaurantRepositoryImpl.java:24)
at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
Run Code Online (Sandbox Code Playgroud)
好吧,问题不是因为Classname RepositoryImpl 命名而是因为RepositoryClassname Impl 命名。您这样做是在告诉 Spring 您正在为RepositoryClassname接口上的某些方法提供自定义行为。也就是说,Spring 正在为您实现JpaRepository 中的所有标准 CRUD 方法。您的实现允许您注入存储库 bean,但它旨在用作实现的一部分,而不仅仅是用于委托. 当您在 Impl 类中实现 save() 时,您实际上是在为接口 save() 提供自定义实现因此,当您在“自定义”实现中调用接口 save() 时,您正在强制一个以 stackoverflow 异常结束的无限循环。
如果您不打算提供自定义行为,请不要在 Impl 类中声明该方法。另一方面,如果您不想为某些接口方法提供自定义行为,而只是想创建一个单独的存储库,请不要将其命名为RepositoryInterfaceName Impl。如果您试图修改实现,请不要调用 interface.save() 方法。
PS:通常在持久层使用@Transactional 是一种不好的做法,我认为在服务层使用@Transactional 总是更好,因为它会处理您的业务逻辑。查看此帖子以获取更多信息。
| 归档时间: |
|
| 查看次数: |
2282 次 |
| 最近记录: |