前言
非事务方法的调用对象是代理对象,但是spring 事务机制只查看带有@Transcational注解的方法,所有非事务方法不会以事务方法执行;事务方法的调用对象是原对象,是不会触发通知(事务)的。
Spring事务自调用失效
@Transcational在某些场景下会失效,在实际生产中,这是个要注意的问题。一个类方法A调用该类的方法B,当用代理类执行方法A时,方法A是由代理对象反射完成的,而方法B并不是反射执行的。下面是一个Spring 事务的例子,
public class TestService {
public int insertUsers(List<User> userList) {
System.out.println("insertUsers ...");
int count = 0;
for (User user: userList) {
count += insertUser(user);
}
return count;
}
@Transactional(isolation = Isolation.READ_COMMITED,
propagation = Propogation.REQUIRES_NEW)
public int insertUser(User user) {
System.out.println("insertUser ...");
return 1;
}
}
非事务方法insertUsers中调用的自身类的事务方法insertUser方法,
- 如果执行TestService.insertUser方法,insertUser方法体会走执行spring 事务。
- 如果执行TestService.insertUsers方法,insertUser方法体不会执行spring事务。
原因是,spring会扫描所有添加了@Transcational注解的方法,添加了@Transcational的类的对象都会生成一个代理对象,spring事务会判断代理类执行的方法是否是添加了@Transcational注解的,而非事务方法调用事务方法属于自调用,自调用方法并不是由代理通过反射来执行的,这里来看Spring aop的原理了。
Spring AOP原理
spring aop主要是通过代理类和反射来实现的,主要两个方式:JDK代理和cglib代理。区别在于代理类是否实现了接口,实现了接口一般用JDK实现,没有实现接口一般用cglib实现,这里主要介绍JDK代理原理。
(1)首先定义一个接口类HelloService
public interface HelloService {
void sayHello(String name);
void say(String word);
}
(2)定义一个接口实现类HelloServiceImp,也就是我们要代理的类
public class HelloServiceImp implements HelloService {
@Override
public void sayHello(String name) {
System.out.println("hello " + name);
}
@Override
public void say(String word) {
System.out.println(word);
}
}
(3)定义Interceptor接口
按照spring aop的切面来定义,分为五种
- before:前置通知
- after:后置通知
- around:环绕通知
- afterReturn:正常返回通知
- afterThrow:异常返回通知
public interface Interceptor {
boolean before();
void after();
Object around(Invocation invocation)
throws InvocationTargetException, IllegalAccessException;
void afterReturning();
void afterThrowing();
}
(4)定义Interceptor实现类
Invocation类是反射执行方法,定义见(5)Invocation
public class MyInterceptor implements Interceptor {
@Override
public boolean before() {
System.out.println("before ...");
return true;
}
@Override
public void after() {
System.out.println("after ...");
}
/**
*
* @param invocation 反射执行方法,定义见下Invocation类
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
@Override
public Object around(Invocation invocation)
throws InvocationTargetException, IllegalAccessException {
System.out.println("around before ...");
Object obj = invocation.proceed();//反射执行方法
System.out.println("around after ...");
return obj;
}
@Override
public void afterReturning() {
System.out.println("afterReturning ...");
}
@Override
public void afterThrowing() {
System.out.println("afterThrowing ...");
}
}
(5)定义Invocation,用于反射执行方法
public class Invocation {
private Object[] params;//代理方法参数
private Method method;//代理方法
private Object target;//代理对象
public Invocation(Object target, Method method, Object[] params) {
this.target = target;
this.method = method;
this.params = params;
}
public Object proceed()
throws InvocationTargetException, IllegalAccessException {
//反射执行方法
return method.invoke(target, params);
}
}
(4)代理类ProxyBean
代理类的实现主要两个jdk类:Proxy类和InvacationHandler接口
先通过InvocationHandler接口的invoke方法来定义(反射方法执行和各种aop通知)
再通过Proxy.newProxyInstance方法传入InvocationHandler对象获取代理对象。这样代理对象就会执行被代理对象的方法,同时还能在代理时发出aop(5种)通知。
public class ProxyBean implements InvocationHandler {
private Object target = null;
private Interceptor interceptor = null;
public static Object getProxyBean(Object target, Interceptor interceptor) {
ProxyBean proxyBean = new ProxyBean();
proxyBean.target = target;
proxyBean.interceptor = interceptor;
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), proxyBean);
return proxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("通过代理对象执行方法:" + method.getName());
boolean exceptionFlag = false;
Invocation invocation = new Invocation(target, method, args);
Object retObj = null;
try {
if (this.interceptor.before()) {
retObj = this.interceptor.around(invocation);
} else {
retObj = method.invoke(target, args);
}
} catch (Exception e) {
exceptionFlag = true;
}
this.interceptor.after();
if (exceptionFlag) {
this.interceptor.afterThrowing();
} else {
this.interceptor.afterReturning();
return retObj;//正常返回
}
return null;//异常返回
}
}
(4)测试
public class Test {
public static void main(String[] args) {
HelloService helloService = new HelloServiceImp();
HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService,
new MyInterceptor());
proxy.sayHello("Hinson");
}
}
结果:
通过代理对象执行方法:sayHello
before ...
around before ...
hello Hinson
around after ...
after ...
afterReturning ...
这就是spring aop的原理,如果在HelloServiceImp中的sayHello方法调用say方法,那么say方法是由代理对象来执行的,还是由原对象执行的呢?
HelloServiceImp修改如下
public class HelloServiceImp implements HelloService {
@Override
public void sayHello(String name) {
// System.out.println("hello " + name);
say("hello " + name);
}
@Override
public void say(String word) {
System.out.println(word);
}
}
执行结果:
通过代理对象执行方法:sayHello
before ...
around before ...
hello Hinson
around after ...
after ...
afterReturning ...
自调用方法,调用类是原对象,而aop中只有调用类是代理对象时,才能出发各种通知,spring 事务就是基于通知来实现的。所以spring非事务方法调用事务方法过程中,非事务方法的调用对象是代理对象,但是spring 事务机制只查看带有@Transcational注解的方法,所有非事务方法不会以事务方法执行;事务方法的调用对象是原对象,是不会触发通知(事务)的。
其他
本人也是在慢慢学习中,如有错误还请原谅、敬请指出,谢谢!