Наше приложение использует Spring. Моя цель состоит в том, чтобы при запуске системного теста я мог инструментировать класс при загрузке класса. Я использую собственный загрузчик классов для инструментария классов в проекте.
instrumenting class 'application.framework.gaeb.util.GaebDataExchangePhase'
Когда я запускаю тест, я получаю NPE.
26 Jun 2020 15:19:46,168 WARN main support.GenericWebApplicationContext:488 - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'lockSubprojectAspect': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private application.persistence.dao.IAttachmentDao application.core.aspect.LockSubprojectAspect.attachmentDao; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'attachmentDaoImpl' defined in file [C:\Users\jizhang\Documents\Projekt\kukagets\target\classes\application\persistence\dao\impl\AttachmentDaoImpl.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.hibernate.SessionFactory]: : Error creating bean with name 'sessionFactory' defined in class path resource [application/core/config/beans/DatabaseDefaultConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [application/core/config/beans/DatabaseDefaultConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException
26 Jun 2020 15:19:46,179 ERROR main context.TestContextManager:215 - Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@3c407114] to prepare test instance [application.systemtest.tlo.calculation.transferTemplate.TloTransferTemplateCalculationTest@24facb47]
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:94)
at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:72)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:170)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:110)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:212)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:200)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:259)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:261)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:219)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
at application.jacoco.ClasspathTestRunner.lambda$0(ClasspathTestRunner.java:59)
at application.jacoco.ClasspathTestRunner.run(ClasspathTestRunner.java:69)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'lockSubprojectAspect': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private application.persistence.dao.IAttachmentDao application.core.aspect.LockSubprojectAspect.attachmentDao; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'attachmentDaoImpl' defined in file [C:\Users\jizhang\Documents\Projekt\kukagets\target\classes\application\persistence\dao\impl\AttachmentDaoImpl.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.hibernate.SessionFactory]: : Error creating bean with name 'sessionFactory' defined in class path resource [application/core/config/beans/DatabaseDefaultConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [application/core/config/beans/DatabaseDefaultConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1208)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.test.context.web.AbstractGenericWebContextLoader.loadContext(AbstractGenericWebContextLoader.java:133)
at org.springframework.test.context.web.AbstractGenericWebContextLoader.loadContext(AbstractGenericWebContextLoader.java:60)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:109)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:261)
at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:68)
at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:86)
... 29 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private application.persistence.dao.IAttachmentDao application.core.aspect.LockSubprojectAspect.attachmentDao; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'attachmentDaoImpl' defined in file [C:\Users\jizhang\Documents\Projekt\kukagets\target\classes\application\persistence\dao\impl\AttachmentDaoImpl.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.hibernate.SessionFactory]: : Error creating bean with name 'sessionFactory' defined in class path resource [application/core/config/beans/DatabaseDefaultConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [application/core/config/beans/DatabaseDefaultConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 45 more
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'attachmentDaoImpl' defined in file [C:\Users\jizhang\Documents\Projekt\kukagets\target\classes\application\persistence\dao\impl\AttachmentDaoImpl.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.hibernate.SessionFactory]: : Error creating bean with name 'sessionFactory' defined in class path resource [application/core/config/beans/DatabaseDefaultConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [application/core/config/beans/DatabaseDefaultConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1137)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1040)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1120)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1044)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
... 47 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [application/core/config/beans/DatabaseDefaultConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1572)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1120)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1044)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
... 60 more
Caused by: java.lang.NullPointerException
at org.hibernate.annotations.common.reflection.java.JavaAnnotationReader.isAnnotationPresent(JavaAnnotationReader.java:50)
at org.hibernate.annotations.common.reflection.java.JavaXAnnotatedElement.isAnnotationPresent(JavaXAnnotatedElement.java:60)
at org.hibernate.cfg.AnnotationBinder.bindPackage(AnnotationBinder.java:308)
at org.hibernate.cfg.Configuration.addPackage(Configuration.java:817)
at org.springframework.orm.hibernate4.LocalSessionFactoryBuilder.scanPackages(LocalSessionFactoryBuilder.java:314)
at org.springframework.orm.hibernate4.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:434)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1631)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1568)
... 71 more
Если я использую свой собственный ClassLoader, но не сам определяю класс, но используйте только super.loadClass
, тогда проблем не будет. Но использование моего собственного loadClass
сообщит об ошибке.
My InstrumentClassLoader:
public class InstrumentClassLoader extends ClassLoader {
private IRuntime runtime = null;
private Instrumenter instr = null;
private static List<String> classesForJaCoCo;
private static Map<String, Class<?>> loadedClasses = new HashMap<>();
public InstrumentClassLoader(ClassLoader classloader) {
super(classloader);
}
public void setRuntime(IRuntime runtime) {
this.runtime = runtime;
instr = new Instrumenter(runtime);
}
public IRuntime getRuntime() {
return runtime;
}
public void setList() {
classesForJaCoCo = new ArrayList<>();
}
public List<String> getList() {
return classesForJaCoCo;
}
private Class<?> getClass(String name, InputStream stream) throws ClassNotFoundException {
// String file = name.replace('.', File.separatorChar) + ".class";
byte[] b = null;
try {
b = loadClassData(stream);
Class<?> c = defineClass(name, b, 0, b.length);
resolveClass(c);
return c;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public Class<?> instrumentClass(String name, InputStream stream) {
byte[] bytes = null;
try {
bytes = instr.instrument(stream, name);
} catch (IOException e) {
e.printStackTrace();
}
Class<?> c = defineClass(name, bytes, 0, bytes.length);
resolveClass(c);
return c;
}
private InputStream getStream(String name) {
String file = name.replace('.', File.separatorChar) + ".class";
return getClass().getClassLoader().getResourceAsStream(file);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> c = loadedClasses.get(name);
if (c == null) {
if (name.startsWith("application.") && !name.contains("jacoco")) {
InputStream stream = getStream(name);
if (stream != null) {
if (name.contains("Test") || name.contains("systemtest")) {
System.out.println("loading class '" + name + "'");
c = getClass(name, stream);
loadedClasses.put(name, c);
return c;
}
System.out.println("instrumenting class '" + name + "'");
c = instrumentClass(name, stream);
classesForJaCoCo.add(name);
loadedClasses.put(name, c);
return c;
} else {
System.out.println("No InputStream: '" + name + "'");
}
}
c = super.loadClass(name);
loadedClasses.put(name, c);
}
return c;
}
private byte[] loadClassData(InputStream stream) throws IOException {
int size = stream.available();
byte buff[] = new byte[size];
DataInputStream in = new DataInputStream(stream);
// Reading the binary data
in.readFully(buff);
in.close();
return buff;
}
public Map<String, Class<?>> getLoadedClasses() {
return loadedClasses;
}
}
My ClasspathTestRunner:
public class ClasspathTestRunner extends SpringJUnit4ClassRunner {
static ClassLoader instrumentClassLoader;
static IRuntime runtime = new LoggerRuntime();
static Instrumenter instr = null;
private static InputStream original = null;
private static RuntimeData data = new RuntimeData();
public ClasspathTestRunner(Class<?> clazz) throws Exception {
super(loadFromCustomClassloader(clazz));
}
// Loads a class in the custom classloader
private static Class<?> loadFromCustomClassloader(Class<?> clazz) throws InitializationError, Exception {
// Only load once to support parallel tests
if (instrumentClassLoader == null) {
instrumentClassLoader = new InstrumentClassLoader(ClassLoader.getSystemClassLoader());
((InstrumentClassLoader) instrumentClassLoader).setRuntime(runtime);
((InstrumentClassLoader) instrumentClassLoader).setList();
}
return Class.forName(clazz.getName(), true, instrumentClassLoader);
}
@Override
public void run(final RunNotifier notifier) {
Runnable runnable = () -> {
super.run(notifier);
};
//Thread thread = new Thread(runnable);
//thread.setContextClassLoader(instrumentClassLoader);
try {
runtime.startup(data);
System.out.println("++++++++++++++++++++ runtime start ++++++++++++++++++++");
//thread.start();
//System.out.println("++++++++++++++++++++ thread start ++++++++++++++++++++");
runnable.run();
//thread.join();
//System.out.println("++++++++++++++++++++ thread join ++++++++++++++++++++");
runtime.shutdown();
System.out.println("++++++++++++++++++++ runtime shutdown ++++++++++++++++++++");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void resetData() {
data.reset();
}
public static void analyse() throws Exception {
final ExecutionDataStore executionData = new ExecutionDataStore();
final SessionInfoStore sessionInfos = new SessionInfoStore();
data.collect(executionData, sessionInfos, false);
final CoverageBuilder coverageBuilder = new CoverageBuilder();
final Analyzer analyzer = new Analyzer(executionData, coverageBuilder);
List<String> classes = ((InstrumentClassLoader) instrumentClassLoader).getList();
for (String s : classes) {
String file = s.replace('.', File.separatorChar) + ".class";
InputStream stream = ClasspathTestRunner.class.getClassLoader().getResourceAsStream(file);
analyzer.analyzeClass(stream, s);
}
for (final IClassCoverage cc : coverageBuilder.getClasses()) {
boolean nameCount = false;
for (IMethodCoverage c : cc.getMethods()) {
if (c.getMethodCounter().getCoveredCount() != 0) {
if (!nameCount) {
System.out.println("Coverage of class " + cc.getName());
nameCount = true;
}
System.out.println(c.getName());
}
}
}
}
System Тест:
@ActiveProfiles({
"defaultDatabase"
})
@ContextConfiguration(classes = Application.class)
@WebAppConfiguration
public class SystemTestBasics {
...
@Before
public void setUpJacoco() {
System.out.println("-------------------------before-------------------------");
ClasspathTestRunner.resetData();
}
@After
public void tearDownJacoco() {
System.out.println("-------------------------after-------------------------");
try {
ClasspathTestRunner.analyse();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@RunWith(ClasspathTestRunner.class)
public class MyTest extends SystemTestBasics {
...
}
Почему такая проблема? Спасибо!