`
猫不吃的鱼
  • 浏览: 159055 次
  • 性别: Icon_minigender_1
  • 来自: 芜湖市
社区版块
存档分类
最新评论

(转)JDK代理 CGLIB代理的实现Spring注解管理事务区别。

阅读更多
转自 http://blog.csdn.net/zhujyy110/article/details/8639119

JDK代理 CGLIB代理的实现Spring注解管理事务区别。
.


javaspring jdk动态代理CGLIB代理动态代理


一、基础工作
首先修改我们上一次做的 SpringMVC + spring3.1.1 + hibernate4.1.0 http://www.2cto.com/kf/201203/122443.html集成及常见问题总结,如下所示:
     将xml声明式事务删除





java代码:
<aop:config expose-proxy="true">
         <!-- 只对业务逻辑层实施事务 -->
         <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service..*.*(..))" />
         <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
     </aop:config>



    并添加注解式事务支持:





java代码:
<tx:annotation-driven transaction-manager="txManager"/>
 

    在我们的BaseService接口上添加 @Transactional 使该方法开启事务





java代码:
package cn.javass.common.service;
public interface IBaseService<M extends java.io.Serializable, PK extends java.io.Serializable> {
@Transactional   //开启默认事务
     public int countAll();
}



在我们的log4j.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代码:
<tx:annotation-driven transaction-manager="txManager"/>
    该配置方式默认就是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代码:
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
    该配置方式是基于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(<generated>)
     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(<generated>)
     at cn.javass.ssonline.spider.service.impl.UserServiceTest2.testCreate(UserServiceTest2.java:20)





如果将注解放在具体类上或具体类的实现方法上才会起作用。



java代码:
package cn.javass.common.service.impl;
public abstract class BaseService<M extends java.io.Serializable, PK extends java.io.Serializable> implements IBaseService<M, PK> {
 
    @Transactional()   //放在抽象类上
     @Override
     public int countAll() {
         return baseDao.countAll();
     }
}



运行测试类,将发现成功了,因为我们的UserService继承该方法,但如果UserService覆盖该方法,如下所示,也将无法织入事务(报错):



java代码:
package cn.javass.demo.service.impl;
public class UserServiceImpl extends BaseService<UserModel, Integer> implements UserService {
     //没有@Transactional
     @Override
     public int countAll() {
         return baseDao.countAll();
     }
}






四、基于aspectj的



java代码:
<tx:annotation-driven transaction-manager="txManager" mode="aspectj" proxy-target-class="true"/>
在此就不演示了,我们主要分析基于JDK动态代理和CGLIB类代理两种的区别。



五、结论:
基于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、如果<tx:annotation-driven 中 proxy-target-class="true" ,Spring将使用CGLIB动态代理,而内部通过Cglib2AopProxy实现代理,而内部通过DynamicAdvisedInterceptor进行拦截:



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;
  }
}

而AnnotationTransactionAttributeSource又使用SpringTransactionAnnotationParser来解析是否有@Transactional注解:





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<M extends java.io.Serializable, PK extends java.io.Serializable> {
     public int countAll();
}



在具体类中添加@Transactional





java代码:
package cn.javass.common.service.impl;
public abstract class BaseService<M extends java.io.Serializable, PK extends java.io.Serializable> implements IBaseService<M, PK> {

    @Transactional()   //开启默认事务
     @Override
     public int countAll() {
         return baseDao.countAll();
     }
}


问题:

    我们之前说过,基于JDK动态代理时, method 一定是接口上的method(因此放置在接口上的@Transactional是可以发现的),但现在我们放在具体类上,那么Spring是如何发现的呢??

    还记得发现TransactionAttribute是通过AnnotationTransactionAttributeSource吗?具体看步骤1.3:



而AnnotationTransactionAttributeSource 继承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建议放置到具体类上,不要放置到接口。
分享到:
评论

相关推荐

    AOP-CGLIB学习-实现简单的注解权限系统

    权限检查的逻辑通常会通过Spring的AOP代理实现,使用CGLIB动态创建的代理对象会在调用真实方法之前执行拦截器逻辑。拦截器可以根据注解的内容进行权限验证,如果验证失败,可以抛出异常或者返回错误信息,阻止方法的...

    AOP之JDK动态代理和CGLib动态代理

    Spring框架是AOP实现的一个典范,它提供了两种主要的动态代理方式:JDK动态代理和CGLib动态代理。 **JDK动态代理**: JDK动态代理基于Java的反射API实现,适用于接口代理。当目标对象实现了至少一个接口时,Spring...

    JDK动态代理 spring aop 的原理

    Spring AOP(面向切面编程)则是一种流行的应用框架,它利用动态代理来实现对业务代码的切面增强,如日志、事务管理等。现在让我们深入探讨JDK动态代理和Spring AOP的原理。 首先,JDK动态代理基于Java的反射机制,...

    Spring-AOP-JDK动态代理

    以下是使用JDK动态代理实现Spring AOP的步骤: 1. **定义切面(Aspect)**:切面是AOP的核心,它封装了横切关注点。在Spring中,可以通过实现`org.aspectj.lang.annotation.Aspect`接口或者使用注解`@Aspect`来定义...

    spring中注解的实现原理

    代理可以是JDK动态代理或CGLIB代理,取决于目标bean是否实现了接口。 事务管理中的`@Transactional`注解则通过TransactionAttributeSource和TransactionInterceptor协同工作。当事务方法被调用时,拦截器会检查方法...

    spring基于注解实现事务

    - 如果在AOP代理模式下使用(如JDK动态代理或CGLIB代理),注解应该应用在接口方法或实现类的方法上,而不是两者的混合使用。 - 事务管理需要在Spring配置中启用,例如在xml配置中添加 `...

    动态代理和注解Spring.rar

    Spring支持两种类型的代理:JDK动态代理和CGLIB代理。JDK动态代理基于接口实现,适用于目标对象实现了至少一个接口的情况;而CGLIB代理则是在运行时动态生成子类来实现,适用于目标对象没有接口或者接口不完全满足...

    JavaEE spring半自动实现AOP代理

    在Spring中,AOP代理有两种实现方式:JDK动态代理和CGLIB代理。JDK代理适用于实现了接口的类,而CGLIB代理则适用于未实现接口的类。 1. **JDK动态代理**: - Spring通过实现`java.lang.reflect.InvocationHandler`...

    Java 动态代理详解(代理模式+静态代理+JDK动态代理+CGLIB动态代理)

    Java 动态代理是 Java 编程语言中的一种强大工具,广泛应用于 Spring AOP、Hibernate 数据查询、测试框架的后端 mock、RPC 远程调用、Java 注解对象获取、日志、用户鉴权、全局性异常处理、性能监控等领域。...

    Spring事务管理失效原因汇总

    代理模式是Spring事务管理的基础,分为JDK动态代理和CGLIB代理。JDK动态代理通过接口实现,而CGLIB代理则通过继承的方式。在Spring中,默认情况下如果目标对象实现了接口,则使用JDK动态代理;如果目标对象没有实现...

    代理模式,JDK动态代理,SpringAOP来龙去脉

    - Spring支持两种类型的代理:JDK动态代理(如果目标对象实现了接口)和CGLIB代理(如果目标对象没有接口,使用字节码生成技术)。 - 在Spring配置中,可以使用`@Aspect`注解定义切面,`@Before`、`@After`、`@...

    spring基于AOP实现事务

    在Spring中,AOP主要是通过代理模式实现的,有JDK动态代理和CGLIB两种方式。 对于Spring事务管理,我们通常有两种方式:编程式和声明式。编程式事务管理需要在代码中显式地调用begin、commit、rollback等事务控制...

    从JDK动态代理看Spring之AOP实现

    总的来说,Spring通过JDK动态代理实现AOP,提供了一种灵活、可扩展的方式来处理横切关注点,使得代码更加模块化,提高了可维护性。理解这一机制对于深入学习和使用Spring框架至关重要。在实际项目中,开发者可以根据...

    spring之AOP(动态代理)

    在Spring中,AOP主要通过两种动态代理技术实现:JDK动态代理和CGLIB动态代理。 首先,让我们详细了解一下JDK动态代理。JDK动态代理基于Java的接口实现,它适用于目标对象实现了至少一个接口的情况。在运行时,JDK...

    Spring框架+Spring工作原理+AOP面向切面编程+JDK代理+Cglib代理

    2. **统一的事务管理**:Spring的AOP模块提供了声明式的事务管理,无需显式编写事务代码。 3. **灵活的配置方式**:支持XML和Java配置方式,开发者可以根据项目需求选择最合适的配置方式。 4. **强大的社区支持**:...

    Spring事务与Java事务比较

    Spring AOP 支持基于接口的代理(JDK 动态代理)和基于类的代理(CGLIB),可以透明地对目标对象进行增强。 总结来说,Spring 框架的事务管理与 Java 原生的事务管理相比,具有更高的抽象层次和更好的可配置性,...

    spring+动态代理

    在Java编程中,动态代理是一种强大的工具,它允许我们在运行时创建对象的代理,以便在调用实际方法之前或之后执行...在实际项目中,无论是简单地记录日志还是实现复杂的事务管理,Spring动态代理都是不可或缺的工具。

    四、Spring源码分析——动态代理

    例如,可以查看`org.springframework.aop.framework.JdkDynamicAopProxy`和`org.springframework.aop.framework.CglibAopProxy`这两个类,它们分别实现了JDK和CGLIB代理。`InvocationHandler`接口的实现类`...

    JavaEE spring自动实现AOP代理

    Spring 提供了两种方式来实现AOP代理:JDK动态代理和CGLIB代理。 1. **JDK动态代理**: - JDK动态代理基于Java的接口实现,适用于目标对象实现了接口的情况。Spring会为这个接口创建一个代理类,代理类在调用真实...

    spring-demo16-JDK动态代理.zip

    在Java开发中,JDK动态代理是一个非常重要的概念,它允许我们在运行时创建对已有接口的代理实现。Spring框架在很多场景下也利用了这一特性,例如AOP(面向切面编程)就是其中之一。"spring-demo16-JDK动态代理.zip...

Global site tag (gtag.js) - Google Analytics