Spring事务实现原理

您所在的位置:网站首页 aop代理类 Spring事务实现原理

Spring事务实现原理

2024-03-17 12:21| 来源: 网络整理| 查看: 265

Spring事务实现原理—Aop以及动态代理

Spring事务管理机制的实现原理是由Spring内置的AOP——默认使用动态代理模式实现的。

AOP

Spring的两大核心之一就是AOP,AOP:面向切面编程。在说原理之前,得先知道一些 AOP的专业术语。 AOP的专业术语 连接点(JoinPoint):增强执行的位置(增加代码的位置),Spring只支持方法; 切点(PointCut):具体的连接点;一般可能通过一个表达式来描述; 增强(Advice):也称为消息,指的是增加的额外的代码,Spring中,增强除了包含代码外,还包含位置信息;

Spring中一共有四种增强:

MethodBeforeAdvice:前置增强。 MethodInterceptor:环绕增强。 ThrowsAdvice:异常增强。 AfterReturingAdvice:返回值增强。

引介(Introduction):特殊的增强,动态为类增加方法。 织入(Weaving):将增强加入到目标类的过程,织入分为三种时期。

编译器:AspectJ。 类加载在运行期:jdk动态代理(实现接口),CGlib(子类,不能用final)。

目标对象(Target):原始对象; 代理对象(Proxy):加入了增强的对象,是生成的; 切面(Aspect):切点+增强。

代理模式

代理模式有三个角色,分别是 抽象角色:接口; 目标角色:实现类; 代理角色:实现接口(InvocationHandler),并引用目标角色;

代理模式分为两种: 静态代理:需要为每个目标角色,创建一个对应的代理角色;类的数量会急剧膨胀; 动态代理:自动为每个目标角色生成对应的代理角色。

AOP通过两种动态代理实现的——cglib和jdk动态代理,简单说下两者区别:JDK动态代理只能对实现了接口的类生成代理,而不能针对类 CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)。 Spring在选择用JDK还是CGLiB的依据: (1)当Bean实现接口时,Spring就会用JDK的动态代理; (2)当Bean没有实现接口时,Spring使用CGlib是实现; (3)可以强制使用CGlib(在spring配置中加入)。 CGlib与JDK速度比如何? (1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。 (2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。

静态代理的实现: 接口代码:

public interface IHello { /** * 业务方法 * @param str */ void sayHello(String str); }

目标类代码:

public class Hello implements IHello{ @Override public void sayHello(String str) { System.out.println("hello"+str); } }

代理类代码,在方法开始前后执行特定的方法:

public class ProxyHello implements IHello{ private Hello hello; public ProxyHello(IHello hello) { super(); this.hello = hello; } @Override public void sayHello(String str) { System.out.println("start"+str);// 添加特定的方法 hello.sayHello(str); System.out.println("end"+str); } }

测试代码:

public class Test { public static void main(String[] args) { IHello hello = new ProxyHello(new Hello());能,则使用代理类 hello.sayHello("明天"); } }

这样会存在一个问题:如果我们像Hello这样的类很多,那么,我们是不是要去写很多个HelloProxy这样的类呢。其实也是一种很麻烦的事。在jdk1.3以后,jdk跟我们提供了一个API java.lang.reflect.InvocationHandler的类, 这个类可以让我们在JVM调用某个类的方法时动态的为些方法做些什么事。下面我们就来实现动态代理的实现。 动态代理实现主要是实现InvocationHandler,并且将目标对象注入到代理对象中,利用反射机制来执行目标对象的方法。 接口实现与静态代理相同,代理类代码:

public class DynaProxyHello implements InvocationHandler{ private Object target;//目标对象 /** * 通过反射来实例化目标对象 * @param object * @return */ public Object bind(Object object){ this.target = object; return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),this.target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("start"+str);//添加额外的方法 //通过反射机制来运行目标对象的方法 result = method.invoke(this.target,args); System.out.println("end"+str); return result; } }

测试类代码:

public class DynaTest { public static void main(String[] args) { IHello hello = (IHello) new DynaProxyHello().bind(new Hello()); hello.sayHello("明天"); } }

通过上面例子,可以发现通过动态代理和反射技术,已经基本实现了AOP的功能,如果我们只需要在方法执行前进行一些操作,则可以不实现打印end方法,这样就可以控制打印的时机了。如果我们想让指定的方法打印日志,我们只需要在invoke()方法中加一个对method名字的判断,method的名字可以写在xml文件中,这样我们就可以实现以配置文件进行解耦了,这样我们就实现了一个简单的spring aop框架。 当然我们以后写代码都是基于Aspect,直接写注解的,当然这并不代表这我们不需要知道AOP底层的实现原理,至于注解用的是哪种实现方式,使用配置来解决的,这里就不详解了。 讲完了动态代理,接下来通过代理模式看一些事务的机制:

public class TxHandler implements InvocationHandler { private Object originalObject; public Object bind(Object obj) {  this.originalObject = obj;  return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  Object result = null;  if (!method.getName().startsWith("save")) {   UserTransaction tx = null;   try {    tx = (UserTransaction) (new InitialContext().lookup("java/tx"));    result = method.invoke(originalObject, args);    tx.commit();   } catch (Exception ex) {    if (null != tx) {     try {      tx.rollback();     }catch (Exception e) {    }   }  } } else {  result = method.invoke(originalObject, args); } return result;} }

首先来看一下这段代码: return Proxy.newProxyInstance(  obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);    java.lang.reflect.Proxy.newProxyInstance 方法根据传入的接口类型 (obj.getClass.getInterfaces()))动态构造一个代理类实例返回,这也说明了为什么动态代理实现要求其所代理的对象一定要实现 一个接口。这个代理类实例在内存中是动态构造的,它实现了传入的接口列表中所包含的所有接口。 再看:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  ……  result = method.invoke(originalObject, args);  ……  return result; }

InvocationHandler.invoke方法将在被代理类的方法被调用之前触发。通过这个方法,我们可以在被代理类方法调用的前后进行一些处 理,如代码中所示,InvocationHandler.invoke方法的参数中传递了当前被调用的方法(Method),以及被调用方法的参数。同 时,可以通过method.invoke方法调用被代理类的原始方法实现。这样就可以在被代理类的方法调用前后写入任何想要进行的操作。

总结: Spring的事务管理机制实现的原理,就是通过这样一个动态代理对所有需要事务管理的Bean进行加载,并根据配置在invoke方法中对当前调用的 方法名进行判定,并在method.invoke方法前后为其加上合适的事务管理代码,这样就实现了Spring式的事务管理。Spring中的AOP实 现更为复杂和灵活,不过基本原理是一致的。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3