将一个类标记为@Transactional
(见下文)后,当我仍然在用户登录时重新加载我的应用程序时,我收到关于“尝试重复类定义”的LinkageErrors。
错误:
org.springframework.security.authentication.InternalAuthenticationServiceException: java.lang.LinkageError-->loader 'app' (instance of jdk.internal.loader.ClassLoaders$AppClassLoader) attempted duplicate class definition for [redacted].AdminUserDao$$FastClassBySpringCGLIB$$2ffa020e.
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:126)
其中adminUserDao是我们在自定义UserDetailsService中用于在身份验证期间加载用户的岛。
原始AdminUserDao.java
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class AdminUserDao {
private final SessionFactory sessionFactory;
@Autowired
public AdminUserDao(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public AdminUser getUserByEmailAddress(String emailAddress) {
String hql = "from " + AdminUser.class.getName()
+ " admin_user where admin_user.emailAddress = :emailAddress";
Session session = null;
try {
session = sessionFactory.openSession();
return session
.createQuery(hql, AdminUser.class)
.setParameter("emailAddress", emailAddress)
.uniqueResult();
} finally {
if (session != null && session.isOpen()) {
session.close();
}
}
}
Transactional AdminUserDao.java
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@Repository
public class AdminUserDao {
private final SessionFactory sessionFactory;
@Autowired
public AdminUserDao(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public AdminUser getUserByEmailAddress(String emailAddress) {
String hql = "from " + AdminUser.class.getName()
+ " admin_user where admin_user.emailAddress = :emailAddress";
return sessionFactory.getCurrentSession()
.createQuery(hql, AdminUser.class)
.setParameter("emailAddress", emailAddress)
.uniqueResult();
}
}
Transaction Manager Bean
@Bean
public HibernateTransactionManager transactionManager(@Qualifier("sessionFactory") SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
复制步骤:
我已经看到Stack Overflow上的其他人在错误地定义自定义ClassLoader(here)时遇到了此问题;但是,我没有使用自定义的ClassLoader。
可能相关的依赖版本:
答案 0 :(得分:0)
上面给出的链接:https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch08s06.html 由https://stackoverflow.com/users/1567452/jwilner 给出正确的提示(thx;))
@Transactional批注意味着Spring正在为您生成一个代理类,该类将您的方法包装为:
GeneratedProxy {
void proxiedMethod(){
try{
tx.begin();
yourClass.yourMethod();
tx.commit();
}
.. plus usual rollback and error stuff ..
}
}
现在spring有两个选项(请参见https://botproxy.net/docs/how-to/mismatched-proxy-types-jdk-vs-cglib-when-using-enablecaching-with-custom-aop-advice/):
如果您的代码实现了一个接口,他将使用JDK代理并公开该接口的所有方法:
(有关JDK代理示例,请参见例如https://www.byteslounge.com/tutorials/jdk-dynamic-proxies)
如果您的代码未实现任何接口,他将创建一个CGLIB代理 (实际上是猜测要公开的方法)
最后,如果他生成一个CGLIB代理,他将创建一个运行时类,例如 AuthcUserService $$ EnhancerBySpringCGLIB $$ 5c9dcb68
他将创建一个com.sun.proxy。$ Proxy1631之类的JDK代理
这两个将在setter(生成的setter)方法中转换为正确的类:
在第一种情况下,您可以在父类中使用该类本身 在第二种情况下,您应该使用接口,否则会得到 org.springframework.beans.factory.BeanNotOfRequiredTypeException
就您而言,您也许可以摆脱@Transactional, 就我而言,我无法摆脱@Transactional,因为我正在Wildfly中跨多个数据库进行真正的事务。
总结一下,这是在Java 13中发生的,可能是由于jee和Reflection的变化所致,实际上在jdk1.8中,下面的代码完全没有意识到这些机制。