Error while starting app with repositories - IllegalAccessError: class com.acme.$Proxy53 cannot access its superinterface com.acme.UserRepository
Error while starting app with repositories - IllegalAccessError: class com.acme.$Proxy53 cannot access its superinterface com.acme.UserRepository
我正在尝试 运行 具有嵌入式 tomcat
的独立 MVC + JPA 应用程序
但是应用程序失败并出现以下错误,我看不出有任何明显的失败原因。我相信这与 Tomcat 处理 class loader
的方式有关
我不确定 spring 启动如何解决这个问题
感谢是否有人能阐明为什么会引发此错误
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[=10=](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.access0(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'
}
}
可以找到完整的 运行nable gradle 项目 here
创建代理失败,因为定义代理 API、UserRepository
的接口是包私有的,禁止跨 [=30= 访问包私有类型] 加载器和用于创建代理的 class 加载器与用于加载 UserRepository
.
的 class 加载器不同
有几种不同的方法可以解决此问题,包括:
- 制作
UserRepository
public
- 更新
MainWebAppInitializer
调用 rootContext.setClassLoader(getClass().getClassLoader())
第二个选项更接近 Spring Boot 所做的,并确保加载 MainWebAppInitializer
(和 UserRepository
)的 class 加载程序用于代理创建。
我正在尝试 运行 具有嵌入式 tomcat
的独立 MVC + JPA 应用程序但是应用程序失败并出现以下错误,我看不出有任何明显的失败原因。我相信这与 Tomcat 处理 class loader
的方式有关我不确定 spring 启动如何解决这个问题
感谢是否有人能阐明为什么会引发此错误
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[=10=](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.access0(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'
}
}
可以找到完整的 运行nable gradle 项目 here
创建代理失败,因为定义代理 API、UserRepository
的接口是包私有的,禁止跨 [=30= 访问包私有类型] 加载器和用于创建代理的 class 加载器与用于加载 UserRepository
.
有几种不同的方法可以解决此问题,包括:
- 制作
UserRepository
public - 更新
MainWebAppInitializer
调用rootContext.setClassLoader(getClass().getClassLoader())
第二个选项更接近 Spring Boot 所做的,并确保加载 MainWebAppInitializer
(和 UserRepository
)的 class 加载程序用于代理创建。