JDK動的エージェントとCGLIB動的エージェントに基づくSpring注釈管理トランザクション(@Trasactional)の実装にはどのような違いがあるのでしょうか.
JDK動的エージェントとCGLIB動的エージェントに基づくSpring注釈管理トランザクション(@Trasactional)の実装にはどのような違いがあるのでしょうか.
私はやはりSchemaスタイルのSpringトランザクション管理が好きですが、@Trasactional注釈ベースのトランザクション管理を使っている人も多いですが、JDKダイナミックエージェントとCGLIBダイナミックエージェントベースのSpring注釈管理トランザクションの実現には違いがあります.次に、どのような違いがあるのか見てみましょう.
一、基礎工作
まず前回やったことを修正します SpringMVC+spring 3.1.1+hibernate 4.1.0統合およびよくある質問のまとめを以下に示します.
xml宣言トランザクションの削除
JAvaコード:
注記トランザクションのサポートを追加します.
JAvaコード:
私たちのBaseServiceインタフェースに@Transactionalを追加して、この方法でトランザクションを開始します.
JAvaコード:
我々のlog 4 j.propertiesにspringを出力するすべてのdebug情報を示す構成を追加します.
JAvaコード:
私たちのresources.propertiesでhibernate.show_sql=trueをtrueに変更し、hibernateのsqlを見るために.
ユニットテストクラス:
JAvaコード:
基本的な作業は、次にSpringがJDKベースのダイナミックエージェントとCGLIBクラスレベルエージェントの違いを詳しく見てみましょう.
二、JDKに基づく動的代理店:
JAvaコード:
この構成は、デフォルトではJDKダイナミックエージェントです.
セルテストを実行します.コア・ログは次のとおりです.
JAvaコード:
これでトランザクションが機能していることがわかります.つまり、@Transactionalをインタフェースに配置してJDKベースのダイナミックエージェントでも動作します.
三、CGLIB類エージェントに基づく:
JAvaコード:
この構成はCGLIBクラスエージェントに基づく
テストを開始するとエラーが発生します.No Session found for current threadは、トランザクションが機能していないことを示します.
JAvaコード:
注釈を特定のクラスまたは特定のクラスの実装方法に置くと機能します.
JAvaコード:
テストクラスを実行すると、Userviceがメソッドを継承しているため、正常に検出されますが、Userviceがメソッドを上書きすると、次のようにトランザクションを織り込むことができません(エラー).
JAvaコード:
四、aspectjに基づく
JAvaコード:
ここでは実証しないが,主にJDK動的エージェントとCGLIBクラスエージェントの2つの違いに基づいて解析する.
五、結論:
JDKダイナミックエージェントに基づいて、@Transactionalをインタフェースと特定のクラスに配置できます.
CGLIBクラスエージェントに基づいて、@Transactionalのみを特定のクラスに配置できます.
したがって,@Transactionalはインタフェースではなく,実際の開発時にすべて特定のクラスに配置される.
六、分析
1、 JDKダイナミックエージェント
1.1、SpringはJdkDynamicAopProxyを使用して代理を実現する:
JAvaコード:
ここでのmethodは必ずインタフェース上のmethodであることに注意してください(したがってインタフェース上に置かれている@Transactionalは発見可能です)
1.2、もし
JAvaコード:
1.3、Spring AnnotationTransactionAttributeSourceを使用して、クラスまたはメソッドに@Transactional注釈トランザクションがあるかどうかを検索してTransactionAttribute(オープントランザクションを示す)を返します.
JAvaコード:
一方、A n t o t i o n T r a n s c t ionAttributeSourceでは、@Transactional注記があるかどうかを解析するためにSpringTransactionAnnotationParserが使用されます.
JAvaコード:
ここではAnnotationUtils.getAnnotation(ae,Transactional.class)を使用します.このメソッドでは、現在のメソッド/クラスの注釈のみが検出され、親の注釈は検出されません.Springでは、親/親インタフェースの注記を検出するAnnotationUtils.findAnnotation()メソッドも用意されています(ただしspringでは使用されていません).
SpringでAnnotationUtils.findAnnotation()に変更すると、親/親インタフェースの注記が表示されます.
ここでは、次のように説明します.
インタフェースで@Transactionalを削除 //デフォルトのトランザクションを開く
JAvaコード:
クラスに@Transactionalを追加
JAvaコード:
質問:
JDKダイナミックエージェントに基づいている場合、methodはインタフェース上のmethodに違いない(したがってインタフェース上に置かれている@Transactionalは発見できる)と述べたが、今は具体的なクラスに置かれているが、Springはどのように発見されているのだろうか.
TransactionAttributeがAnnotationTransactionAttributeSourceを通過したことを覚えていますか?具体的には、手順1.3を参照してください.
A n t o n t i o n T r a n s c t ionAttributeSourceはAbstractFallbackTransactionAttributeSourceを継承します
JAvaコード:
//①ここではサブクラスオーバーライドの方法を探します
Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
//ClassUtils.getMostSpecificMethod
public static Method getMostSpecificMethod(Method method, Class> targetClass) {
Method specificMethod = null;
if (method != null && isOverridable(method, targetClass) &&
targetClass != null && !targetClass.equals(method.getDeclaringClass())) {
try {
specificMethod = ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());
} catch (AccessControlException ex) {
//security settings are disallowing reflective access; leave
//'specificMethod' null and fall back to 'method' below
}
}
return (specificMethod != null ? specificMethod : method);
}
現在のクラスを見つける方法がわかります.そのため、BaseService countAllメソッドに配置されている@Transactionalが機能します.
//②サブクラスの上書き方法が現在のパスを直接探していない場合
if (specificMethod != method) {
//Fallback is to look at the original method.
txAtt = findTransactionAttribute(method);
if (txAtt != null) {
return txAtt;
}
//Last fallback is the class of the original method.
return findTransactionAttribute(method.getDeclaringClass());
}
サブクラスの検索に失敗した場合は、転送されたメソッドを直接使用します.
したがって、Schemaベースのトランザクションを使用することをお勧めします(多くの問題を考慮する必要はありません.クラスかメソッドかを考慮する必要はありません).@Transactionalは、インタフェースに配置しないで、特定のクラスに配置することをお勧めします.
作者オリジナルhttp://sishuok.com/forum/blogPost/list/0/3845.html#9317】
私はやはりSchemaスタイルのSpringトランザクション管理が好きですが、@Trasactional注釈ベースのトランザクション管理を使っている人も多いですが、JDKダイナミックエージェントとCGLIBダイナミックエージェントベースのSpring注釈管理トランザクションの実現には違いがあります.次に、どのような違いがあるのか見てみましょう.
一、基礎工作
まず前回やったことを修正します SpringMVC+spring 3.1.1+hibernate 4.1.0統合およびよくある質問のまとめを以下に示します.
xml宣言トランザクションの削除
JAvaコード:
注記トランザクションのサポートを追加します.
JAvaコード:
私たちのBaseServiceインタフェースに@Transactionalを追加して、この方法でトランザクションを開始します.
JAvaコード:
package cn.javass.common.service;
public interface IBaseService {
@Transactional //
public int countAll();
}
我々のlog 4 j.propertiesにspringを出力するすべてのdebug情報を示す構成を追加します.
JAvaコード:
log4j.logger.org.springframework=INFO,CONSOLE
私たちのresources.propertiesでhibernate.show_sql=trueをtrueに変更し、hibernateのsqlを見るために.
ユニットテストクラス:
JAvaコード:
package cn.javass.ssonline.spider.service.impl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import cn.javass.demo.service.UserService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-config.xml"})
public class UserServiceTest2 {
@Autowired
private UserService userService;
@Test
public void testCreate() {
userService.countAll();
}
}
基本的な作業は、次にSpringがJDKベースのダイナミックエージェントとCGLIBクラスレベルエージェントの違いを詳しく見てみましょう.
二、JDKに基づく動的代理店:
JAvaコード:
この構成は、デフォルトではJDKダイナミックエージェントです.
セルテストを実行します.コア・ログは次のとおりです.
JAvaコード:
2012-03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Creating new transaction with name [cn.javass.common.service.impl.BaseService.countAll]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' //
2012-03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Opened new Session
2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] to thread [main] // session ThreadLocal
2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor - Getting transaction for [cn.javass.common.service.impl.BaseService.countAll]
2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] bound to thread [main]
2012-03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Found thread-bound Session
2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] bound to thread [main]
Hibernate:
select
count(*) as col_0_0_
from
tbl_user usermodel0_
2012-03-07 09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Committing Hibernate transaction on Session //
2012-03-07 09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Removed value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] from thread [main] // session ThreadLocal
これでトランザクションが機能していることがわかります.つまり、@Transactionalをインタフェースに配置してJDKベースのダイナミックエージェントでも動作します.
三、CGLIB類エージェントに基づく:
JAvaコード:
この構成はCGLIBクラスエージェントに基づく
テストを開始するとエラーが発生します.No Session found for current threadは、トランザクションが機能していないことを示します.
JAvaコード:
org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1024)
at cn.javass.common.dao.hibernate4.BaseHibernateDao.getSession(BaseHibernateDao.java:63)
at cn.javass.common.dao.hibernate4.BaseHibernateDao.aggregate(BaseHibernateDao.java:238)
at cn.javass.common.dao.hibernate4.BaseHibernateDao.countAll(BaseHibernateDao.java:114)
at cn.javass.common.service.impl.BaseService.countAll(BaseService.java:60)
at cn.javass.common.service.impl.BaseService$$FastClassByCGLIB$$5b04dd69.invoke()
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:618)
at cn.javass.demo.service.impl.UserServiceImpl$$EnhancerByCGLIB$$7d46c567.countAll()
at cn.javass.ssonline.spider.service.impl.UserServiceTest2.testCreate(UserServiceTest2.java:20)
注釈を特定のクラスまたは特定のクラスの実装方法に置くと機能します.
JAvaコード:
package cn.javass.common.service.impl;
public abstract class BaseService implements IBaseService {
@Transactional() //
@Override
public int countAll() {
return baseDao.countAll();
}
}
テストクラスを実行すると、Userviceがメソッドを継承しているため、正常に検出されますが、Userviceがメソッドを上書きすると、次のようにトランザクションを織り込むことができません(エラー).
JAvaコード:
package cn.javass.demo.service.impl;
public class UserServiceImpl extends BaseService implements UserService {
// @Transactional
@Override
public int countAll() {
return baseDao.countAll();
}
}
四、aspectjに基づく
JAvaコード:
ここでは実証しないが,主にJDK動的エージェントとCGLIBクラスエージェントの2つの違いに基づいて解析する.
五、結論:
JDKダイナミックエージェントに基づいて、@Transactionalをインタフェースと特定のクラスに配置できます.
CGLIBクラスエージェントに基づいて、@Transactionalのみを特定のクラスに配置できます.
したがって,@Transactionalはインタフェースではなく,実際の開発時にすべて特定のクラスに配置される.
六、分析
1、 JDKダイナミックエージェント
1.1、SpringはJdkDynamicAopProxyを使用して代理を実現する:
JAvaコード:
package org.springframework.aop.framework;
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
// method method( @Transactional )
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
}
}
ここでのmethodは必ずインタフェース上のmethodであることに注意してください(したがってインタフェース上に置かれている@Transactionalは発見可能です)
1.2、もし
JAvaコード:
package org.springframework.aop.framework;
final class Cglib2AopProxy implements AopProxy, Serializable {
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
// method method( @Transactional )
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
}
}
}
1.3、Spring AnnotationTransactionAttributeSourceを使用して、クラスまたはメソッドに@Transactional注釈トランザクションがあるかどうかを検索してTransactionAttribute(オープントランザクションを示す)を返します.
JAvaコード:
package org.springframework.transaction.annotation;
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
if (attr != null) {
return attr;
}
}
return null;
}
}
一方、A n t o t i o n T r a n s c t ionAttributeSourceでは、@Transactional注記があるかどうかを解析するためにSpringTransactionAnnotationParserが使用されます.
JAvaコード:
package org.springframework.transaction.annotation;
public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
Transactional ann = AnnotationUtils.getAnnotation(ae, Transactional.class);
if (ann != null) {
return parseTransactionAnnotation(ann);
}
else {
return null;
}
}
public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
}
}
ここではAnnotationUtils.getAnnotation(ae,Transactional.class)を使用します.このメソッドでは、現在のメソッド/クラスの注釈のみが検出され、親の注釈は検出されません.Springでは、親/親インタフェースの注記を検出するAnnotationUtils.findAnnotation()メソッドも用意されています(ただしspringでは使用されていません).
SpringでAnnotationUtils.findAnnotation()に変更すると、親/親インタフェースの注記が表示されます.
ここでは、次のように説明します.
インタフェースで@Transactionalを削除 //デフォルトのトランザクションを開く
JAvaコード:
package cn.javass.common.service;
public interface IBaseService {
public int countAll();
}
クラスに@Transactionalを追加
JAvaコード:
package cn.javass.common.service.impl;
public abstract class BaseService implements IBaseService {
@Transactional() //
@Override
public int countAll() {
return baseDao.countAll();
}
}
質問:
JDKダイナミックエージェントに基づいている場合、methodはインタフェース上のmethodに違いない(したがってインタフェース上に置かれている@Transactionalは発見できる)と述べたが、今は具体的なクラスに置かれているが、Springはどのように発見されているのだろうか.
TransactionAttributeがAnnotationTransactionAttributeSourceを通過したことを覚えていますか?具体的には、手順1.3を参照してください.
A n t o n t i o n T r a n s c t ionAttributeSourceはAbstractFallbackTransactionAttributeSourceを継承します
JAvaコード:
package org.springframework.transaction.interceptor;
public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {
public TransactionAttribute getTransactionAttribute(Method method, Class> targetClass) {
// computeTransactionAttribute
}
// TransactionAttribute
private TransactionAttribute computeTransactionAttribute(Method method, Class> targetClass) {
//
// Ignore CGLIB subclasses - introspect the actual user class.
Class> userClass = ClassUtils.getUserClass(targetClass);
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
//①
Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
// If we are dealing with method with generic parameters, find the original method.
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
// First try is the method in the target class.
TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
if (txAtt != null) {
return txAtt;
}
//
// Second try is the transaction attribute on the target class.
txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAtt != null) {
return txAtt;
}
//②
if (specificMethod != method) {
// Fallback is to look at the original method.
txAtt = findTransactionAttribute(method);
if (txAtt != null) {
return txAtt;
}
// Last fallback is the class of the original method.
return findTransactionAttribute(method.getDeclaringClass());
}
return null;
}
}
//①ここではサブクラスオーバーライドの方法を探します
Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
//ClassUtils.getMostSpecificMethod
public static Method getMostSpecificMethod(Method method, Class> targetClass) {
Method specificMethod = null;
if (method != null && isOverridable(method, targetClass) &&
targetClass != null && !targetClass.equals(method.getDeclaringClass())) {
try {
specificMethod = ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());
} catch (AccessControlException ex) {
//security settings are disallowing reflective access; leave
//'specificMethod' null and fall back to 'method' below
}
}
return (specificMethod != null ? specificMethod : method);
}
現在のクラスを見つける方法がわかります.そのため、BaseService countAllメソッドに配置されている@Transactionalが機能します.
//②サブクラスの上書き方法が現在のパスを直接探していない場合
if (specificMethod != method) {
//Fallback is to look at the original method.
txAtt = findTransactionAttribute(method);
if (txAtt != null) {
return txAtt;
}
//Last fallback is the class of the original method.
return findTransactionAttribute(method.getDeclaringClass());
}
サブクラスの検索に失敗した場合は、転送されたメソッドを直接使用します.
したがって、Schemaベースのトランザクションを使用することをお勧めします(多くの問題を考慮する必要はありません.クラスかメソッドかを考慮する必要はありません).@Transactionalは、インタフェースに配置しないで、特定のクラスに配置することをお勧めします.
作者オリジナルhttp://sishuok.com/forum/blogPost/list/0/3845.html#9317】