1

私は、Eclipse OSGi コンテナ (これを実行しようとしている TIBCO ActiveMatrix ランタイムの基礎です) 内での Spring のクラスローディング動作について少し困惑しており、これを以下の簡単な例にまとめました。

その中で (TestComponent オブジェクトは OSGi バンドルの Activator クラス内で作成され、呼び出されますが、ここでは重要ではないと思います) まずオブジェクトを直接作成し、そのクラスが存在し、インスタンス化できることを再確認します。 . もちろん、これは期待どおりに機能します... 次に、Spring を使用してまったく同じクラスの 2 番目のオブジェクトを作成したい (最初に意図したとおり) が、これは ClassNotFoundException で失敗します。

Spring は、(パッケージとクラス名が確実に一致していても) そのクラスを見つけることができないと主張しているため、.setClassLoader(...) 呼び出しを追加して、まったく同じクラスを正常にロードした同じクラスローダーを渡しました。 、しかしSpringはまだそのクラスを見つけることができません。それがなぜそうなのかの手がかりはありますか?私はアイデアを使い果たしました。私は何が欠けていますか?

後で編集: クラッシュするのは ...getBean(...) メソッド呼び出しではなく、既に ClassPathXmlApplicationContext() コンストラクターであることを認識しなければなりませんでした。つまり、オブジェクトはそのコンストラクターで既に作成されており、その後の getBean(...) メソッド呼び出しだけではありません。したがって、クラスローダーを渡そうとする私の試みは、すでに遅れているため無駄です。したがって、問題は次のとおりです。コンテキストのクラスローダーをそのコンストラクター (またはファクトリー、または Spring が ClassPathXmlApplicationContext オブジェクトを作成するために内部で使用するもの) に渡すにはどうすればよいですか?

私の例:

最初に、Spring 経由で作成されるクラスのインターフェースを定義しました。

package com.example.some_package_0;    
public interface SomeInterface 
{
    public String getSomeString();
}

...そして、このインターフェースを実装するクラス:

package com.example.some_package_1;

import com.example.some_package_0.SomeInterface;

public class SomeClassA implements SomeInterface
{
    private String someProperty;

    public void setSomeProperty(String someProperty) {
        this.someProperty = someProperty;
    }
    public String getSomeString() {
        return this.someProperty;
    }
}

私のテストプログラムの読み取り

public class TestComponent 
{
    import com.example.some_package_0.SomeInterface;
    import com.example.some_package_1.SomeClassA;

    public void test() {
        SomeClassA obj1 = new SomeClassA();
        obj1.setSomeProperty("SomeClassA-object (directly created)");
        System.out.println("@@ message=\"" + obj1.getSomeString() + "\"");          

        ClassPathXmlApplicationContext applicationContext;          
        applicationContext = new ClassPathXmlApplicationContext("/META-INF/package1_beans.xml");
        applicationContext.setClassLoader(Thread.currentThread().getContextClassLoader());
        SomeInterface obj2 = (SomeInterface) applicationContext.getBean("bean1");
        System.out.println("@@ message=\"" + obj2.getSomeString() + "\"");
    }
}

使用されている /META-INF/package1_beans.xml の読み取り:

<?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 classpath:/org/springframework/beans/factory/xml/spring-beans-2.5.xsd">

    <bean id="bean1" class="com.example.some_package_1.SomeClassA">
        <property name="someProperty"><value>SomeClassA-object (created via Spring)</value></property>
    </bean>
</beans>

例外は次のとおりです。

org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.example.some_package_1.SomeClassA] for bean with name 'bean1' defined in class path resource [META-INF/package1_beans.xml]; nested exception is java.lang.ClassNotFoundException: com.example.some_package_1.SomeClassA
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1141)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:524)
    at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1177)
    at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:758)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:422)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:380)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
    at com.example.test_spring_example.TestComponent.testOperation(TestComponent.java:71)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.tibco.amf.platform.runtime.componentframework.internal.proxies.operation.OperationHandler.invokeMethodWithThreadContext(OperationHandler.java:667)
    at com.tibco.amf.platform.runtime.componentframework.internal.proxies.operation.AsyncToSyncOperationHandler.invoke(AsyncToSyncOperationHandler.java:98)
    at com.tibco.amf.platform.runtime.componentframework.internal.proxies.ProxyInvocationHandlerRegistry$ProxyInvocationContext.invoke(ProxyInvocationHandlerRegistry.java:411)
    at $Proxy67.invoke(Unknown Source)
    at com.tibco.amf.binding.soap.runtime.transport.http.SoapHttpInboundEndpoint.processHttpPost(SoapHttpInboundEndpoint.java:565)
    at com.tibco.amf.binding.soap.runtime.transport.http.SoapHttpServer.doPost(SoapHttpServer.java:195)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502)
    at org.mortbay.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1213)
    at com.tibco.governance.pa.amxcomponent.pep.http.HttpPepFilter.doFilter(HttpPepFilter.java:126)
    at org.mortbay.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1205)
    at com.tibco.amf.implementation.common.httpfilter.GenericComponentFilter.doFilter(GenericComponentFilter.java:65)
    at org.mortbay.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1205)
    at com.tibco.amf.hpa.tibcohost.jetty.internal.ConnectorFilter.doFilter(ConnectorFilter.java:49)
    at org.mortbay.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1205)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
    at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:536)
    at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:928)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:747)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:405)
    at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
Caused by: java.lang.ClassNotFoundException: com.example.some_package_1.SomeClassA
    at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:513)
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:429)
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:417)
    at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    at org.springframework.util.ClassUtils.forName(ClassUtils.java:211)
    at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:385)
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1138)
    ... 46 more
4

2 に答える 2

3

実際、解決策はほとんど簡単であることがわかりました。@Robin へのコメントで既に述べたように、明らかに OSGi では、現在のスレッドのクラスローダーと現在のコンテキストまたはクラスのクラスローダーは同じではありません!

結局、現在のスレッドの context-classloader を呼び出し元のオブジェクトの classloader に設定するだけで済みました。つまり、OSGi を掘り下げてバンドルのクラスローダーを取得したり、マニフェストや POM などをいじったりする必要さえありません。Spring に「自分の」クラスのクラスローダーを使用するように指示するだけで、すぐに使用できます。

     ...
        // need to set the context class loader to "my" class loader to make Spring work:
        Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());         
        ClassPathXmlApplicationContext applicationContext;          
        applicationContext =
            new ClassPathXmlApplicationContext("/META-INF/package1_beans.xml");
        SomeInterface obj2 = (SomeInterface) applicationContext.getBean("bean1");
        System.out.println("@@ message=\"" + obj2.getSomeString() + "\"");
     ...

そして-はい-念のため、その後スレッドのクラスローダーを元の値に戻す必要があります-したがって、try { ... } finally { ... } 句に別の良いものを使用します。:-)

回答ありがとうございます。M.

于 2013-01-10T18:08:59.847 に答える
0

もう1つ考えがあります。

Spring コンテキストを手動で初期化する代わりに。これを pom.xml に追加してみてください (Maven を使用している場合):

<configuration>
    <instructions>
        <Spring-Context>spring/*.xml</Spring-Context>

applContext の場所を指すようにします。これは、それを接続するためのよりきちんとした方法のようにも見えます。Mavenを使用していない場合は、マニフェストに手動で追加する必要があります。そのための正しい構文が正確にはわかりません

于 2013-01-10T11:04:46.993 に答える