私たちが知っているように春には、(機能を追加するためにプロキシを使用する@Transactional
と、@Scheduled
たとえば)。2つのオプションがあります-JDK動的プロキシを使用する(クラスは空でないインターフェースを実装する必要があります)、またはCGLIBコードジェネレーターを使用して子クラスを生成します。proxyModeを使用すると、JDK動的プロキシとCGLIBのどちらかを選択できるといつも思っていました。
しかし、私の想定が間違っていることを示す例を作成することができました。
ケース1:
シングルトン:
@Service
public class MyBeanA {
@Autowired
private MyBeanB myBeanB;
public void foo() {
System.out.println(myBeanB.getCounter());
}
public MyBeanB getMyBeanB() {
return myBeanB;
}
}
プロトタイプ:
@Service
@Scope(value = "prototype")
public class MyBeanB {
private static final AtomicLong COUNTER = new AtomicLong(0);
private Long index;
public MyBeanB() {
index = COUNTER.getAndIncrement();
System.out.println("constructor invocation:" + index);
}
@Transactional // just to force Spring to create a proxy
public long getCounter() {
return index;
}
}
メイン:
MyBeanA beanA = context.getBean(MyBeanA.class);
beanA.foo();
beanA.foo();
MyBeanB myBeanB = beanA.getMyBeanB();
System.out.println("counter: " + myBeanB.getCounter() + ", class=" + myBeanB.getClass());
出力:
constructor invocation:0
0
0
counter: 0, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$2f3d648e
ここでは2つのことがわかります。
MyBeanB
一度だけインスタンス化されました。- の
@Transactional
機能を追加するためにMyBeanB
、SpringはCGLIBを使用しました。
ケース2:
MyBeanB
定義を訂正させてください:
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBeanB {
この場合の出力は次のとおりです。
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$b06d71f2
ここでは2つのことがわかります。
MyBeanB
3回インスタンス化されました。- の
@Transactional
機能を追加するためにMyBeanB
、SpringはCGLIBを使用しました。
何が起こっているのか説明してもらえますか?プロキシモードは実際にはどのように機能しますか?
PS
ドキュメントを読みました:
/**
* Specifies whether a component should be configured as a scoped proxy
* and if so, whether the proxy should be interface-based or subclass-based.
* <p>Defaults to {@link ScopedProxyMode#DEFAULT}, which typically indicates
* that no scoped proxy should be created unless a different default
* has been configured at the component-scan instruction level.
* <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML.
* @see ScopedProxyMode
*/
しかし、それは私には分かりません。
更新
ケース3:
私はもう1つのケースを調査しましたMyBeanB
。
public interface MyBeanBInterface {
long getCounter();
}
@Service
public class MyBeanA {
@Autowired
private MyBeanBInterface myBeanB;
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES)
public class MyBeanB implements MyBeanBInterface {
この場合の出力は次のとおりです。
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class com.sun.proxy.$Proxy92
ここでは2つのことがわかります。
MyBeanB
3回インスタンス化されました。- の
@Transactional
機能を追加するためにMyBeanB
、SpringはJDK動的プロキシを使用しました。
MyBeanB
クラスはインターフェイスを拡張しないため、コンソールログにCGLIBプロキシインスタンスが表示されることは当然です。ケース3では、インターフェースを導入して実装し、その結果、JDKプロキシを取得します。導入テキストでこれを説明することもできます。
<aop:config proxy-target-class="true">
またはを介して@EnableAspectJAutoProxy(proxyTargetClass = true)
それぞれ設定されます。