Spring 4升级后未加载应用程序上下文

时间:2016-08-16 20:09:11

标签: java spring

我正在将我们的webapp中使用的spring框架版本从3.1.4升级到4.1.8。随着新的Spring版本,我们的一些单元测试失败了,因为@Autowired不再工作。这是失败的测试之一:

@ContextConfiguration(locations={"/math-application-context.xml"})
public class MathematicaMathServiceTest extends JavaMathServiceTest{

@Autowired
private KernelLinkPool mathematicalKernelPool; 

protected static String originalServiceType = System.getProperty("calculation.math.service.type");

@AfterClass
public static void unsetMathServiceType(){
    System.clearProperty("calculation.math.service.type");

}

@BeforeClass
public static void setMathServiceType(){
    System.setProperty("calculation.math.service.type","Mathematica");
}

@Test
public void testMathematicaService() throws Exception{          


    try {           

        acquireKernelAndExecute(0);

        Assert.assertEquals(0, mathematicalKernelPool.getBorrowingThreadsCount());

    } catch(UnsatisfiedLinkError e) {
        System.out.println("Mathematica not installed. Skipping test");
    }catch(Exception ex){
        if (!ExceptionFormatter.hasCause(ex, MathServiceNotConfiguredException.class)){throw ex;}
        if (System.getProperty(MathService.SERVICE_CONFIGURED_SYSTEM_VARIABLE) != null){
            throw ex;
        }
        logger.error("Cannot execute test. Math service is not configured");
    }
}

}

这是KernelLinkPool类:

public class KernelLinkPool extends GenericObjectPool implements InitializingBean{

private static final int RETRY_TIMEOUT_MS = 5000;

private static final long STARTUP_WAIT_TIME_MS = 10000;

private boolean mathematicaConfigured = true;
private PoolableObjectFactory factory;
// ensures that multiple requests from the same thread will be given the same KernelLink object
private static ThreadLocal<KernelLink> threadBoundKernel = new ThreadLocal<KernelLink>();
// holds the number of requests issued on each thread
private static ThreadLocal<Integer> callDepth = new ThreadLocal<Integer>();
private long maxBorrowWait;
private Integer maxKernels;
private boolean releaseLicenseOnReturn;
private Logger logger = LoggerFactory.getLogger(this.getClass());
// (used only for unit testing at this point)
private Map<String,Integer> borrowingThreads = new ConcurrentHashMap<String,Integer>();

public KernelLinkPool(PoolableObjectFactory factory) {
    super(factory);     
    this.factory = factory;
    this.setMaxWait(maxBorrowWait);

}

@Override
public Object borrowObject() throws Exception{
    return borrowObject(this.maxBorrowWait);
}

public Object borrowObject(long waitTime) throws Exception {
    long starttime = System.currentTimeMillis();

    if (!mathematicaConfigured){
        throw new MathServiceNotConfiguredException();
    }

    try{
        if (callDepth.get() == null){
            callDepth.set(1);
        }else{
            callDepth.set(callDepth.get()+1);
        }

        KernelLink link = null;         
        if (threadBoundKernel.get() != null){
            link = threadBoundKernel.get();
        }else{
            //obtain kernelLink from object pool
            //retry when borrowObject fail until
            //maxBorrowWait is reached
            while(true){
                try{
                    logger.debug("Borrowing MathKernel from object pool");
                    link = (KernelLink) super.borrowObject();
                    break;
                }catch(KernelLinkCreationException ex){
                    long timeElapsed = System.currentTimeMillis() - starttime;
                    logger.info("Failed to borrow MathKernel. Time elapsed [" + timeElapsed + "] ms", ex);
                    if(timeElapsed >= waitTime){
                        logger.info("Retry timeout reached");
                        throw ex;
                    }
                    Thread.sleep(RETRY_TIMEOUT_MS);
                }
            }
            logger.debug("borrowed [" + link + "]");
            threadBoundKernel.set(link);
        }

        borrowingThreads.put(Thread.currentThread().getName(),callDepth.get());

        return link;

    }catch(Exception ex){
        logger.error("Failed to acquire Mathematica kernel. Borrowing threads [" + borrowingThreads + "]");
        throw ex;
    }
}


public void returnObject(Object obj) throws Exception {

    callDepth.set(callDepth.get()-1);

    if (callDepth.get() <= 0){
        threadBoundKernel.set(null);

        borrowingThreads.remove(Thread.currentThread().getName());

        if (releaseLicenseOnReturn){
            // will destroy obj
            super.invalidateObject(obj);
        }
        else{
            // will park obj in the pool of idle objects
            super.returnObject(obj);
        }
    }else{
        borrowingThreads.put(Thread.currentThread().getName(),callDepth.get());
    }

}


@Override
public void afterPropertiesSet() throws Exception {

    try{

        if (maxKernels == 0){
            List<KernelLink> links = new ArrayList<KernelLink>();
            while (true){
                try{
                    links.add((KernelLink)factory.makeObject());
                }catch(KernelLinkCreationException ex){
                    break;
                }
            }       
            if(links.isEmpty()){
                logger.warn("No available Mathematica license!");
                mathematicaConfigured = false;
                return;
            }
            for (KernelLink link : links){
                factory.destroyObject(link);
            }
            logger.info("Detected number of available Mathematica license = [" + links.size() + "]");
            setMaxActive(links.size());
            setMaxIdle(links.size());
        }else{
            if(maxKernels < 0){
                logger.info("Set number of Mathematica license to no limit");
            }else{
                logger.info("Set number of Mathematica license to [" + maxKernels + "]");
            }
            setMaxActive(maxKernels);
            setMaxIdle(maxKernels);         
        }

        Object ob = borrowObject(STARTUP_WAIT_TIME_MS);
        returnObject(ob);                   

        mathematicaConfigured = true;
    }catch(Throwable ex){
        logger.warn("Mathematica kernel pool could not be configured: ", ex.getMessage());
        mathematicaConfigured = false;
    }
}


public int getBorrowingThreadsCount() {
    return borrowingThreads.size();
}

public Integer getMaxKernels() {
    return maxKernels;
}

public void setMaxKernels(Integer maxKernels) {
    this.maxKernels = maxKernels;
}

public boolean isMathematicaConfigured(){
    return mathematicaConfigured;
}

public boolean isReleaseLicenseOnReturn() {
    return releaseLicenseOnReturn;
}

public void setReleaseLicenseOnReturn(boolean releaseLicenseOnReturn) {
    this.releaseLicenseOnReturn = releaseLicenseOnReturn;
}

public long getMaxBorrowWait() {
    return maxBorrowWait;
}

public void setMaxBorrowWait(long maxBorrowWait) {
    this.maxBorrowWait = maxBorrowWait;
}       
}

测试失败,出现此异常:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.etse.math.wolfram.KernelLinkPool] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

这是math-application-context文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

<beans profile="unitTest,integratedTest,activeServer">
    <bean class="org.springframework.jmx.export.MBeanExporter"
        lazy-init="false">
        <property name="registrationBehaviorName" value="REGISTRATION_IGNORE_EXISTING" />
        <property name="beans">
            <map>
                <entry key="etse.math:name=MathematicalKernelFactory"
                    value-ref="mathematicalKernelFactory" />
                <entry key="etse.math:name=MathematicalKernelPool" value-ref="mathematicalKernelPool" />
            </map>
        </property>
    </bean>

    <bean id="mathService" class="com.etse.math.MathServiceFactoryBean">
        <property name="mathServiceType" value="${calculation.math.service.type}"/>
        <property name="mathematicaService" ref="mathematicaService"/>
    </bean> 

    <bean id="mathematicaService" class="com.etse.math.wolfram.MathematicaService">
        <property name="kernelPool" ref="mathematicalKernelPool" />
        <property name="minParallelizationSize" value="${calculation.mathematica.kernel.parallel.batch.size}" />
    </bean>

    <bean id="mathematicalKernelPool" class="com.etse.math.wolfram.KernelLinkPool"
        destroy-method="close">
        <constructor-arg ref="mathematicalKernelFactory" />
        <property name="maxKernels" value="${calculation.mathematica.max.kernels}" />
        <property name="maxBorrowWait"
            value="${calculation.mathematica.kernel.borrow.max.wait}" />
        <property name="releaseLicenseOnReturn"
            value="${calculation.mathematica.kernel.release.license.on.return}" />
    </bean>

    <bean id="mathematicalKernelFactory" class="com.etse.math.wolfram.KernelLinkFactory">
        <property name="debugPackets" value="false" />
        <property name="linkMode" value="launch" />
        <property name="mathematicaKernelLocation" value="${calculation.mathematica.kernel.location}" />
        <property name="mathematicaLibraryLocation" value="${calculation.mathematica.library.location}" />
        <property name="mathematicaAddOnsDirectory" value="${calculation.mathematica.addons.directory}" />
        <property name="linkProtocol" value="sharedMemory" />
    </bean>
</beans>

<beans profile="passiveServer,thickClient,tools">
        <bean id="mathService" class="com.etse.math.DummyMathService"/>
</beans>

我也尝试使用应用程序上下文来加载bean,但是因为以下异常而失败:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'mathematicalKernelPool' is defined

如果我删除了自动连接字段,则测试会因为超级类中的应用程序上下文加载的另一个bean(mathService)的NoSuchBeanDefinitionException而失败。因此,似乎由于某种原因未加载来自math-application-context的应用程序上下文。想知道这里会发生什么吗?谢谢。

更新:

我查看了应用程序上下文中定义的bean,并确认在math-application-context中定义的bean都不存在。应用程序上下文仅包含在超类加载的另一个上下文文件中定义的bean。为什么无法加载math-application-context?

2 个答案:

答案 0 :(得分:0)

此时我会诚实地摆脱XML配置并基于总注释/代码。创建一个Config类,让它创建你需要自动装配的任何bean。

答案 1 :(得分:0)

这是个人资料问题。测试的超级课程使用:

@ProfileValueSourceConfiguration(TestProfileValueSource.class)

设置配置文件,但它无法正常工作。删除该注释后,我添加了:

@ActiveProfiles(resolver = TestProfileValueSource.class),现在再次运作。