title: Error while starting app with repositories - IllegalAccessError: class com.acme.$Proxy53 cannot access its superinterface com.acme.UserRepository
body: <p>I am trying to run a standalone MVC + JPA application with embedded tomcat</p>
  
  <p>But the application fails with the following error, tho I dont see any obvious reason for this failure. I believe this has something to do with the way Tomcat deals with class loaders</p>
  
  <p>I am not sure how spring boot gets around this problem</p>
  
  <p>Appreciate if anyone can shed light on why this error is thrown</p>
  
  <pre class="lang-java prettyprint-override"><code>org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepositoryImpl': Initialization of bean failed; nested exception is java.lang.IllegalAccessError: class com.acme.$Proxy53 cannot access its superinterface com.acme.UserRepository
      ... 36 more
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:584)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
      at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
      at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
      at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
      at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
      at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846)
      at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
      at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:400)
      at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:291)
      at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:103)
      at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4661)
      at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5131)
      at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
      at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1382)
      at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1372)
      at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
      at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
      at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:907)
      at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:831)
      at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
      at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1382)
      at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1372)
      at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
      at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
      at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:907)
      at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
      at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
      at org.apache.catalina.core.StandardService.startInternal(StandardService.java:423)
      at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
      at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:933)
      at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
      at org.apache.catalina.startup.Tomcat.start(Tomcat.java:398)
      at com.acme.EmbeddedTomcatWebAppWithoutBoot.main(EmbeddedTomcatWebAppWithoutBoot.java:65)
  Caused by: java.lang.IllegalAccessError: class com.acme.$Proxy53 cannot access its superinterface com.acme.UserRepository
      at java.lang.reflect.Proxy.defineClass0(Native Method)
      at java.lang.reflect.Proxy.access$300(Proxy.java:228)
      at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:642)
      at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
      at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
      at java.lang.reflect.WeakCache.get(WeakCache.java:127)
      at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
      at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
      at org.springframework.aop.framework.JdkDynamicAopProxy.getProxy(JdkDynamicAopProxy.java:123)
      at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110)
      at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:473)
      at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:352)
      at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:301)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:434)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1749)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:576)
      ... 36 more
  </code></pre>
  
  <p>Sources used</p>
  
  <p><code>EmbeddedTomcatWebAppWithoutBoot.java</code></p>
  
  <pre class="lang-java prettyprint-override"><code>package com.acme;
  
  import lombok.AllArgsConstructor;
  import lombok.Builder;
  import lombok.Getter;
  import lombok.NoArgsConstructor;
  import lombok.Setter;
  import lombok.ToString;
  import org.apache.catalina.Container;
  import org.apache.catalina.Context;
  import org.apache.catalina.LifecycleException;
  import org.apache.catalina.startup.Tomcat;
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.context.annotation.Import;
  import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
  import org.springframework.data.jpa.repository.JpaRepository;
  import org.springframework.data.jpa.repository.Query;
  import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  import org.springframework.data.repository.config.RepositoryConfiguration;
  import org.springframework.http.ResponseEntity;
  import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
  import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
  import org.springframework.orm.jpa.JpaTransactionManager;
  import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
  import org.springframework.orm.jpa.SharedEntityManagerCreator;
  import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
  import org.springframework.stereotype.Repository;
  import org.springframework.transaction.annotation.EnableTransactionManagement;
  import org.springframework.transaction.annotation.Transactional;
  import org.springframework.web.WebApplicationInitializer;
  import org.springframework.web.bind.annotation.GetMapping;
  import org.springframework.web.bind.annotation.RestController;
  import org.springframework.web.context.ContextLoaderListener;
  import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
  import org.springframework.web.servlet.DispatcherServlet;
  import org.springframework.web.servlet.config.annotation.EnableWebMvc;
  
  import javax.persistence.Entity;
  import javax.persistence.EntityManager;
  import javax.persistence.EntityManagerFactory;
  import javax.persistence.GeneratedValue;
  import javax.persistence.Id;
  import javax.servlet.ServletContext;
  import javax.servlet.ServletRegistration;
  import javax.sql.DataSource;
  import java.io.File;
  import java.io.IOException;
  import java.util.List;
  import java.util.Properties;
  
  import static java.lang.String.valueOf;
  
  public class EmbeddedTomcatWebAppWithoutBoot {
    private static final int PORT = 8080;
  
    public static void main(String[] args) throws LifecycleException {
      String appBase = ".";
      Tomcat tomcat = new Tomcat();
      tomcat.setBaseDir(createTempDir());
      tomcat.setPort(PORT);
      tomcat.getHost().setAppBase(appBase);
      tomcat.addWebapp("", ".");
      tomcat.getConnector(); // Trigger the creation of the default connector
      tomcat.start();
      ClassLoader classLoader = findContext(tomcat).getLoader().getClassLoader();
      Thread.currentThread().setContextClassLoader(classLoader);
      tomcat.getServer().await();
    }
  
    private static Context findContext(Tomcat tomcat) {
      for (Container child : tomcat.getHost().findChildren()) {
        if (child instanceof Context) {
          return (Context) child;
        }
      }
      throw new IllegalStateException("The host does not contain a Context");
    }
  
    // based on AbstractEmbeddedServletContainerFactory
    private static String createTempDir() {
      try {
        File tempDir = File.createTempFile("tomcat.", "." + PORT);
        tempDir.delete();
        tempDir.mkdir();
        tempDir.deleteOnExit();
        return tempDir.getAbsolutePath();
      } catch (IOException ex) {
        throw new RuntimeException(
            "Unable to create tempDir. java.io.tmpdir is set to " + System.getProperty("java.io.tmpdir"),
            ex
        );
      }
    }
  }
  
  
  class MainWebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) {
      AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
      rootContext.register(DataAccessConfiguration.class, UserRepositoryImpl.class);
      servletContext.addListener(new ContextLoaderListener(rootContext));
  
      AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
      servletAppContext.register(WebMvcConfiguration.class);
      DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext);
      ServletRegistration.Dynamic servletRegistration = servletContext.addServlet("dispatcher", dispatcherServlet);
      servletRegistration.setLoadOnStartup(1);
      servletRegistration.addMapping("/");
    }
  }
  
  
  @EnableWebMvc
  @Configuration
  @Import(UserResource.class)
  class WebMvcConfiguration {}
  
  
  @RestController
  class UserResource {
  
    private final UserRepository userRepository;
  
    public UserResource(UserRepository userRepository) {
      this.userRepository = userRepository;
    }
  
    @GetMapping
    ResponseEntity&lt;List&lt;User&gt;&gt; get() {
      return ResponseEntity.ok().body(userRepository.findAllUsers());
    }
  }
  
  
  @Getter
  @Setter
  @Builder
  @NoArgsConstructor
  @AllArgsConstructor
  class UserRequest {
    private String name;
    private String authorityName;
  }
  
  
  @Configuration
  @EnableTransactionManagement
  class DataAccessConfiguration  {
    @Bean
    DataSource dataSource() {
      return new EmbeddedDatabaseBuilder()
          .setType(EmbeddedDatabaseType.H2)
          .setName("sample")
          .build();
    }
  
    @Bean
    LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
      LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
  
      factoryBean.setDataSource(dataSource);
  
      HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
      factoryBean.setJpaVendorAdapter(vendorAdapter);
      factoryBean.setPackagesToScan(EmbeddedTomcatWebAppWithoutBoot.class.getPackage().getName());
  
      // set som extra properties hibernate
      Properties jpaProperties = new Properties();
      jpaProperties.setProperty("hibernate.show_sql", "true");
      jpaProperties.setProperty("hibernate.format_sql", "true");
      jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
      jpaProperties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
      factoryBean.setJpaProperties(jpaProperties);
  
      return factoryBean;
    }
  
    @Bean
    JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
      return new JpaTransactionManager(entityManagerFactory);
    }
  
    @Bean
    EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
      return SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
    }
  
    @Bean
    PersistenceExceptionTranslationPostProcessor postProcessor() {
      return new PersistenceExceptionTranslationPostProcessor();
    }
  }
  
  
  interface UserRepository {
    List&lt;User&gt; findAllUsers();
  }
  
  @Repository
  @Transactional
  class UserRepositoryImpl implements UserRepository {
    private final EntityManager entityManager;
  
    public UserRepositoryImpl(EntityManager entityManager) {
      this.entityManager = entityManager;
    }
  
    @Override
    public List&lt;User&gt; findAllUsers() {
      return entityManager.createQuery("from User", User.class)
          .getResultList();
    }
  }
  
  @Entity
  @Getter
  @Setter
  @Builder
  @NoArgsConstructor
  @AllArgsConstructor
  @ToString
  class User {
    @Id
    @GeneratedValue
    private int id;
    private String name;
  }
  </code></pre>
  
  <p><code>build.gradle</code></p>
  
  <pre><code>ext {
      set('spring-boot.version', '2.1.2.RELEASE')
      // copied from above
      set('spring.version', '5.1.4.RELEASE')
      // async logging
      set('lmax-disruptor.version', '3.4.2')
      // misc utils
      set('unexceptional.version', '1.0.0')
      set('mapstruct.version', '1.3.0.Final')
      // tooling
      set('jetbrains-annotations.version', '16.0.2')
  }
  
  group 'com.acme'
  version '1.0.0-SNAPSHOT'
  
  apply plugin: 'java-library'
  
  sourceCompatibility = 1.8
  
  repositories {
      mavenCentral()
      jcenter()
      maven {
          url 'https://repo.spring.io/libs-milestone'
      }
  }
  
  configurations {
      springBom
      developmentOnly
  
      // we want to make sure the dependencies bom ia available everywhere so that the dependencies can be resolved across all configurations
      compileOnly.extendsFrom(springBom)
      annotationProcessor.extendsFrom(springBom)
      testAnnotationProcessor.extendsFrom(springBom)
      api.extendsFrom(springBom)
      implementation.extendsFrom(springBom)
  
      runtimeClasspath {
          extendsFrom developmentOnly
      }
  
      // lets inherit everything
      testCompileOnly.extendsFrom(compileOnly)
  }
  
  dependencies {
      //*** bill of materials
      springBom platform("org.springframework.boot:spring-boot-dependencies:${project.'spring-boot.version'}")
  
      implementation 'org.springframework:spring-context'
      implementation 'org.springframework.data:spring-data-jpa'
      implementation 'org.hibernate:hibernate-core'
      implementation 'com.h2database:h2'
  
      // web
      implementation 'org.springframework:spring-web'
      implementation 'org.springframework:spring-webmvc'
  
      // jackson for json serialization
      implementation 'com.fasterxml.jackson.core:jackson-databind'
      implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8' // new java 8 classes like stream, OPtional
      implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' // new java date &amp; time api
      implementation 'com.fasterxml.jackson.module:jackson-module-parameter-names' // custom modules within jackson
  
      // embedded server
      implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
  
      // log4j2
      implementation 'org.apache.logging.log4j:log4j-core'
      implementation 'org.apache.logging.log4j:log4j-slf4j-impl'
      implementation 'org.apache.logging.log4j:log4j-jcl'
      implementation 'org.apache.logging.log4j:log4j-jul'
      implementation "com.lmax:disruptor:${project.'lmax-disruptor.version'}"
  
      // unexceptional for making sure code is not super polluted
      implementation "io.earcam:io.earcam.unexceptional:${project.'unexceptional.version'}"
  
      // junit
      testImplementation 'org.junit.jupiter:junit-jupiter-api'
      testImplementation 'org.junit.jupiter:junit-jupiter-engine'
      testImplementation 'org.junit.jupiter:junit-jupiter-params'
      testImplementation 'org.mockito:mockito-junit-jupiter'
  
      // test misc
      testImplementation 'org.assertj:assertj-core'
      testImplementation 'org.hamcrest:hamcrest-library'
      testImplementation 'org.mockito:mockito-junit-jupiter'
  
      // NOTE: Due to a bug as of now mapstruct needs to go before lombok
      // https://github.com/mapstruct/mapstruct/issues/1581
  
      // maspstruct
      implementation "org.mapstruct:mapstruct:${project.'mapstruct.version'}"
      implementation "org.mapstruct:mapstruct:${project.'mapstruct.version'}"
      annotationProcessor "org.mapstruct:mapstruct-processor:${project.'mapstruct.version'}"
      testAnnotationProcessor "org.mapstruct:mapstruct-processor:${project.'mapstruct.version'}"
  
      // lombok
      compileOnly('org.projectlombok:lombok')
      annotationProcessor('org.projectlombok:lombok')
  }
  
  configurations {
      all {
          // we need to exclude logging to avoid keeping both slf4j &amp; log4j2 both in the same location
          exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
          // `spring-boot-starter-test` is pulling older version of junit. Lets just ignore it
          exclude group: 'junit', module: 'junit'
      }
  }
  </code></pre>
  
  <p>The complete runnable gradle project can be be found <a href="https://github.com/thekalinga/embedded-tomcat-server-issue" rel="nofollow noreferrer">here</a></p>