Spring AOP中的动态代理
- 0 前言
- 1 动态代理
- 1.1 JDK动态代理
- 1.2 CGLIB动态代理
- 1.2.1 CGLIB的代理用法
- 1.2.2 CGLIB的过滤功能
- 2 Spring AOP中的动态代理机制
- 2.1 JdkDynamicAopProxy
- 2.2 CglibAopProxy
- 3 总结
0 前言
前一个季度旅游TDC的Thames服务有几次宕机,根据组内原因认真查找发现是数据库事务造成的,后来把服务中的事务配置全部去掉,服务恢复正常。根据这次教训,虽然现在还是很难确定是哪一个方面的真正原因,但是激发了我学习Spring事务方面的兴趣。而Spring事务的实现是根据AOP来实现的,对于我这个小菜鸟,只能一步一步来了,决定先从Spring的AOP开始。
1 动态代理
Spring AOP中使用了两种动态代理,一种是JDK的动态代理,一种CGLIB的动态代理。JDK的动态代理必须指定接口,这些接口都是已经被代理对象实现了的;而CGLIB代理则不需要指定接口。
1.1 JDK动态代理
JDK的动态代理网上有很多资料,这里只说我自己的理解。
JDK动态代理必须实现InvocationHandler接口,然后通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)获得动态代理对象。
示例:
public interface Service {
public void help();
public void hello(String str);
} public interface Item {
void test();
} public class ServiceImpl implements Service, Item {
@Override
public void help() {
System.out.println("I want to buy some book");
} @Override
public void hello(String str) {
System.out.println("hello: " + str);
} @Override
public void test() {
System.out.println("Test dynamic proxy.");
}
} public class DynamicProxy implements InvocationHandler {
//这个就是我们要代理的真实对象
private Object service; //构造方法,给我们要代理的真实对象赋初值
public DynamicProxy(Object service) {
this.service = service;
} @Override
public Object invoke(Object object, Method method, Object[] args)
throws Throwable {
//在代理真实对象前我们可以添加一些自己的操作
System.out.println("before buy some book"); System.out.println("Method:" + method); //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
method.invoke(service, args); //在代理真实对象后我们也可以添加一些自己的操作
System.out.println("after buy some book"); return null;
}
} public class Client {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true); //要代理的真实对象
Service service = new ServiceImpl(); //要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
InvocationHandler handler = new DynamicProxy(service); //添加以下的几段代码, 就可以将代理生成的字节码保存起来
try {
Field field = System.class.getDeclaredField("props");
field.setAccessible(true);
Properties props = (Properties) field.get(null);
props.put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); Package pkg = Client.class.getPackage();
if (pkg != null) {
String packagePath = pkg.getName().replace(".", File.pathSeparator);
new File(packagePath).mkdirs();
} Service serviceProxy = (Service) Proxy.newProxyInstance(service.getClass().getClassLoader(),
service.getClass().getInterfaces(), handler); System.out.println(serviceProxy.getClass().getName());
serviceProxy.help();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} }
} //生成的字节码文件$Proxy01.class反编译后的代码:
public final class $Proxy0 extends Proxy implements Service, Item {
private static Method m1;
private static Method m5;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m0; 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 test() throws {
try {
super.h.invoke(this, m5, (Object[])null);
} 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);
}
} public final void help() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
} public final void hello(String var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
} 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);
}
} static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m5 = Class.forName("com.meituan.proxy.Item").getMethod("test", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.meituan.proxy.Service").getMethod("help", new Class[0]);
m4 = Class.forName("com.meituan.proxy.Service").getMethod("hello", new Class[]{Class.forName("java.lang.String")});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
所有方法都会被导向调用InvocationHandler接口的唯一的方法invoke,这个就是我们在被代理对象前后插入相关逻辑的地方。
1.2 CGLIB动态代理
1.2.1 CGLIB的代理用法
使用CGLib动态代理,被代理类不需要强制实现接口。
CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
示例:
public class Biz {
public void add() {
System.out.println("这是一个普通方法...加法。");
} public void minus() {
System.out.println("这是一个普通方法...减法。");
}
} public class BizInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("BizInterceptor, 调用开始 method: " + method +
", methodProxy: " + methodProxy);
methodProxy.invokeSuper(o, objects);
System.out.println("BizInterceptor, 调用结束");
return null;
}
} public class BizCglibClient {
private Object target; /**
* 创建代理对象
*
* @param target
* @return
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
/*Class<?> clazz[] = {Service.class};
enhancer.setInterfaces(clazz);*/
//回调方法
Callback[] obj = {new BizInterceptor(), new BizInterceptor2()};
enhancer.setCallbacks(obj);
enhancer.setCallbackFilter(new BizClassFilter());
//创建代理对象
return enhancer.create();
} public static void main(String[] args) {
try {
Field field = System.class.getDeclaredField("props");
field.setAccessible(true);
Properties props = (Properties) field.get(null);
props.put(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/lanhongzhong/Workspace/LuceneTest"); /*Package pkg = Client.class.getPackage();
if (pkg != null) {
String packagePath = pkg.getName().replace(".", File.pathSeparator);
new File(packagePath).mkdirs();
}*/
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} BizCglibClient cglib = new BizCglibClient();
Biz bizCglib = (Biz) cglib.getInstance(new Biz());
bizCglib.add(); bizCglib.minus();
}
}
intercept方法中的参数:Object obj为由CGLib动态生成的代理类实例,Method method为被代理类中的方法引用,Object[] args为参数值列表,MethodProxy为生成的代理类对方法的代理引用。
1.2.2 CGLIB的过滤功能
CallbackFilter可以实现不同的方法使用不同的回调方法,用于分发不同的拦截器。
示例:
//结合1.2.1中的代码,添加如下代码
public class BizInterceptor2 implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("BizInterceptor2, 调用开始 method: " + method +
", methodProxy: " + methodProxy);
methodProxy.invokeSuper(o, objects);
System.out.println("BizInterceptor2, 调用结束");
return null;
}
} public class BizClassFilter implements CallbackFilter {
@Override
public int accept(Method method) {
if (method.getName().equals("minus")) {
return 1;
} return 0;
}
}
2 Spring AOP中的动态代理机制
Spring AOP中的代理根据被代理对象是否实现了接口选择不同的生成代理对象的方式,即第1部分中的两个情况。
2.1 JdkDynamicAopProxy
如果被代理对象实现了需要被代理的接口,则使用JDK的动态代理,在Spring AOP中对应的包装类为JdkDynamicAopProxy。
JdkDynamicAopProxy类实现了InvocationHandler接口,将被代理对象和拦截器作为参数传入,然后生成代理对象。
当代理对象被调用时,JdkDynamicAopProxy的invoke方法作为Proxy对象的回调函数而被触发,从而通过invoke的具体实现,来完成对目标对象方法调用的拦截或者说功能增强的工作。
在invoke方法中,设置了包括获取目标对象、拦截器链,同时把这些对象作为输入,创建了ReflectiveMethodInvocation对象,通过这个ReflectiveMethodInvocation对象来完成对AOP功能实现的封装。在这个invoke方法中,包含了一个完整的拦截器链对目标对象的拦截过程,比如获得拦截器链并对其中的拦截器进行配置,逐个运行拦截器链里的拦截增强,直到最后对目标对象方法的运行。具体可以看源代码。
//我只是搬运工 /**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource;
Class targetClass = null;
Object target = null; try {
... ... // May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
} // Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
} ... ... return retVal;
}
}
2.2 CglibAopProxy
Cglib2AopProxy的intercept回调方法的实现和JdkDynamicAopProxy的回调实现是非常类似的,只是在Cglib2AopProxy中构造的是CglibMethodInvocation对象来完成拦截器链的调用,而在JdkDynamicAopProxy中是通过构造ReflectiveMethodInvocation对象来完成这个功能的。
3 总结
Spring AOP的核心实现原理就是采用的动态代理,根据被代理对象是否实现了所要被代理的接口这个条件,动态代理会选择不同的实现方案。本文只是尽我所能简单的拿着各方资料来了一个汇总,是一个自己的学习总结。对于Spring AOP的设计架构是我下一步的学习目标。
参考文献
1、http://blog.csdn.net/mhmyqn/article/details/48474815
2、《Spring技术内幕:深入解析Spring架构与设计原理》 许文柯
Spring AOP中的动态代理的更多相关文章
- 转:Spring AOP中的动态代理
原文链接:Spring AOP中的动态代理 0 前言 1 动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2 S ...
- Spring Boot实践——Spring AOP实现之动态代理
Spring AOP 介绍 AOP的介绍可以查看 Spring Boot实践——AOP实现 与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改 ...
- spring---aop(4)---Spring AOP的CGLIB动态代理
写在前面 前面介绍了Spring AOP的JDK动态代理的过程,这一篇文章就要介绍下Spring AOP的Cglib代理过程. CGLib全称为Code Generation Library,是一个强 ...
- Spring AOP实现原理-动态代理
目录 代理模式 静态代理 动态代理 代理模式 我们知道,Spring AOP的主要作用就是不通过修改源代码的方式.将非核心功能代码织入来实现对方法的增强.那么Spring AOP的底层如何实现对方法的 ...
- spring---aop(2)---Spring AOP的JDK动态代理
写在前面 spring 事务是springAOP 的一个实现.我们以分析spring的事务,来分析spring的AOP实现. 基本知识 如果目标方法被spring的事务声明,则执行该目标方法的对象就会 ...
- Spring AOP系列(二) — 动态代理引言
接上一篇Spring AOP系列(一)- 代理模式,本篇来聊聊动态代理. 动态代理与静态代理的区别 要想了解动态代理与静态代理的区别,需要有两个前置知识点:java程序是如何执行的以及类加载机制. j ...
- Spring AOP关于cglib动态代理
一: Spring AOP的默认代理方式是jdk动态代理,还有另外一种代理方式是cglib代理,简单说前者基于接口,后者基于继承,基本思路是将被代理对象的类作为父类,然后创建子类来进行方法的调用,调用 ...
- Spring AOP系列(三) — 动态代理之JDK动态代理
JDK动态代理 JDK动态代理核心是两个类:InvocationHandler和Proxy 举个栗子 为便于理解,首先看一个例子: 希望实现这样一个功能:使用UserService时,只需关注自己的核 ...
- Spring AOP源码分析--代理方式的选择
能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! 年前写了一个面试突击系列的文章,目前只有redis相关的.在这个系列里,我整理了一些面试题与大家 ...
随机推荐
- Java——类谜题
1.令人混淆的构造器 代码如下格式: public class Confusing { private Confusing(Object o) { System.out.println("O ...
- astah* professional 6.9.0
下载地址:http://members.change-vision.com/files/astah_professional/6_9_0 破解方法:按照Astah Professional 6.9后打 ...
- 电子工程师名片——FAT16文件系统(转)
源:电子工程师名片——FAT16文件系统 从8月8号开始,连续一个月利用每天下班时间和周末的时间终于初步完成了一个电子工程师的电路板名片,就像U盘一样,不过这个FLASH只有64KB的大小,用的单片机 ...
- (二)Jquery Mobile介绍以及Jquery Mobile页面与对话框
Jquery Mobile介绍以及Jquery Mobile页面与对话框 一. Adobe Dreamweaver CS6 环境 最新版本的cs6会支持JM的使用,有自动提示功能,很强大.安装说明地 ...
- Java设计模式遵循的七大原则
最近几年来,人们踊跃的提倡和使用设计模式,其根本原因就是为了实现代码的复用性,增加代码的可维护性.设计模式的实现遵循了一些原则,从而达到代码的复用性及增加可维护性的目的,设计模式对理解面向对象的三大特 ...
- 关于cin的用法一些小结
在写二叉树的时候遇到if(!cin)那几个标志位弄得并不清楚,还遇到了诸如cin.clear()等函数,感觉C++又白学了,于是打算去网上搜了几篇靠谱的文章,有时候看来,一些事件处理类的工程代码,在A ...
- 一起学JUCE之HashMap
基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同.) ...
- leetcode--007 word break I
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+4AAAC5CAIAAAA55fI7AAAZa0lEQVR4nO3dPW7bQIMG4L2MT6B7+A
- IAR for STM8 错误
一个IAR for STM8 v1.3 的工程,换到1.4版后出现如下错误 unable to allocate space for sections/blocks with a total esti ...
- Android自定义控件(状态提示图表) (转)
源:Android自定义控件(状态提示图表) 源:Android应用开发 [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 前面分析 ...