5

Web アプリケーションでは、実際に行う必要はありません ..

ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");
ctx.getBean("beanId");

一般的な方法は、コンテキスト ファイルをロードし、このように web.xml で ContextLoaderServlet を使用して依存関係を持つすべての Bean を注入することです。

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/spring-context.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- or use the ContextLoaderServlet instead of the above listener
<servlet>
  <servlet-name>context</servlet-name>
  <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
--> 

ただし、コンテナーのないスタンドアロンの Java アプリでは、ctx.getBean("xyz");を実行することになります。. これを行うためのクリーンな方法はありますか?オンラインで例を見つけることができませんでした。

私はチェックしました..シンプルなSpring、スタンドアロンアプリ用のClasspathApplicationContextの使用、再利用方法は? 、 SingletonBeanFactoryLocatorの使用について説明していますが、最終的には context.getBean() を使用します。

ServiceLocatorFactoryBeanも調べましたが、プロキシを使用してオンデマンドで Bean を取得しています。

オンデマンドで Bean を取得したくないので、スタンドアロンの Java アプリの main() プログラムからコンテキスト ファイル (すべての Bean) をロードするソリューションを探しています。

コード例:

public interface IReader {
    public String read();
}

public class TextFileReader implements IReader {

    private StringBuilder builder = null;
    private Scanner scanner = null;

    public TextFileReader(String fileName) throws FileNotFoundException {
        scanner = new Scanner(new File(fileName));
        builder = new StringBuilder();
    }

    public String read() {
        while (scanner.hasNext()) {
            builder.append(scanner.next());
            builder.append(",");
        }
        return builder.toString();
    }
}



 public class SpringNoConextDataReaderClient {

    private IReader reader = null;

    public void setReader(TextFileReader reader) {
        this.reader = reader;
    }

    private String fetchDataOne() {
        return reader.read();
    }

    private String fetchDataTwo() {
        return reader.read();
    }

    public static void main(String[] args) {

        final ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        String fetchedData = context.getBean(SpringNoConextDataReaderClient.class).fetchDataOne(); // <-- reader is injected as TextFileReader in fetchDataOne which reads the file

        SpringNoConextDataReaderClient client = new SpringNoConextDataReaderClient();
        client.fetchDataOne(); // <--  reader is null and throws NPE, probably its lifetime ended with previous call?

        System.out.println("Example 1.1: Got data without context: " + fetchDataOne);
    }

}

spring-context.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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

    <bean id="reader" class="com.himalay.spring.core.basic.readers.TextFileReader">
        <constructor-arg value="src/main/resources/data.txt" />
    </bean>

    <bean id="springNoConextDataReaderClient" class="com.himalay.spring.core.basic.SpringNoConextDataReaderClient">
        <property name="reader"><ref bean = "reader" /></property>
    </bean>

</beans>

ありがとうございました。

4

1 に答える 1

8

スタンドアロン アプリケーションでは、ApplicationContext自分自身のインスタンスを作成し、それを使用して少なくとも 1 つの Bean をロードする必要があります。しかし、ロードした 1 つの Bean は、すべての Spring マジック@Autowiredなどを使用でき、それ以上使用する必要はありませんgetBean。したがって、1 つのブートストラップ Bean を使用してロードしgetBean、この 1 つの Bean に他のすべてを実行させることができます。何かのようなもの:

@Component
public class Main
{
    @Autowired
    protected MyDependencyClass2 someClass1;
    @Autowired
    protected MyDependencyClass2 someClass2;
    // ...
    // or if you need an entity manager
    @PersistenceContext
    protected EntityManager em;
    // etc.

    protected void mainInternal(String[] args)
        throws Exception
    {
        // do everything here
        // all dependencies are initialized
        // ...
    }

    public static void main(String[] args)
        throws Exception
{
        // Bootstrap Spring and let it create and configure beans.
        final ApplicationContext context =
            new ClassPathXmlApplicationContext("spring-context.xml");
        context.getBean(Main.class).mainInternal(args);
    }
}

注: 一般に、パラメーターを取るgetBean(Class)またはgetBean(String,Class)Class<T>のバリアントを使用する方が安全です。


を呼び出すだけnew Main()では、依存関係は初期化されません。Spring は、 を使用して作成したインスタンスについては認識せずnew、自身で作成したインスタンスについてのみ認識します。これは、Spring の重要な概念です。クラスのインスタンスを作成するだけでなく、他の Bean との依存関係を管理し、作成されたインスタンスをアスペクトなどを使用して処理できます。これは、 を使用して自分で作成したインスタンスでは不可能ですnew

ここでのポイントは、すべてのコードを から に移動するとmain、必要なすべての依存関係が初期化されるということです。だけでなく、その依存関係、それらの依存関係なども同様です。したがって、アプリケーションが Spring を使用して適切に構築され、Spring の機能 ( など) を使用して依存関係のみを管理する場合、環境に似た環境が得られます。ウェブアプリケーション。mainInternalMain@Autowired

したがって、この場合の適切な手順は次のとおりです。 のアプリケーション起動依存関係に必要なすべての Bean を作成しますMain。それらはすべての依存関係と一緒に初期化され、それらがmainInternal呼び出すもので安全に使用できます。


編集:あなたの例にコメントしてください。説明したように、 Spring はを使用して作成したオブジェクトではなく、 Spring が作成したオブジェクトのみを管理しますnew。あなたの例では、使用します

SpringNoConextDataReaderClient client = new SpringNoConextDataReaderClient();

そのためclient、Spring によって管理されず、その依存関係が設定または解決されません。次のように考えてみてください。自分でオブジェクトを作成した場合、Spring はどのようにしてそれを知ることができるのでしょうか?

また、あなたの例はうまく設計されていません。Spring の主なアイデアは、プログラム コンポーネントを管理し、制御原理の反転を使用してそれらを結び付けることです。ほとんどの場合、このようなプログラム コンポーネントは、アプリケーションの存続期間全体にわたって存在するシングルトン オブジェクトとして意図されています。(1 つの HTTP リクエストまたは 1 つの HTTP セッション スコープなど、寿命の短いコンポーネントを使用することもできますが、これはこの質問の範囲外です。) 重要な部分は、そのようなシングルトン コンポーネントは、一度それらの内部状態を変更してはならないということです。初期化しています。

一方、Spring は、 などのデータ オブジェクトを管理するためのものではありませんIReaderIReaderプログラムのコンポーネントではなく、作成し、ファイルから読み取り、後で破棄するオブジェクトです。より良い設計は次のようになります。

  • IReader次のようなオンデマンドを提供するシングルトン Bean を用意します。

    public class TextFileReaderProvider {
        public IReader createReader() { ... }
    }
    
  • このプロバイダーをSpringNoConextDataReaderClientlikeに配線します

    public class SpringNoConextDataReaderClient {
        @Autowired
        protected TextFileReaderProvider readerProvider;
    
        public SomeResult doMyComputation() {
            IReader r = readerProvider.createReader();
            try {
                // compute the result
                return theResult;
            } finally {
                r.close();
            }
        }
    }
    

    @Autowired(または、依存関係を XML で手動で構成する代わりに)。

  • mainで、Spring に のインスタンスを取得させ、これSpringNoConextDataReaderClientを呼び出しdoMyComputation()ます。

このような設計により、ソフトウェアを個別のコンポーネントに分離し、アプリケーション全体で再利用でき、並行性の問題も発生しません。

于 2012-08-15T15:16:30.947 に答える