zoukankan      html  css  js  c++  java
  • AOP 详解

    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); 
            }
    }
    

    参考资料:

  • 相关阅读:
    PHP解决跨域问题
    《高性能MySQL》笔记——MySQL建表数据类型的选择
    PHP中有关IPV4 和IPV6地址转换以及其它一些常见问题
    Axure RP Extension for Chrome安装
    DelayQueue的使用
    MySQL8.0设置远程访问权限
    Git 常用命令
    Zipkin分布式跟踪系统介绍
    什么是kibana?
    Elastic-Job-分布式调度解决方案
  • 原文地址:https://www.cnblogs.com/linkworld/p/9949405.html
Copyright © 2011-2022 走看看