我正在尝试运行带有嵌入式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
答案 0 :(得分:2)
代理的创建失败,因为定义代理的API UserRepository
的接口是包私有的,禁止在类加载器和用于创建的类加载器之间访问包私有类型代理与用于加载UserRepository
的类加载器不同。
有几种解决问题的方法,包括:
UserRepository
MainWebAppInitializer
以呼叫rootContext.setClassLoader(getClass().getClassLoader())
第二个选项更接近Spring Boot的功能,并确保使用加载了MainWebAppInitializer
(和UserRepository
)的类加载器进行代理创建。