1. 需求:统计方法执行的性能情况(来源:《精通Spring 4.x》)

// 性能监视类 PerformanceMonitor
package com.noodles.proxy;
public class PerformanceMonitor{
// 通过一个ThreadLocal, 保存与调用线程相关的性能监视信息
private static ThreadLocal<MethodPerformance> performanceRecord =
new ThreadLocal<MethodPerformance>(); // 开始对某一方法进行性能监视
public static void begin(String method){
System.out.println("性能监视开始...");
MethodPerformance mp = new MethodPerformance(method);
performanceRecord.set(mp);
} // 对某一方法的性能监视结束
public static void end(){
System.out.println("性能监视结束...");
MethodPerformance mp = performanceRecord.get();
// 打印性能监视结果信息
mp.printPerformance();
}
} // 记录性能监视类 MethodPerformance
public class MethodPerformance {
private long begin;
private long end;
private String serviceMethod; // 构造函数
public MethodPerformance(String serviceMethod){
this.serviceMethod = serviceMethod;
// 程序执行的开始时间
this.begin = System.currentTimeMillis();
} // 打印方法执行消耗的时间
public void printPerformance(){
end = System.currentTimeMillis();
long elapse = end - begin;
System.out.println(serviceMethod + " 花费 " + elapse + "毫秒");
}
} // 业务类中完成性能监视的功能
public class ForumServiceImpl implements ForumService {
// 方法一: removeTopic
public void removeTopic(int topicId){
// 开始性能监视
PerformanceMonitor.begin(
"com.noodles.proxy.ForumServiceImpl.removeTopic");
System.out.println("模式删除Topic:" + topicId);
try{
Thread.currentThread().sleep(20);
}catch(Exception ex){
throw new RuntimeException(e);
}
// 结束性能监视
PerformanceMonitor.end();
} // 方法二: 根据id,更新个人信息
public void updateInfoById(int userId){
// 开始性能监视
PerformanceMonitor.begin("com.noodles.proxy.ForumServiceImpl.updateInfoById");
System.out.println("模拟更新个人信息:" + userId);
try{
Thread.currentThead().sleep(40);
}catch(Exception ex){
throw new RuntimeException(ex);
}
// 结束性能监视
PerformanceMonitor.end();
}
}

1.2 上述代码存在问题

  • ForumServiceImpl中每个方法都存在性能监视的代码,存在冗余;
  • 性能监视与ForumServiceImpl业务不相关,属于系统功能,此处,存在耦合;
  • AOP(Aspect Oriented Programming, 面向切面编程)也就是为解决此类问题,采用横向抽取的机制,将大量重复,且与业务不相关的代码(如性能监控,事务管理以及日志记录等)剥离,作为一个切面类;

2. AOP 详解

  • AOP 的核心为切面,切面 = 切点 + 增强;
  • 增强(Advice): 从业务类中剥离处理的非业务代码(如上例中的性能监控代码);
  • 切点(Pointcut): 用于解决在业务类中的哪个位置来执行增强(Advice);

2.1 AOP 底层技术

  • AOP 底层依赖的是动态代理技术;根据所代理的类有无实现接口,分为:

    - JDK 动态代理: Proxy类和InvocationHandler

    - CGLib 动态代理:
/**
*JDK 动态代理
*/
// 实现InvocationHandler接口
public class PerformanceHandler implements InvocationHandler{
private Object target; // target 为业务类
public PerformanceHandler(Object target){
this.target = target;
} // 实现 invoke()方法, 返回代理对象
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 开启性能监视
PerformanceMonitor.begin(
target.getClass().getName() + "." + method.getName());
// 通过反射调用业务方法
Object obj = method.invoke(target, args);
// 结束性能监视
PerformanceMonitor.end();
return obj;
}
} // 编写测试类 ForumServiceTest
public class ForumServiceTest {
// 希望被代理的目标业务类
ForumService target = new ForumServiceImpl(); // 将目标业务类和横切代码编织到一起
PerformanceHandler handler = new PerformanceHandler(target); // 创建代理类
ForumService proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler); // 调用代理实例
proxy.removeTopic(1011);
proxy.updateInfoById(567);
} /**
* CGLib 代理
* 采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入
* 横切逻辑;
*/
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz){
enhancer.setSuperClass(clazz);
enhancer.setCallback(this);
return enhancer.create();
} // 拦截父类所有方法的调用
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
PerformanceMonitor.begin(obj.getClass().getName() + "." + method.getName());
Object result = proxy.invokeSuper(obj, args);
PerformanceMonitor.end();
return result;
}
} // 编写测试方法 ForumServiceTest
public class ForumServiceTest {
@Test
public void proxy(){
CglibProxy proxy = new CglibProxy();
ForumServiceImpl forumService =
(ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class);
forumService.removeTopic(1011);
forumService.updateInfoById(567);
}
}

参考资料:

最新文章

  1. mysql在Linux下
  2. Robot Framework安装教程
  3. 毫无保留开源我写的:IOS Android Ipad 多点触摸通用js 库
  4. linux内核中的get_user和put_user
  5. XZ压缩最新压缩率之王
  6. Intellij Idea 13 vmoptions (Mac版本)
  7. MIT 操作系统实验 MIT JOS lab2
  8. Opencv常用函数
  9. HTTPS的原理解析
  10. 如何打开hprof文件
  11. 简单的纯js三级联动
  12. Arduino语法-变量和常量
  13. js浮点数运算精度问题
  14. docker容器跑redis
  15. Redis 通过 info 查看信息和状态
  16. 20175314 《Java程序设计》第二周学习总结
  17. Unity入门教程(上)
  18. Jquery选择器 选择一个不存在的元素 为什么不会返回 false
  19. 「日常训练」「小专题·USACO」 Ski Course Design (1-4)
  20. DNN CMS Platform

热门文章

  1. Linux关闭Tomcat为什么要用Kill,而不是shutdown.sh
  2. 01python初识—编辑器&amp;版本&amp;变量知识
  3. Bypass 护卫神SQL注入防御(多姿势)
  4. VS2017编译Poco1.9.0的64版本
  5. ScaleType属性
  6. windowsError错误码详解
  7. 查看sql server日志
  8. Git学习(二)(2015年11月18日)(2016年1月29日)
  9. &lt;转&gt;机器学习系列(9)_机器学习算法一览(附Python和R代码)
  10. Linux学习(二)