JDK动态代理干了什么事情?为什么我的事务会不起效?spring的事务到底应该怎么使用?原理是什么?

JDK动态代理剖析:

//接口

public interface HelloInterface extends Serializable {

public String hello(String name);

public String bye(String name);

}

//实现类

public class HelloInterfaceImpl implements HelloInterface {

@Override

public String hello(String name) {

return "你好" + name;

}

@Override

public String bye(String name) {

hello(name);

return "Bye " + name;

}

}

//方法拦截

public class HelloInterceptor implements InvocationHandler {

private HelloInterface helloInterface;

public HelloInterceptor(HelloInterface helloInterface) {

this.helloInterface = helloInterface;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("拦截到方法:" + method.getName() + ",参数:" + args[0]);

return method.invoke(helloInterface, args);

}

}

//产生代理并测试

HelloInterface helloInterface = new HelloInterfaceImpl();

HelloInterceptor helloInterceptor = new HelloInterceptor(helloInterface);

HelloInterface helloInterfaceProxy = (HelloInterface) Proxy.newProxyInstance(

helloInterface.getClass().getClassLoader(),

helloInterface.getClass().getInterfaces(),

helloInterceptor);

helloInterfaceProxy.bye("张三");

输出结果:

拦截到方法:bye,参数:张三

注意:调用了方法bye,bye又调用了hello,但只拦截到了bye方法,没有拦截到bye又调用的hello方法

解释这个原因前先看看这个产生的代理对象:

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.lang.reflect.UndeclaredThrowableException;

import prox.HelloInterface;

public final class $Proxy0 extends Proxy

implements HelloInterface

{

private static Method m3;

private static Method m1;

private static Method m4;

private static Method m0;

private static Method m2;

public $Proxy0(InvocationHandler paramInvocationHandler)

throws

{

super(paramInvocationHandler);

}

public final String bye(String paramString)

throws

{

try

{

return (String)this.h.invoke(this, m3, new Object[] { paramString });

}

catch (RuntimeException localRuntimeException)

{

throw localRuntimeException;

}

catch (Throwable localThrowable)

{

}

throw new UndeclaredThrowableException(localThrowable);

}

public final boolean equals(Object paramObject)

throws

{

try

{

return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();

}

catch (RuntimeException localRuntimeException)

{

throw localRuntimeException;

}

catch (Throwable localThrowable)

{

}

throw new UndeclaredThrowableException(localThrowable);

}

public final String hello(String paramString)

throws

{

try

{

return (String)this.h.invoke(this, m4, new Object[] { paramString });

}

catch (RuntimeException localRuntimeException)

{

throw localRuntimeException;

}

catch (Throwable localThrowable)

{

}

throw new UndeclaredThrowableException(localThrowable);

}

public final int hashCode()

throws

{

try

{

return ((Integer)this.h.invoke(this, m0, null)).intValue();

}

catch (RuntimeException localRuntimeException)

{

throw localRuntimeException;

}

catch (Throwable localThrowable)

{

}

throw new UndeclaredThrowableException(localThrowable);

}

public final String toString()

throws

{

try

{

return (String)this.h.invoke(this, m2, null);

}

catch (RuntimeException localRuntimeException)

{

throw localRuntimeException;

}

catch (Throwable localThrowable)

{

}

throw new UndeclaredThrowableException(localThrowable);

}

static

{

try

{

m3 = Class.forName("prox.HelloInterface").getMethod("bye", new Class[] { Class.forName("java.lang.String") });

m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });

m4 = Class.forName("prox.HelloInterface").getMethod("hello", new Class[] { Class.forName("java.lang.String") });

m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);

return;

}

catch (NoSuchMethodException localNoSuchMethodException)

{

throw new NoSuchMethodError(localNoSuchMethodException.getMessage());

}

catch (ClassNotFoundException localClassNotFoundException)

{

}

throw new NoClassDefFoundError(localClassNotFoundException.getMessage());

}

}

该类是代理对象反编译后的源码,该类是JDK动态产生的,可以看到该类实现了HelloInterface,并通过如下代码得到了HelloInterface所有方法的反射

m3 = Class.forName("prox.HelloInterface").getMethod("bye", new Class[] { Class.forName("java.lang.String") });

m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });

m4 = Class.forName("prox.HelloInterface").getMethod("hello", new Class[] { Class.forName("java.lang.String") });

m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);

再看bye方法:

public final String bye(String paramString)

throws

{

try

{

return (String)this.h.invoke(this, m3, new Object[] { paramString });

}

catch (RuntimeException localRuntimeException)

{

throw localRuntimeException;

}

catch (Throwable localThrowable)

{

}

throw new UndeclaredThrowableException(localThrowable);

}

调用改方法实际上是调用HelloInterceptor(即:this.h,这个是在父类Proxy中通过protected InvocationHandler h;定义的)的invoke方法,

参数this即是代理对象本身,m3是bye方法的反射,new Object[] { paramString }为传入的参数,因此当调用helloInterface.bye("张三");时,

其实是执行HelloInterceptor的invoke方法,而在该方法:

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("拦截到方法:" + method.getName() + ",参数:" + args[0]);

return method.invoke(helloInterface, args);

}

先输出,后用反射直接调用刚刚创建的实例 HelloInterface helloInterface = new HelloInterfaceImpl();的bye方法,很明显该实例是原始类,未做任何改变,

只不过调用的方式由显示调用变成了反射间接调用,不再与HelloInterceptor有关,其执行的不过是其内部的代码,所以bye方法再去调用hello方法并没有被拦截到,

同理,如果还有其它类似的方法被bye调用也不会被拦截到。

现在来回答事务的问题就简单了

spring中,JDK代理是由这个类产生的

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable

它实现了InvocationHandler接口,当调用代理对象时会调用JdkDynamicAopProxy 的invoke方法,如果配置了事务,invoke方法会调用TransactionInterceptor来实现真正的事务处理,

现在就很明白了,如果我们有一个类BusinessService,它有两个方法methodA,methodB,如果methodA没有设置事务,methodB设置了事务,那么methodA调用了methodB,

methodB的事务也不会起效,因为虽然BusinessService被代理了,但当调用BusinessService的代理对象的methodA方法时会调用JdkDynamicAopProxy的invoke方法,

JdkDynamicAopProxy 发现mthodA没有事务就会略过TransactionInterceptor,进而调用原始的BusinessService的methodA方法,原始的BusinessService与JdkDynamicAopProxy 无关,

也就不会调用TransactionInterceptor了,所以不会起效;反之,如果先调用BusinessService的代理对象的 methodB,接着调用JdkDynamicAopProxy的invoke方法,

JdkDynamicAopProxy 发现mthodB有事务就会调用TransactionInterceptor进行事务处理,完毕后调用原始的BusinessService的methodB方法,如果methodB再掉其它方法,

这些方法都会有事务(因为事务对象(说白了就是jdbc的connnection)是存放在ThreadLocal中的,后续的方法如果设置了事务,调用时会判断事务是否在当前线程中存在,如果存在则直接使用,否则新开一个线程并存入TheadLocal中)

所以要保证事务有效可以有以下方法:

1.保证入口方法设置了事务

2.将事务方法封装到其他类中,在要使用的地方注入即可(这样保证了事务方法和调用方法属于不同的类,不同的代理)

3.要保证所有操作在一个事务中,请一定要在同一线程中执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值