14

内部 DSL に変換する API があります。そのため、私の PoJos のほとんどのメソッドは this への参照を返すため、メソッドを宣言的に連鎖させることができます (シンタックス シュガー)。

myComponent
    .setID("MyId")
    .setProperty("One")
    .setProperty2("Two")
    .setAssociation(anotherComponent)
    .execute();

私の API は Spring に依存していませんが、引数のないコンストラクター、ゲッター、セッターを使用して PoJo フレンドリーにすることで、「Spring フレンドリー」にしたいと考えています。問題は、戻り値の型が void でない場合、Spring がセッター メソッドを検出しないように見えることです。

これの戻り値の型は、コマンドをチェーンするときに非常に便利なので、Spring インジェクションとの互換性のためにプログラム API を破棄したくありません。

非 void セッターを使用できるようにする Spring の設定はありますか?

クリス

4

6 に答える 6

9

すべての人 (特に、Spring 内のさまざまなオプションを示すために多大な努力を払ってくれた Espen) に感謝します。

結局、Spring 構成を必要としない解決策を自分で見つけました。

Stephen C のリンクをたどると、そのスレッドのセット内に SimpleBeanInfo クラスへの参照が見つかりました。このクラスを使用すると、ユーザーは独自の Bean メソッド解決コードを記述できます。そのためには、非標準のセッター/ゲッターを含むクラスと同じパッケージに別のクラスを配置し、クラス名に「BeanInfo」を追加してロジックをオーバーライドし、「BeanInfo」を実装します。 ' インターフェース。

その後、Google で検索を行ったところ、道を示しているこのブログを見つけました。ブログの解決策は非常に基本的なものだったので、目的のためにパディングしました。

クラスごと(流暢なセッターを使用)

public class MyComponentBeanInfo<T> extends SimpleBeanInfo {

private final static Class<?> _clazz = MyComponent.class;
PropertyDescriptor[] _properties = null;

public synchronized PropertyDescriptor[] getPropertyDescriptors() {
    if (_properties == null) {
        _properties = Helpers.getPropertyDescriptionsIncludingFluentSetters(_clazz);
    }
    return _properties;
}

public BeanDescriptor getBeanDescriptor() {
    return new BeanDescriptor(_clazz);
}
}

PropertyDescriptor の生成方法

public static PropertyDescriptor[] getPropertyDescriptionsIncludingFluentSetters( Class<?> clazz) {
    Map<String,Method> getterMethodMap = new HashMap<String,Method>();
    Map<String,Method> setterMethodMap = new HashMap<String,Method>();
    Set<String> allProperties = new HashSet<String>();
    PropertyDescriptor[] properties = null;
    try {
        Method[] methods = clazz.getMethods();
        for (Method m : methods) {
            String name = m.getName();
            boolean isSetter = m.getParameterTypes().length == 1 && name.length() > 3 && name.substring(0,3).equals("set") && name.charAt(3) >= 'A' && name.charAt(3) <= 'Z';
            boolean isGetter = (!isSetter) && m.getParameterTypes().length == 0 && name.length() > 3 && name.substring(0,3).equals("get") && name.charAt(3) >= 'A' && name.charAt(3) <= 'Z';

            if (isSetter || isGetter) {
                name = name.substring(3);
                name = name.length() > 1
                        ? name.substring(0,1).toLowerCase() + name.substring(1)
                        : name.toLowerCase();

                if (isSetter) {
                    setterMethodMap.put(name, m);
                } else {
                    getterMethodMap.put(name, m);
                }
                allProperties.add(name);
            }
        }

        properties = new PropertyDescriptor[allProperties.size()];
        Iterator<String> iterator = allProperties.iterator();
        for (int i=0; i < allProperties.size(); i++) {
            String propertyName = iterator.next();
            Method readMethod = getterMethodMap.get(propertyName);
            Method writeMethod = setterMethodMap.get(propertyName);
            properties[i] = new PropertyDescriptor(propertyName, readMethod, writeMethod);
        }
    } catch (IntrospectionException e) {
        throw new RuntimeException(e.toString(), e);
    }
    return properties;
}

このアプローチの利点:

  • カスタム Spring 構成はありません (Spring は非標準のセッターを認識せず、それらを通常と見なします)。Spring .jar ファイルには依存しませんが、Spring からアクセスできます。
  • ただうまくいくようです。

このアプローチの欠点:

  • 非標準のセッターを使用して、すべての API クラスに BeanInfo クラスを作成する必要があります。幸いなことに、そのようなクラスは約 10 個しかなく、メソッド解決ロジックを別のクラスに移動することで、維持する場所が 1 つだけになりました。

最後に

私の意見では、Spring は流暢なセッターをネイティブに処理する必要があります。それらは誰も傷つけず、戻り値を無視するだけです。

セッターが厳密に無効であることを要求することで、そうでない場合よりも多くの定型コードを作成する必要がありました。Bean 仕様には感謝していますが、Bean の解決は、標準の Bean リゾルバーを使用しなくてもリフレクションを使用すると簡単なので、Spring はこの状況を処理する独自の Bean リゾルバーのオプションを提供する必要があります。

必ず、標準のメカニズムをデフォルトのままにしておきますが、1 行の構成オプションを提供してください。これがオプションで緩和される将来のバージョンを楽しみにしています。

于 2010-05-25T01:59:29.093 に答える
9

非 void セッターを使用できるようにする Spring の設定はありますか?

簡単な答えはノーです。そのような設定はありません。

Spring は JavaBeans 仕様と互換性を持つように設計されており、セッターが を返す必要がありvoidます。

ディスカッションについては、この Spring フォーラムのスレッドを参照してください。フォーラムで言及されているこの制限を回避する方法はいくつかありますが、簡単な解決策はありません。また、これを試してうまくいったと報告した人は誰もいないと思います。

于 2010-05-25T00:08:49.833 に答える
7

Spring は、Java 構成で構成することもできます。

例:

@Configuration
public class Config {
    @Bean
    public MyComponent myComponent() {
        return MyComponent
            .setID(id)
            .setProperty("One", "1")
            .setProperty("Two", "2")
            .setAssociation(anotherConfig.anotherComponent())
            .execute();
    }

    @Autowired
    private AnotherConfig anotherConfig;

    @Value("${id}")
    private String id;
}

あなたは素敵な不変オブジェクトを持っています。Builder パターンを実際に実装しました。

Chris のコメントに対応するために更新されました。

それはまさにあなたが望むものではないと思いますが、プロパティ ファイルを使用するといくつかの問題が解決します。上記の例の id フィールドを参照してください。

それ以外の場合は、Spring のFactoryBeanパターンを使用できます。

public class MyComponentFactory implements FactoryBean<MyComponent> {

    private MyComponent myComponent;

    public MyComponentFactory(String id, Property propertyOne, ..) {
        myComponent = MyComponent
            .setID(id)
            .setProperty("One", "1")
            .set(..)
            .execute();
    }

    public MyComponent getObject() throws Exception {
        return myComponent;
    }

    public Class<MyComponent> getObjectType() {
        return MyComponent.class;
    }

    public boolean isSingleton() {
        return false;
    }
}

FactoryBean を使用して、getObject()メソッドから返されたオブジェクトから構成を保護します。

XML 構成では、FactoryBean 実装を構成します。この場合、<constructor-arg />要素を使用します。

于 2010-05-25T00:09:29.527 に答える
4

簡単な提案の 1 つとして、setter を使用せず、プロパティ自体に名前を付けるのが通例です。したがって、セッターを用意し、ビルダー用の別のメソッドを用意します。

component.id("MyId")
    .property("One")
    .property2("Two")
    .association(anotherComponent)
    .execute();
于 2010-05-25T04:18:53.767 に答える
3

私の知る限り、単純なスイッチはありません。Spring は Beans 規則を使用し、void セッターを想定しています。Spring は、 BeanWrapperインターフェースのインスタンスを介してプロパティ レベルで Bean を操作します。デフォルトの実装である BeanWrapperImpl はイントロスペクションを使用しますが、リフレクションを使用してパターンに一致するメソッドを見つける独自の変更バージョンを作成できます。

編集:Spring コードを見るとBeanWrapperImpl、Bean ファクトリに組み込まれています。これを別の実装に置き換える簡単な方法はありません。ただし、Spring はイントロスペクションを使用するため、必要な結果を生成するためにjava.beans.Introspectorの取得に取り組むことができます。痛みが軽減する順に以下の選択肢があります。

  1. 準拠するようにセッターのメソッド署名を変更してください。
  2. BeanInfoBean ごとに独自のクラスを実装する
  3. リフレクションを使用して、動的に生成さBeanInfoれたクラスをイントロスペクターにプラグインします。

最初の 2 つのオプションは、非常に多くの変更を伴うため、おそらく実際にはオプションではありません。3 番目のオプションをさらに詳しく調べる:

  1. Spring によってどの Bean がインスタンス化されているかを知るには、独自のBeanFactoryPostProcessorを実装します。これにより、BeanFactory によって使用される前にすべての Bean 定義が表示されます。実装は、要素内のすべての BeanDefinitions を反復処理し、各定義から Bean クラスをフェッチします。これで、使用されているすべてのクラスがわかります。

  2. クラスのリストを使用して、これらのクラス用に独自の BeanInfo を作成することを設定できます。Introspector を使用して、各クラスのデフォルトの BeanInfo を生成します。これにより、戻り値セッターを持つプロパティの読み取り専用プロパティが得られます。次に、元の BeanInfo に基づいて新しい BeanInfo を作成しますが、PropertyDescriptors はセッター メソッド (戻り値のセッター) を参照します。

  3. クラスごとに生成された新しい beanInfo を使用して、クラスの beaninfo を要求されたときに Introspector がこれらを返すことを確認する必要があります。イントロスペクターには、beanInfo をキャッシュするために使用されるプライベート Map があります。リフレクションを介してこれを取得し、アクセスを有効にし - setAccessible(true) - BeanInfo インスタンスをそれに追加できます - map.put(Class,BeanInfo)

  4. Spring がイントロスペクターに Bean クラスの BeanInfo を要求すると、イントロスペクターは変更された beanInfo を返し、戻り値を持つセッターにマップされたセッター メソッドを完了します。

于 2010-05-25T00:13:24.297 に答える
2

他の人が言ったように、あなたが失うリスクがあるのは、Spring との親和性だけではありません。非 void セッターは、JavaBeans に関する限り、実際にはセッターではありません。また、他のあらゆる種類のツール (バリデーター、マーシャラー、ビューアー、永続化ツールなど、他に思いつくものは何でも) は、おそらくセッターを期待するIntrospectorBeanInfoを使用します。ヌルであること。

これを念頭に置いて、それらが呼び出されるという要件はどの程度柔軟setXですか? Java の流暢なインターフェースの多くはwithX代わりに使用します。Eclipse を使用している場合は、おそらくコード生成テンプレートを作成してX getX()、 、void setX(X x)、およびX withX(X x)を作成できます。他の codegen ツールを使用している場合は、withX流暢なセッター/ゲッター メソッドを追加するのも簡単だと思います。

このwith言葉は少し奇妙に思えますが、コンストラクターと並べて見ると、非常に読みやすくなっています。

Request r = new Request().withEndpoint("example.com")
                         .withPort(80)
                         .withSSL(false)
                         .withFoo("My Foo");

service.send(r);

そのような API の 1 つがAWS SDK for Javaで、例について調べることができます。トピック外の警告は、booleangetter を呼び出すことができますがisXBooleangetter を呼び出さなければならないということgetXです。

于 2010-05-25T04:21:26.960 に答える