声明:本文取材于《精通Spring2.x》上的经典例子。
要了解Spring AOP,建议先熟悉一下设计模式中的代理模式(不基于编程语言的代理模式,关键是理解其思想)。
场景:在各个业务方法中添加进行方法性能测试的逻辑,输出测试方法性能信息(比如运行所花费的时间)。
本例假设业务为ForumServiceImpl,它实现了ForumService接口,该接口提供两个业务操作,见代码:
interface ForumService{
void removeTopic(int topicId);
void removeForum(int forumId);
}
public class ForumServiceImpl implements ForumService{
public void removeTopic(int topicId){
PerformanceMonitor.begin("removeTopic");//性能测试代码
System.out.println("模拟删除Topic记录:"+topicId);
try{
Thread.currentThread().sleep(20);
}catch(Exception e){
e.printStackTrace();
}
PerformanceMonitor.end();//性能测试代码
}
public void removeForum(int forumId){
PerformanceMonitor.begin("removeForum");//性能测试代码
System.out.println("模拟删除Forum记录..."+forumId);
try{
Thread.currentThread().sleep(400);
}catch(Exception e){
e.printStackTrace();
}
PerformanceMonitor.end();//性能测试代码
}
}
所有注视了“性能测试代码”的代码行,都是用于进行性能测试的附加代码,跟真正的业务逻辑没有太大关系。一、跟真正业务无关的代码,二,零散在业务逻辑各个角落中的代码,对于满足这两个特性的代码,正是
Spring AOP所要解决的问题,即把零散在业务逻辑各个角落中零散的非业务逻辑代码抽取出来进行统一管理,使得开发人员只需集中精力在真正的业务逻辑代码。
代码中的PerformanceMonitor是用于性能监测的工具类,它通过ThreadLocal保存一个MethodPerformance实例,见代码:
class MethodPerformance{
private long begin;
private String monitorMethod;//监控的方法
public MethodPerformance(String method){
this.monitorMethod = method;
this.begin = System.currentTimeMillis();
}
public void printPerformance(){
System.out.println("End Monitor...");
System.out.println(monitorMethod+" took "+(System.currentTimeMillis()-this.begin)+" milli seconds!");
}
}
class PerformanceMonitor{
private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>();
public static void begin(String method){
System.out.println("Begin Monitor...");
MethodPerformance performance = new MethodPerformance(method);
performanceRecord.set(performance);
}
public static void end(){
MethodPerformance methodPerformance = performanceRecord.get();
methodPerformance.printPerformance();
}
}
下面看测试代码:
ForumService forumService = new ForumServiceImpl();
forumService.removeForum(10);
forumService.removeTopic(1);
运行结果:
Begin Monitor...
模拟删除Forum记录...10
End Monitor...
removeForum took 400 milli seconds!
Begin Monitor...
模拟删除Topic记录:1
End Monitor...
removeTopic took 20 milli seconds!
当然,各位看官眼明早就看出了程序中的问题了吧:把非业务逻辑操作混杂于业务逻辑!我们的目标是:将性能测试代码从业务逻辑中剔除。实现方法是动态代理,有基于JDK的动态代理和给予cglib的动态代理,本文先讲讲JDK Dynamic Delegate。
为了更好的理解,看客需要了解以下两个个名词:目标对象,代理对象。
Java本身支持动态代理,提供了两个接口:InvocationHandler和Proxy。InvocationHandler包含invoke方法,用于调用代理对象的目标方法,Proxy充当工厂角色,用于封装代理对象。先看代码再解释,先注释掉业务方法中用于性能测试的四行代码:
public class ForumServiceImpl implements ForumService{
public void removeTopic(int topicId){
//PerformanceMonitor.begin("removeTopic");
System.out.println("模拟删除Topic记录:"+topicId);
try{
Thread.currentThread().sleep(20);
}catch(Exception e){
e.printStackTrace();
}
//PerformanceMonitor.end();
}
public void removeForum(int forumId){
//PerformanceMonitor.begin("removeForum");
System.out.println("模拟删除Forum记录..."+forumId);
try{
Thread.currentThread().sleep(400);
}catch(Exception e){
e.printStackTrace();
}
//PerformanceMonitor.end();
}
}
然后,增加一个调用处理器,即InvocationHandler接口的实现,它用于封装目标对象的方法调用,在invoke方法中我们可以加入所有附加的业务逻辑。在这里,我们将性能测试的代码加到其中,而不是直接加到业务类中与业务逻辑混杂:
class PerformanceHandler implements InvocationHandler{
private Object target;//目标对象
public PerformanceHandler(){}
public PerformanceHandler(Object target){
this.target = target;//用于指定目标对象
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/*proxy:代理对象,method:目标类的方法,此例中即ForumService提供的两个的业务方法*/
PerformanceMonitor.begin(target.getClass().getName()+"."+method.getName()+"()");//在业务逻辑方法之前加入性能测试代码
Object obj = method.invoke(target, args);//使用反射调用目标类中的方法
PerformanceMonitor.end();//在业务逻辑方法之后加入性能测试代码
return obj;
}
}
这样就实现了性能测试代码和业务逻辑代码的分离,测试代码为:
ForumService target = new ForumServiceImpl();
PerformanceHandler handler = new PerformanceHandler(target);
ForumService proxyForum = (ForumService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
proxyForum.removeForum(1);
proxyForum.removeTopic(200);
此处,Proxy.getProxyInstance方法根据目标类的类加载器和所实现的接口,实现了基于特定InvocationHandler的代理对象,后面的操作中我们就可以透明的使用代理对象进行业务逻辑操作了。
问题总结:
- 动态代理对目标类中所有的方法都进行了拦截!如果此时ForumService加入了一个简单方法无需进行性能测试,
也逃不过InvocationHandler的拦截。当然,可以在invoke方法中加入简单判断:if(method.getName().equals(targetMethod)){...},
这样就可以对目标对象的目标方法进行区别对待。另外,AOP对此提供了更好的解决办法:IntroductionInterceptor。
- 从target.getClass().getInterfaces()可以看出,JDK动态代理需要目标对象必须实现一定的接口,如果此例中我们ForumServiceImpl不实现ForumService接口呢?
那么通过Proxy获得代理对象的代码将出现异常,异常描述类似于:$Proxy0 can't be cast to...即类型转换错误。这点cglib做得很成功。
- JDK动态代理是每次在使用代理对象时动态的改变目标类的信息,因此使用代理对象时执行效率受到了影响,这点cglib占有优势。
分享到:
相关推荐
SpringAOP动态代理 Spring AOP 使用的动态代理主要有两种方式:JDK 动态代理和 CGLIB 代理。 JDK 动态代理:用于代理实现了接口的类。Spring 会使用 java.lang.reflect.Proxy 类来创建代理对象。 CGLIB 代理:用于...
Spring-AOP-利用java中的动态代理和Spring的拦截器做到AOP
spring之AOP(动态代理),包括jdk动态代理和CGLib动态代理
主要对Spring AOP的相关概念和简单的静态代理、动态代理以及常见的几种AOP配置方式做总结学习。主要包括:1. AOP的常见概念 2. 静态代理 3. jdk动态代理 4. Aspectj and Aspectjweaver 5. **aop-config** 6. CGLIB ...
Jdk动态代理,基于接口的代理示例 InovactionHandler Proxy
我们还提供了实际示例,演示如何在Spring AOP中使用JDK动态代理。 CGLib动态代理: 我们将深入研究CGLib动态代理,它允许您在不需要接口的情况下创建代理对象。您将了解CGLib的工作原理,以及如何生成子类来实现...
对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。 Hibernate 的 二级缓存 学习案例 案例代码 接下来,就通过一个案例来演示Spring中JDK动态代理的实现过程,具体代码请参见教材3.2.1小节。 CGLIB代理 ...
基于JDK的动态代理。必须是面向接口的,只有实现了具体接口的类才能生成代理对象
spring aop jdk 动态代理的底层实现解析模拟
AOP之JDK动态代理和CGLib动态代理 ,具体效果和过程看博文 http://blog.csdn.net/evankaka/article/details/45195383
AOP的意思就是面向切面编程。本文主要是通过梳理JDK中自带的反射机制,实现 AOP动态代理模式,这也是Spring AOP 的实现原理
Spring Aop的底层实现技术 --- Jdk动态代理原理 很不错的一篇文章
Spring框架的AOP中重要的一个知识点,动态代理,springAOP框架会根绝实际情况选择使用jdk的动态代理还是cglib的动态代理
NULL 博文链接:https://hyp1987.iteye.com/blog/1833776
哪怕没有看过源码的同学也应该知道,AOP是通过动态代理实现的,动态代理又分为两个部分:JDK动态代理和CGLIB动态代理 确实,Spring也就是通过这两种方式来实现AOP相关功能,下面就通过源码来简单求证下
NULL 博文链接:https://fruitking.iteye.com/blog/601106
如果一个类实现了一个或多个接口,那么Spring就会使用默认的JDK动态代理,如果没有实现任何接口,就会使用cglib来代理。当然我们也可以手动改变这些设置。这也是比较容易掉坑的部分,如果设置错了代理方式,那么在...
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP 3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间...
aspectj-1.7.4.jar,aspectjweaver-1.7.4.jar
为了说明Spring的AOP原理,本人使用代理模式中的动态代理完成演示AOP编程的原理的演示。相信,如果你耐心看完整个程序(几乎一行注释一行代码),那么你对Spring这个东西就不是觉得有什么神秘了! 阅读对象:凡是喜爱...