初探面向切片AOP(AspectJ)

同学做的东西遇到性能问题了,我们就想,
如果可以测出每个方法执行的时间
就可以分析出性能瓶颈在那里了,想了一下,不可能在每个方法调用前后加入时间代码,用代理模式可以实现,面向切面应该也可以实现。
既然别人已经发明好了轮子,就用轮子吧。
接触过一点AspectJ,发现可以实现
只是切入点的通用写法不会,好吧,百度
这样,可以将所有对类 hello.HelloWorld的访问都拦截,在调用之前,或调用之后,统计相应的信息
经过格式化后的结果如下:
execution(void hello.HelloWorld.main(String[])):
{
        execution(void hello.HelloWorld.sayHello()):
        {
                execution(void hello.HelloWorld.sayHello1()):
                {
                        execution(void hello.HelloWorld.sayHello2()):
                        {
                               
                        }
                        ,
                        耗时:65683
                }
                ,
                耗时:198282,
                execution(void hello.HelloWorld.sayHello2()):
                {
                       
                }
                ,
                耗时:50084
        }
        ,
        耗时:369469,
        execution(void hello.HelloWorld.sayHello1()):
        {
                execution(void hello.HelloWorld.sayHello2()):
                {
                       
                }
                ,
                耗时:51726
        }
        ,
        耗时:151483,
        execution(void hello.HelloWorld.sayHello2()):
        {
               
        }
        ,
        耗时:49263
}
有了这个类似JSON的数据,
思考:
可以分析出源代码中的调用main{sayHello();sayHello1();sayHello2();}这里的时间获取不到?是因为main方法已经退出啦,没有机会记录
                                            sayHello{sayHello1();sayHello2();}369469
                                              sayHello1{sayHello2();} 151483
                                                sayHello2{};49263
可以方便的分析出那一个方法在那个调用路径下耗时最长。
sayHello2方法,在4种情况下(在复杂的条件下,调用上下文会导致最终)的执行时间是不相同的,65683,50084,51726,49263
这对分析源代码的执行时的性能问题,十分有用,还可以获得传入的参数值和方法的返回值,果真是十分强大
另外,这个东西,分析出来的调用堆栈已经大部分还原了我的方法中内容了,用来反编译,那是相当准确的。
比如:我的sayHello1就是这样写的
public void sayHello1()
{
  this.sayHello2();
}
这还不足以说明问题么?
其实可以做得更深入一点,用一个可视化的树来表示调用堆栈,和调用时间,分析各个方法调用时间的平均值,找出异常执行时的传入参数,这对查找BUG和修复BUG都是很有用的,做这样一个性能分析工具,需要的只是时间。
进一步思考,面向切片编程的根本用途就是,在调用一个方法前或后去执行一些特定的操作。在对一些封装包,无法获取源代码,或无权更改相应的业务代码(实际工作是经常遇到),而要对某个类的某些方法添加一些通用的操作时,就有很大的优势。而不用为了每个类加一个统计时间或记录调用参数或一些其实操作,将所有类的每个方法进行一次封装。
更深一点的应用,就应该是Spring对它的一些应用了。
aspectj可以用.aj来写,也可以在.java中用注解来写,灵活。
最终代码在最后,依赖aspectj.jar包
package hello;
import org.apache.commons.lang.time.StopWatch;
public aspect World
{
String resultStr = "";
pointcut callMethod() : call(* hello.HelloWorld.*(..));

pointcut afterCallAnyMethod() : execution(* hello.HelloWorld.*(..));

pointcut beforeCallAnyMethod() : execution(* hello.HelloWorld.*(..));

Object around():callMethod()
{
//  StopWatch clock = new StopWatch();
//  clock.start();
//  clock.stop();
//  System.out.println(clock.getTime());
  long startTime = System.nanoTime();
  Object result = proceed();
  long endTime = System.nanoTime();
  //System.out.print(thisEnclosingJoinPointStaticPart.toShortString());
//  System.out.println(thisJoinPointStaticPart.toShortString());
  //System.out.print(" called ");
  //System.out.print(thisJoinPoint.toShortString());
  resultStr+="耗时:" + (endTime - startTime)+",";
  System.out.print("耗时:" + (endTime - startTime)+",");
  return result;
}

before() :beforeCallAnyMethod()
{
  resultStr+=thisJoinPoint.toString()+":{";
  System.out.print(thisJoinPoint.toString()+":{");
  //System.out.println("开始执行:"+thisJoinPoint.toString()+"\t\t\t\tat time:"+System.nanoTime());
  //System.out.println("before call time:"+System.nanoTime());
}

after() returning : afterCallAnyMethod()
{
  resultStr+="},";
  System.out.print("},");
  //System.out.println("执行完成:"+thisJoinPoint.toString()+"\t\t\t\tat time:"+System.nanoTime());
  //System.out.println("after  call time:"+System.nanoTime());
}
}

转载于:https://my.oschina.net/wamdy/blog/88412

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值