AOP学习一:JDK动态代理
2013-05-15 20:20
344 查看
AOP,Aspect Oriented Programming,就是所谓的面向切面编程。这个词语的含义,网上的解释已经很多很多了,理解起来并不怎么难,照葫芦画瓢也能开发出带aop功能的模块出来。开始我也是这样,但只是会用,对它的原理却并没有做过深入地学习。之前一段时间挺闲,于是就打算从头开始认认真真地去研究下aop的原理。于是便有了如下的一套学习笔记,分享出来,只要照着上面的代码敲一遍,相信也会对aop的实现有所掌握。
在开始aop学习之前,先来说下两种动态代理技术:JDK动态代理及CGLib动态代理。aop便是以这两者为基础,实现了切面编程的功能。
何为代理?当一个类被AOP织入增强后,就产出了一个代理类,这个类融合了原类和一些需要添加的增强逻辑。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
Spring AOP使用了两种代理机制:一种是基于JDK的动态代理;另一种是基于CGLib的动态代理。之所以需要两种代理机制,很大程度上是因为JDK本身只提供接口的代理,而不支持类的代理。下面依次来讲述这两种动态代理的实现。
JDK动态代理
JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。其中InvocationHandler是一个接口,可以通过实现该接口来定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。而Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。下面给出使用示例:
业务接口:
[java] view
plaincopy
package proxy.jdk;
public interface ForumService {
void removeTopic(int topicId);
void removeForum(int forumId);
}
业务接口实现类:
[java] view
plaincopy
package proxy.jdk;
public class ForumServiceImpl implements ForumService {
@Override
public void removeTopic(int topicId) {
System.out.println("模拟删除Topic记录:" + topicId);
try {
Thread.sleep(20);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void removeForum(int forumId) {
System.out.println("模拟删除Forum记录:" + forumId);
try {
Thread.sleep(40);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
上面的实现类中,只是简单地实现了接口中定义的方法。现在,我们要实现这样的功能:如果这两个方法被调用,则记录下调用日志,并且简单计算下执行时间。如果照正常的逻辑来处理,那应该是类似下面的逻辑代码:
[java] view
plaincopy
@Override
public void removeTopic(int topicId) {
System.out.println("调用目标对象方法 [removeTopic]开始...");
long start = System.currentTimeMillis();
System.out.println("模拟删除Topic记录:" + topicId);
try {
Thread.sleep(20);
} catch (Exception e) {
throw new RuntimeException(e);
}
long end = System.currentTimeMillis();
System.out.println("调用目标对象方法[removeTopic]结束.总共花费了: " + (end - start) + "ms");
}
日志的记录及时间的计算代码包围在核心的业务代码外,并且同样的代码实现需要对removeForum这个方法再写一遍。可想而知,如果有多个方法要处理,这样的重复处理的代码需要写多次。既浪费时间,又使代码变得臃肿。现在,我们通过JDK动态代理技术,来实现上面的功能:
实现了InvocationHandler接口的类:
[java] view
plaincopy
package proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PerformationHandler implements InvocationHandler {
// 目标业务类
private Object target;
public PerformationHandler(Object target) {
this.target = target;
}
/**
* 将横切逻辑代码与业务代码编织到一起
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = target.getClass().getName() + "." +method.getName();
System.out.println("调用目标对象方法 [" + methodName + "]开始...");
long start = System.currentTimeMillis();
// 通过反射调用目标类的目标方法
Object obj = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("调用目标对象方法[" + methodName + "]结束.总共花费了: " + (end - start) + "ms");
return obj;
}
}
InvocationHandler接口定义了一个invoke()方法,proxy是最终生成的代理实例,一般不会用到;method是被代理目标实例的某个具体方法,通过它可以发起目标实例方法的反射调用;args是通过被代理实例某一方法的入参,在方法反射调用时使用。
按照我们正常调用的逻辑,实现代码应该如下:
[java] view
plaincopy
// 正常的业务实现
ForumService forumService = new ForumServiceImpl();
forumService.removeTopic(10);
forumService.removeForum(13);
但现在我们使用了动态代理,因此就通过代理来调用。
通过Proxy结合PerformationHandler创建业务接口的代理实例:
[java] view
plaincopy
// 通过动态代理来实现
ForumService target = new ForumServiceImpl();
PerformationHandler handler = new PerformationHandler(target);
ForumService proxy = (ForumService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target
.getClass().getInterfaces(), handler);
proxy.removeForum(10);
proxy.removeTopic(12);
通过Proxy的newProxyInstance()静态方法为混合了业务处理逻辑和性能监视逻辑的handler创建一个符合ForumService接口的代理实例。该方法的第一个入参为类加载器;第二个入参为创建代理所需要实现的一组接口;第三个参数是整合了业务逻辑和性能监视逻辑(这里即所谓的横切逻辑)的编织器对象。生成的代理实例实现了目标业务类的所有接口,即ForumServiceImpl的ForumService接口。这样,我们就可以按照调用ForumService接口实现相同的方式调用代理实例。运行以上代码,输出结果如下:
调用目标对象方法 [proxy.jdk.ForumServiceImpl.removeForum]开始...
模拟删除Forum记录:10
调用目标对象方法[proxy.jdk.ForumServiceImpl.removeForum]结束.总共花费了: 40ms
调用目标对象方法 [proxy.jdk.ForumServiceImpl.removeTopic]开始...
模拟删除Topic记录:12
调用目标对象方法[proxy.jdk.ForumServiceImpl.removeTopic]结束.总共花费了: 20ms
我们发现,程序的运行效果和直接在业务类中编写性能监视逻辑的效果一致,但是在这里,原来分散的横切逻辑代码已经被抽取到PerformationHandler中,当调用代理对象的removeForum()和removeTopic()方法时,便调用了invoke()方法。
在开始aop学习之前,先来说下两种动态代理技术:JDK动态代理及CGLib动态代理。aop便是以这两者为基础,实现了切面编程的功能。
何为代理?当一个类被AOP织入增强后,就产出了一个代理类,这个类融合了原类和一些需要添加的增强逻辑。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
Spring AOP使用了两种代理机制:一种是基于JDK的动态代理;另一种是基于CGLib的动态代理。之所以需要两种代理机制,很大程度上是因为JDK本身只提供接口的代理,而不支持类的代理。下面依次来讲述这两种动态代理的实现。
JDK动态代理
JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。其中InvocationHandler是一个接口,可以通过实现该接口来定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。而Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。下面给出使用示例:
业务接口:
[java] view
plaincopy
package proxy.jdk;
public interface ForumService {
void removeTopic(int topicId);
void removeForum(int forumId);
}
业务接口实现类:
[java] view
plaincopy
package proxy.jdk;
public class ForumServiceImpl implements ForumService {
@Override
public void removeTopic(int topicId) {
System.out.println("模拟删除Topic记录:" + topicId);
try {
Thread.sleep(20);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void removeForum(int forumId) {
System.out.println("模拟删除Forum记录:" + forumId);
try {
Thread.sleep(40);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
上面的实现类中,只是简单地实现了接口中定义的方法。现在,我们要实现这样的功能:如果这两个方法被调用,则记录下调用日志,并且简单计算下执行时间。如果照正常的逻辑来处理,那应该是类似下面的逻辑代码:
[java] view
plaincopy
@Override
public void removeTopic(int topicId) {
System.out.println("调用目标对象方法 [removeTopic]开始...");
long start = System.currentTimeMillis();
System.out.println("模拟删除Topic记录:" + topicId);
try {
Thread.sleep(20);
} catch (Exception e) {
throw new RuntimeException(e);
}
long end = System.currentTimeMillis();
System.out.println("调用目标对象方法[removeTopic]结束.总共花费了: " + (end - start) + "ms");
}
日志的记录及时间的计算代码包围在核心的业务代码外,并且同样的代码实现需要对removeForum这个方法再写一遍。可想而知,如果有多个方法要处理,这样的重复处理的代码需要写多次。既浪费时间,又使代码变得臃肿。现在,我们通过JDK动态代理技术,来实现上面的功能:
实现了InvocationHandler接口的类:
[java] view
plaincopy
package proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PerformationHandler implements InvocationHandler {
// 目标业务类
private Object target;
public PerformationHandler(Object target) {
this.target = target;
}
/**
* 将横切逻辑代码与业务代码编织到一起
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = target.getClass().getName() + "." +method.getName();
System.out.println("调用目标对象方法 [" + methodName + "]开始...");
long start = System.currentTimeMillis();
// 通过反射调用目标类的目标方法
Object obj = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("调用目标对象方法[" + methodName + "]结束.总共花费了: " + (end - start) + "ms");
return obj;
}
}
InvocationHandler接口定义了一个invoke()方法,proxy是最终生成的代理实例,一般不会用到;method是被代理目标实例的某个具体方法,通过它可以发起目标实例方法的反射调用;args是通过被代理实例某一方法的入参,在方法反射调用时使用。
按照我们正常调用的逻辑,实现代码应该如下:
[java] view
plaincopy
// 正常的业务实现
ForumService forumService = new ForumServiceImpl();
forumService.removeTopic(10);
forumService.removeForum(13);
但现在我们使用了动态代理,因此就通过代理来调用。
通过Proxy结合PerformationHandler创建业务接口的代理实例:
[java] view
plaincopy
// 通过动态代理来实现
ForumService target = new ForumServiceImpl();
PerformationHandler handler = new PerformationHandler(target);
ForumService proxy = (ForumService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target
.getClass().getInterfaces(), handler);
proxy.removeForum(10);
proxy.removeTopic(12);
通过Proxy的newProxyInstance()静态方法为混合了业务处理逻辑和性能监视逻辑的handler创建一个符合ForumService接口的代理实例。该方法的第一个入参为类加载器;第二个入参为创建代理所需要实现的一组接口;第三个参数是整合了业务逻辑和性能监视逻辑(这里即所谓的横切逻辑)的编织器对象。生成的代理实例实现了目标业务类的所有接口,即ForumServiceImpl的ForumService接口。这样,我们就可以按照调用ForumService接口实现相同的方式调用代理实例。运行以上代码,输出结果如下:
调用目标对象方法 [proxy.jdk.ForumServiceImpl.removeForum]开始...
模拟删除Forum记录:10
调用目标对象方法[proxy.jdk.ForumServiceImpl.removeForum]结束.总共花费了: 40ms
调用目标对象方法 [proxy.jdk.ForumServiceImpl.removeTopic]开始...
模拟删除Topic记录:12
调用目标对象方法[proxy.jdk.ForumServiceImpl.removeTopic]结束.总共花费了: 20ms
我们发现,程序的运行效果和直接在业务类中编写性能监视逻辑的效果一致,但是在这里,原来分散的横切逻辑代码已经被抽取到PerformationHandler中,当调用代理对象的removeForum()和removeTopic()方法时,便调用了invoke()方法。
相关文章推荐
- spring aop学习8:spring对jdk和cglib动态代理的选择
- AOP学习心得&jdk动态代理与cglib比较
- Spring AOP 学习之java JDK动态代理
- AOP编程学习笔记之----JDK动态代理技术
- AOP的底层实现-CGLIB动态代理和JDK动态代理 (以后学习)
- Spring学习(1)AOP初步—JDK动态代理
- spring框架学习(八)—静态代理、JDK与CGLIB动态代理、AOP+IoC
- Spring学习总结(二)——静态代理、JDK与CGLIB动态代理、AOP+IoC
- Spring学习总结——静态代理、JDK与CGLIB动态代理、AOP+IoC
- Spring学习总结(二)——静态代理、JDK与CGLIB动态代理、AOP+IoC
- aop学习总结一------使用jdk动态代理简单实现aop功能
- Spring学习总结(二)——静态代理、JDK与CGLIB动态代理、AOP+IoC
- jdk动态代理学习
- Spring AOP 代理实现的两种方式: JDK动态代理 和 Cglib框架动态代理
- 静态代理、JDK动态代理、CGLIB动态代理、Spring实现AOP、IOC+AOP
- AOP的底层实现-CGLIB动态代理和JDK动态代理
- AOP动态代理解析4-jdk代理的实现
- Spring Aop 中的JDK动态代理的实现
- spring---aop(2)---Spring AOP的JDK动态代理
- Java动态代理模式jdk和cglib的2种实现以及二者的区别(AOP面向切面的前奏)