Fork me on GitHub

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

参考资料:

posted @ 2018-11-12 21:59  小a的软件思考  阅读(691)  评论(0编辑  收藏  举报