最近再项目中发现不少同事不理解默认情况下的Spring事务的AOP机制,导致随意使用事务注解.因此也在很多场景事务不生效。因此想从代理机制开始理一下整个Spring声明式的原理。
1.代理
通常,我们代码中处理核心的业务逻辑,还包含一些枝节性的代码和功能,比如日志记录、消息发送、安全、事务保证和监控等。
我们通常期望将这些枝节性的代码功能和我们的核心业务逻辑分离,减少业务功能和枝节功能的耦合。这时候我们就要使用代理来达到我们的目的。
代理的作用是:为其它对象提供一种代理以控制对这个对象的访问。简单理解就是中间的作用。代理一般涉及到三个角色:
(1).抽象角色:声明真实对象和代理对象的共同接口;
(2).代理角色:代理对象内部包含有真实角色的引用,从而可以操作真实角色,同时代理对象与真实对象有相同的接口,能在任何时候代替真实对象,同时代理对象可以在执行真实对 象前后加入特定的逻辑以实现功能的扩展;
(3).真实角色:代理角色所代表的真实对象,是我们最终要引用的对象;
1.1 代理模式
代理模式是一个经典的设计模式,介绍往上很多。
下面是一个最简单的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| public interface IHello {
void hello();
}
public class HelloImpl implements IHello {
@Override
public void hello() {
System.out.println("HelloImpl: Hello World.");
}
}
public class HelloProxyImpl implements IHello {
private IHello inner;
public HelloProxyImpl(IHello inner) {
this.inner = inner;
}
@Override
public void hello() {
doSomethingBefore();
inner.hello();
doSomethingAfter();
}
private void doSomethingBefore() {
System.out.println("HelloProxyImpl: Before hello()...");
}
private void doSomethingAfter() {
System.out.println("HelloProxyImpl: After hello()...");
}
} |
UML图大致如下:
从代码上我们可以看出,代理模式要求(这也是它的限制所在):
(1).代理类需要和被代理者一样实现相同的接口;
(2).代理类包含被代理者的引用;
1.2 Java动态代理
代理模式我们可以理解为一种静态代理,其问题是需要我们显示的为每个需要被代理的类实现一个代理类,即一个代理类只能为i一个被代理者做代理的功能。如果有上千个类需要代理,估计要骂娘了。
java动态代理正是为了解决这个问题。主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。我们只需要实现InvocationHandler接口,在实现invoke()方法的时候加上我们的代理逻辑。
一个简单的使用实例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
| /**
* @author xiaobaoqiu Date: 16-3-10 Time: 下午6:18
*/
public class JavaDynamicProxy implements InvocationHandler {
/**
* 被代理者
*/
private Object inner;
/**
* 生成代理类
*/
public static Object generateProxy(Object inner) {
return Proxy.newProxyInstance(
inner.getClass().getClassLoader(),
inner.getClass().getInterfaces(),
new JavaDynamicProxy(inner));
}
private JavaDynamicProxy(Object inner) {
this.inner = inner;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doSomethingBefore();
Object result = method.invoke(inner, args);
doSomethingAfter();
return result;
}
private void doSomethingBefore() {
System.out.println("JavaDynamicProxy: Before...");
}
private void doSomethingAfter() {
System.out.println("JavaDynamicProxy: After...");
}
}
//使用
public static void main(String[] args) {
//被动态代理的IHello实例对象A
IHello helloA = new HelloImpl();
//生成对象A的动态代理
IHello helloAProxy = (IHello) JavaDynamicProxy.generateProxy(helloA);
helloAProxy.hello();
System.out.println("-------------------------------------------------");
// 一个JavaDynamicProxy可以一直使用
//被动态代理的IHello实例对象B
IHello helloB = new HelloWithLogImpl();
//生成对象B的动态代理
IHello helloBProxy = (IHello) JavaDynamicProxy.generateProxy(helloB);
helloBProxy.hello();
System.out.println("-------------------------------------------------");
//被动态代理对象IBye实例
IBye bye = new ByeImpl();
//生成IBye实例的动态代理
IBye byeProxy = (IBye) JavaDynamicProxy.generateProxy(bye);
byeProxy.bye();
} |
JavaDynamicProxy.generateProxy的输出会是一个动态的代理类。debug信息如下,从debug信息我们大致知道,这个代理类的类名称为$Proxy0,内部包含一个属性名为h的属性,h指向的就是我们实现的InvocationHandler的类(这里即JavaDynamicProxy)的实例。
但是Java动态代理的限制是:
(1).被代理的类要求至少实现了一个Interface;
(2).被代理的类要求有public的构造函数(即没有显示的将其设置为private等);
(3).被代理的类要求不是final;
如下代理一个没有实现任何接口的类会报错
1
2
3
4
5
6
| //没有实现任何interface的类使不能被动态代理的
System.out.println("-------------------------------------------------");
HelloWithoutInterface helloC = new HelloWithoutInterface();
//生成对象B的动态代理
HelloWithoutInterface helloCProxy = (HelloWithoutInterface) JavaDynamicProxy.generateProxy(helloC);
helloCProxy.hello(); |
错误信息如下:
1
2
3
4
5
6
7
| Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy2 cannot be cast to proxy.javaProxy.HelloWithoutInterface
at proxy.javaProxy.JavaProxyMain.main(JavaProxyMain.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140) |
Java动态代理的机制是利用反射机制生成。具体代码可以debug,主要的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| //Proxy.java
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
...
Class<?> cl = getProxyClass0(loader, intfs); //生成代理类
...
return newInstance(cons, ih); // 生成代理类的实例
}
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
//生成代理类的主要逻辑在ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
//ProxyClassFactory的apply方法
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
...
String proxyPkg = null; // 生成代理类的包路径
...
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; //默认路径在com.sun.proxy下面
}
long num = nextUniqueNumber.getAndIncrement(); //代理类的序号, 我们熟悉的$Proxy0中的0
String proxyName = proxyPkg + proxyClassNamePrefix + num; //代理类的类名称
//使用ProxyGenerator生成代理类的字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
//生成代理类
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
}
//ProxyGenerator.generateProxyClass的代码太恶心的... |
我们可以自己使用ProxyGenerator来生成代理类并将其字节码记录下来:
1
2
3
| // 获取代理类的字节码
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", ByeImpl.class.getInterfaces());
//将字节码写文件,建议写成.class文件 |
获取的字节码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
| import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.sample.IBye;
public final class $Proxy0 extends Proxy implements IBye {
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void bye() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m3 = Class.forName("proxy.sample.IBye").getMethod("bye", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
} |
从生成的类我们知道,代理类继承Proxy类(这就是为什么Proxy类内的InvocationHandler实例未protected)并实现我们需要的被代理者的Interface(比如这里的bye()方法),另外它只要一个接受一个InvocationHandler为参数的构造函数。当我们调用代理类的bye()方法时候,其实是调用我们实现的InvocationHandler(即上面的JavaDynamicProxy)的invoke()方法,在invoke()方法里面,我们实现了我的代理逻辑。
我们这里这个Demo的大致UML图如下:
参考: http://www.cnblogs.com/cruze/p/3819761.html
1.3 CGLib动态代理
鉴于Java动态代理的限制,我们有需要代理没有任何实现接口的类的时候,可以考虑使用CGLib。CGLib的全称是Code Generate Library。CGLib的使用使用十分广泛,我们这里要讲的Spring AOP,已经EasyMock等。
同样先上Demo代码,我们只需要实现MethodInterceptor接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| /**
* @author xiaobaoqiu Date: 16-3-10 Time: 下午7:15
*/
public class CglibDynamicProxy implements MethodInterceptor {
/**
* 动态生成代理类
*/
public Object generateProxy(Class cls) {
Enhancer enhancer = new Enhancer();
enhancer.setCallback(this);
// enhancer.setCallbackFilter(); //filter
enhancer.setSuperclass(cls);
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
doSomethingBefore();
Object result = proxy.invokeSuper(obj, args);
doSomethingAfter();
return result;
}
private void doSomethingBefore() {
System.out.println("CglibDynamicProxy: Before...");
}
private void doSomethingAfter() {
System.out.println("CglibDynamicProxy: After...");
}
}
//使用
public static void main(String[] args) {
CglibDynamicProxy proxy = new CglibDynamicProxy();
//注意: 原始类的instance不需要存在,只需要Class类型
//用接口IHello接, 或者 HelloImpl 接
IHello helloAProxy = (IHello)proxy.generateProxy(HelloImpl.class);
helloAProxy.hello();
System.out.println("-------------------------------------------------");
//用接口IHello接, 或者 HelloWithLogImpl 接
IHello helloBProxy = (IHello)proxy.generateProxy(HelloWithLogImpl.class);
helloBProxy.hello();
System.out.println("-------------------------------------------------");
//代理没有实现任何interface的类
HelloWithoutInterface helloCProxy = (HelloWithoutInterface)proxy.generateProxy(HelloWithoutInterface.class);
helloCProxy.hello();
} |
同样我们debug一下,得到的信息如下,同样生成了代理类,类名称为proxy.sample.HelloImpl$$EnhancerByCGLIB$$b46b6f06,这个稀奇古怪的类名我们后面会分析:
下面还是尝试分析CGLib动态代理的原理。默认情况下生成的代理class文件只存储在内存中,我们可以在代码中设置一个环境变量:
1
| System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/xiaobaoqiu/cglib_proxy"); |
之后我们在目标目录下得到很多的class,其中的proxy目录包含了生成的class文件。我们发现一大堆的class文件,类名都是稀奇古怪。我们先看看class文件的生成策略。每个Class Generator(比如这里的Enhancer)都继承自AbstractClassGenerator(实现接口ClassGenerator,这个接口只有一个generateClass的方法),需要实现其generateClass()方法。generateClassName()方法用来生成Class名称:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| private String generateClassName(Predicate nameTestPredicate) {
return namingPolicy.getClassName(namePrefix, source.name, key, nameTestPredicate);
}
//namingPolicy的默认实现NamingPolicy
public String getClassName(String prefix, String source, Object key, Predicate names) {
//prefix为被代理类的路径,
String base =
prefix + "$$" + //prefix为被代理类的路径
source.substring(source.lastIndexOf('.') + 1) + //获取生成代理类的类,比如我们这里的Enhancer
getTag() + "$$" + //getTag()默认为ByCGLIB
Integer.toHexString(STRESS_HASH_CODE ? 0 : key.hashCode()); //hashcode
String attempt = base;
int index = 2;
while (names.evaluate(attempt)) //如果有重复,则再在后面加上下标,小标从2开始
attempt = base + "_" + index++;
return attempt;
} |
下面正是进入class文件的生成原理分析,还是从源代码入手,enhancer.create()最终进入AbstractClassGenerator.create()方法,我们发现最终return的Object是从内部变量obj得来,因此,我们看看ClassLoaderData的生成逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| protected Object create(Object key) {
ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
if (data == null) {
synchronized (AbstractClassGenerator.class) {
data = cache.get(loader);
if (data == null) {
Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
data = new ClassLoaderData(classLoader);
newCache.put(classLoader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this);
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
return nextInstance(obj);
}
public ClassLoaderData(ClassLoader classLoader) {
this.classLoader = new WeakReference<ClassLoader>(classLoader);
Function<AbstractClassGenerator, Object> load =
new Function<AbstractClassGenerator, Object>() {
public Object apply(AbstractClassGenerator gen) {
Class klass = gen.generate(ClassLoaderData.this); //生成class的关键代码
return gen.wrapCachedClass(klass);
}
};
generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load);
} |
因此我们将目光转到generate()中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| //Enhancer的generate()方法
protected Class generate(ClassLoaderData data) {
validate();
if (superclass != null) {
setNamePrefix(superclass.getName());
} else if (interfaces != null) {
setNamePrefix(interfaces[ReflectUtils.findPackageProtected(interfaces)].getName());
}
return super.generate(data); //父类AbstractClassGenerator
}
// AbstractClassGenerator的generate()方法
protected Class generate(ClassLoaderData data) {
//...
ClassLoader classLoader = data.getClassLoader();
this.setClassName(generateClassName(data.getUniqueNamePredicate())); //代理类的类名称的生成逻辑
//...
byte[] b = strategy.generate(this); //生成策略,默认实现DefaultGeneratorStrategy,生成class文件的字节码
String className = ClassNameReader.getClassName(new ClassReader(b));
ProtectionDomain protectionDomain = getProtectionDomain();
synchronized (classLoader) { // just in case
if (protectionDomain == null) {
gen = ReflectUtils.defineClass(className, b, classLoader);
} else {
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
}
}
return gen;
...//异常处理
}
//DefaultGeneratorStrategy的generate()方法
public byte[] generate(ClassGenerator cg) throws Exception {
DebuggingClassWriter cw = getClassVisitor();
transform(cg).generateClass(cw); //这里调用最终的generateClass()逻辑,Visitor模式
return transform(cw.toByteArray()); //通过Visitor得到最后的字节码
} |
所以最终的调用是Enhancer的generateClass()调用。
参考: http://www.cnblogs.com/cruze/p/3843996.html
2.Spring事务
目标: The most common reason for using transactions in an application is to maintain a high degree of data integrity and consistency.
代理实现选择器:
AdviceMode
TransactionManagementConfigurationSelector
声明事务切面实现:
TransactionInterceptor
事务管理器
PlatformTransactionManager
DataSourceTransactionManager
事务实现
Connection关闭自动提交
ConnectionHolder来保持Connection
事务模板
TransactionTempale
AOP简化事务模板
核心类: PlatformTransactionManager
隔离级别: Isolation
传播属性: Propagation
2.1 编程式事务
2.2 AOP实现
2.3 Aspectj实现
参考:
http://openwares.net/java/spring_mybatis_transaction.html
http://my.oschina.net/guanzhenxing/blog/214228
http://www.mybatis.org/spring/zh/transactions.html
http://tracylihui.github.io/2015/07/28/spring/Spring%E5%A3%B0%E6%98%8E%E5%BC%8F%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86/
http://www.cnblogs.com/davenkin/archive/2013/02/16/java-tranaction-1.html
http://www.importnew.com/12300.html
详细介绍Spring事务管理: http://developer.51cto.com/art/200906/129854.htm
spring事务原理1-事务的抽象: http://sqtds.github.io/2014/06/09/2014/spring-tx1/
Spring官方文档Transaction Management: http://docs.spring.io/autorepo/docs/spring/3.2.x/spring-framework-reference/html/transaction.html
Spring官方文档Aspect Oriented Programming with Spring: http://docs.spring.io/spring/docs/3.0.x/reference/aop.html
StackOverFlow Spring - @Transactional - What happens in background?: http://stackoverflow.com/questions/1099025/spring-transactional-what-happens-in-background
Transaction strategies: Understanding transaction pitfalls: http://www.ibm.com/developerworks/library/j-ts1/
Annotation-based Transactions in Spring: http://springinpractice.com/2008/03/18/annotation-based-transactions-in-spring
Design principle behind Transaction framework with Spring 3: http://stackoverflow.com/questions/11789857/design-principle-behind-transaction-framework-with-spring-3
Chapter 6. 使用Spring进行面向切面编程(AOP): http://shouce.jb51.net/spring/aop.html
wiki: http://wiki.corp.qunar.com/display/~yushen.ma/Spring+3.x-JDBC+Transaction
spring事务: http://wiki.corp.qunar.com/pages/viewpage.action?pageId=52084161
spring 事务: http://wiki.corp.qunar.com/pages/viewpage.action?pageId=93338632
aspectj事务: http://wiki.corp.qunar.com/pages/viewpage.action?pageId=92656954
Demo:
编程事务: http://www.tutorialspoint.com/spring/programmatic_management.htm
声明事务: http://www.tutorialspoint.com/spring/declarative_management.htm