2

そのクラスのメソッドが呼び出されたときにクラスが CREATED (新しいインスタンス) および INITIALIZED (セッターが呼び出される) になるように、クラスをプロキシするためのすぐに使用できるソリューションが Spring にありますか?

org.springframework.aop.target.LazyInitTargetSource を見つけて使ってみました。

たぶん私は何か間違ったことをしているのですが、次のシナリオではクラスインスタンスが 2 回作成されています。Bean がコンテキストから取得されたときに 1 回、次にメソッドが呼び出されたときにもう一度:

私のプロキシクラス:

public class NewClass {

    private Integer i;

    public NewClass() {
        System.out.println("NewClass()");
    }

    public void setI(Integer i) {
        System.out.println("setI(): " + i);
        this.i = i;
    }

    public Integer add() {
        return i + 1;
    }
}

春の設定:

<bean id="newClassTarget" class="com.mycompany.spring.NewClass" lazy-init="true">
    <property name="i" value="1"/>
</bean>

<bean id="newClass" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource">
        <bean class="org.springframework.aop.target.LazyInitTargetSource">
            <property name="targetBeanName">
                <idref local="newClassTarget"/>
            </property>
        </bean>
    </property>
</bean>

実行中のコード:

public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("lazy.xml");
    System.out.println("Context initialized");
    System.out.println("before getting bean");
    NewClass newClass = (NewClass) applicationContext.getBean("newClass");
    System.out.println("after getting bean");
    System.out.println("calling add()...");
    System.out.println(newClass.add());
}

生成:

Context initialized
before getting bean
NewClass()
after getting bean
calling add()...
NewClass()
setI(): 1
2

そのため、Spring コンテキストから Bean を取得するとき、および add() メソッドを呼び出すときに、NewClass コンストラクターが呼び出されます。私はそれがいいとは思わない、私は何かを台無しにしましたか?

いずれにせよ、最初の呼び出しは Cglib2AopProxy.getProxy() のEnhancer.create() から来ます。最初のメソッド呼び出しが発生したときではなく、Bean が要求されたときにプロキシがプロキシされたクラスのインスタンスを作成するようです。それは私が望むものではありません。

NewClass のホルダーとして独自の java.lang.reflect.Proxy を作成し、メソッドが最初に呼び出されたときにハンドラーの invoke() で NewClass のインスタンスを作成できます。ただし、最初にセッターを呼び出すことで遊んでいたでしょう。

私が達成するために使用できるSpringの準備が整ったソリューションはありますか?

Context initialized
before getting bean
after getting bean
calling add()...
NewClass()
setI(): 1
2

?

4

3 に答える 3

2

私はあなたの振る舞いを再現することができました - 私が持っている修正は、単に NewClass のインターフェースを定義することです。このようにして、CGLIB 拡張プロキシの代わりに Java 動的プロキシが作成されます。次に、期待どおりに機能します-メソッドが呼び出された時点で NewClass をインスタンス化します。

public interface NewInterface {
    public void setI(Integer i);
    public Integer add() ;
}

public class NewClass implements NewInterface{

    private Integer i;

    public NewClass() {
        System.out.println("NewClass()");
    }

    public void setI(Integer i) {
        System.out.println("setI(): " + i);
        this.i = i;
    }

    public Integer add() {
        return i + 1;
    }
}
于 2012-06-19T12:42:26.283 に答える
0

@PostConstructメソッドでクラスを初期化してみてください。Springは、プロキシではなく、クラスの実際のインスタンスに対してのみこのメソッドを呼び出すと思います。

アップデート

注釈構成を有効にした場合は、次のことを試してください。

public class NewClass {

    private Integer i;

    public NewClass() {
        System.out.println("NewClass()");
    }

    @PostConstruct
    public void init() {
        System.out.println("Initialization");
    }

    public void setI(Integer i) {
        System.out.println("setI(): " + i);
        this.i = i;
    }

    public Integer add() {
        return i + 1;
    }
}

"Initialization"コンソールに印刷された 回数を確認します。

一部のリンク:
アノテーションなしの初期化メソッド:http:
//www.mkyong.com/spring/spring-init-method-and-destroy-method-example/アノテーション付きの初期化メソッド: http ://www.mkyong.com/spring / spring-postconstruct-and-predestroy-example /

于 2012-06-19T08:57:07.560 に答える
0

すべてのコメントをありがとう。メソッドが呼び出されたときと同じように、クラスを作成および初期化するための高速な(完全ではない)ソリューションを思いつきました。あまり複雑ではないことを願っています。このソリューションで気に入らない点は、setter 呼び出しの委任に関する問題です。ファクトリとクラスが同じ「設定可能な」インターフェイスを実装するようにしました。多分誰かがより良い考えを持っているでしょう。

クラスとそのプロキシ ファクトリの共通インターフェイス:

public interface ProxiedClassSettable {

    void setI(Integer i);
}

プロキシされたクラス インターフェイス (「セッター」インターフェイスを拡張します):

public interface ProxiedClass extends ProxiedClassSettable {

   Integer add();

}

プロキシされるクラス:

public class ProxiedClassImpl implements ProxiedClass {

    private Integer i;

    public ProxiedClassImpl() {
        System.out.println("ProxiedClassImpl()");
    }

    public Integer add() {
        return i + 1;
    }

    public void setI(Integer i) {
        System.out.println("setI(): " + i);
        this.i = i;
    }
}

プロキシ ファクトリ (ProxiedClass との結合を示すために「セッター」インターフェイスを実装しました):

public class ProxiedClassFactory implements FactoryBean, ProxiedClassSettable {

    private Integer i;

    public Object getObject() throws Exception {
        return Proxy.newProxyInstance(ProxiedClass.class.getClassLoader(),
                new Class[]{ProxiedClass.class},
                new InvocationHandler() {

                    private ProxiedClass proxiedClass;

                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (proxiedClass == null) {
                            proxiedClass = new ProxiedClassImpl();
                            //not a very nice way to initialize the proxied class
                            proxiedClass.setI(i);
                        }
                        return method.invoke(proxiedClass, args);
                    }
                });
    }

    public Class getObjectType() {
        return ProxiedClass.class;
    }

    public boolean isSingleton() {
        return true;
    }

    public void setI(Integer i) {
        this.i = i;;
    }
}

春の XML:

<bean id="proxiedClass" class="com.mycompany.spring.proxy.ProxiedClassFactory">
    <property name="i" value="1"/>
</bean>

ランニング:

public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("proxiedClass.xml");
    System.out.println("Context initialized");
    System.out.println("before getting bean");
    ProxiedClass newClass = (ProxiedClass) applicationContext.getBean("proxiedClass");
    System.out.println("after getting bean");
    System.out.println("calling add()...");
    System.out.println(newClass.add());
}

私が欲しかったものを生成します-クラスは最初にそのメソッドが呼び出されるものとして作成されます:

Context initialized
before getting bean
after getting bean
calling add()...
ProxiedClassImpl()
setI(): 1
2
于 2012-06-19T13:31:45.543 に答える