From 2fbb3b00c72d36e65a459de98cba42836df5ee17 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 25 Oct 2017 14:57:55 +0200 Subject: [PATCH] DATAJDBC-136 - Fix ClassNotFoundException when MyBatis is not on the classpath. --- .../support/JdbcRepositoryFactoryBean.java | 16 +++++++++--- .../JdbcRepositoryFactoryBeanUnitTests.java | 25 +++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java index 1156e8206..942efe12a 100644 --- a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java @@ -118,12 +118,22 @@ public class JdbcRepositoryFactoryBean, S, ID extend private Optional createMyBatisDataAccessStrategy() { - if (!ClassUtils.isPresent("org.apache.ibatis.session.SqlSessionFactory", this.getClass().getClassLoader())) { + String myBatisSqlSessionFactoryClassName = "org.apache.ibatis.session.SqlSessionFactory"; + ClassLoader classLoader = this.getClass().getClassLoader(); + + if (!ClassUtils.isPresent(myBatisSqlSessionFactoryClassName, classLoader)) { return Optional.empty(); } - return getBean(SqlSessionFactory.class, SQL_SESSION_FACTORY_BEAN_NAME) - .map(ssf -> new MyBatisDataAccessStrategy(ssf)); + try { + + return getBean(classLoader.loadClass(myBatisSqlSessionFactoryClassName), SQL_SESSION_FACTORY_BEAN_NAME) + // note that the cast to SqlSessionFactory happens in a lambda, which is basically a separate class + // thus it won't get loaded if this code path doesn't get executed. + .map(ssf -> new MyBatisDataAccessStrategy((SqlSessionFactory) ssf)); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("Detected MyBatis on classpath but failed to load the class " + myBatisSqlSessionFactoryClassName); + } } private Optional createDefaultAccessStrategy(JdbcMappingContext context, diff --git a/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java b/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java index 0cca7ab3a..513d9231d 100644 --- a/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java +++ b/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java @@ -25,9 +25,11 @@ import org.springframework.data.jdbc.core.DelegatingDataAccessStrategy; import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.core.support.RepositoryFactorySupport; +import org.springframework.instrument.classloading.ShadowingClassLoader; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; +import org.springframework.util.ReflectionUtils; /** * Tests the dependency injection for {@link JdbcRepositoryFactoryBean}. @@ -211,6 +213,29 @@ public class JdbcRepositoryFactoryBeanUnitTests { assertThat(findDataAccessStrategy(factory, MyBatisDataAccessStrategy.class)).isNotNull(); } + @Test // DATAJDBC-136 + public void canBeLoadedWithoutMyBatis() throws Exception { + + String sqlSessionFactoryClassName = SqlSessionFactory.class.getName(); + + ShadowingClassLoader classLoader = new ShadowingClassLoader(this.getClass().getClassLoader()) { + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + + if (name.equals(sqlSessionFactoryClassName)) { + throw new ClassNotFoundException("%s is configured not to get loaded by this classloader"); + } + return super.loadClass(name); + } + }; + + Class loadedClass = classLoader.loadClass(JdbcRepositoryFactoryBean.class.getName()); + + assertThat(loadedClass).isNotNull(); + ReflectionUtils.getAllDeclaredMethods(loadedClass); + } + private Condition using(NamedParameterJdbcOperations expectedOperations) { Predicate predicate = r -> extractNamedParameterJdbcOperations(r) == expectedOperations;