使用存储库启动应用程序时出错-IllegalAccessError:com.acme。$ Proxy53类无法访问其超级接口com.acme.UserRepository

时间:2019-02-18 13:09:10

标签: spring spring-boot tomcat9 embedded-tomcat

我正在尝试运行带有嵌入式tomcat的独立MVC + JPA应用程序

但是应用程序失败并出现以下错误,我看不出发生此失败的任何明显原因。我相信这与Tomcat处理类加载器的方式有关

我不确定春季靴如何解决这个问题

赞赏是否有人能弄清楚为什么会引发此错误

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

使用的来源

EmbeddedTomcatWebAppWithoutBoot.java

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<List<User>> 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<User> findAllUsers();
}

@Repository
@Transactional
class UserRepositoryImpl implements UserRepository {
  private final EntityManager entityManager;

  public UserRepositoryImpl(EntityManager entityManager) {
    this.entityManager = entityManager;
  }

  @Override
  public List<User> 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;
}

build.gradle

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 & 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 & 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'
    }
}

可以找到完整的可运行gradle项目here

1 个答案:

答案 0 :(得分:2)

代理的创建失败,因为定义代理的API UserRepository的接口是包私有的,禁止在类加载器和用于创建的类加载器之间访问包私有类型代理与用于加载UserRepository的类加载器不同。

有几种解决问题的方法,包括:

  1. 公开UserRepository
  2. 更新MainWebAppInitializer以呼叫rootContext.setClassLoader(getClass().getClassLoader())

第二个选项更接近Spring Boot的功能,并确保使用加载了MainWebAppInitializer(和UserRepository)的类加载器进行代理创建。