zoukankan      html  css  js  c++  java
  • Spring基础系列——AOP

    基础知识

    AOP是Aspect Oriented Programing 的简称,译为“面向切面编程”。涉及到的概念有连接点、切点、增强、目标对象、引介、织入、代理、切面。

    织入:

      1)编译期织入,需要特殊的java编译器;

      2)类装载期织入,需要特殊的类装载器;

      3)动态代理织入,在运行期为目标类添加增强生成子类的方法;

    Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

    View Code
    public interface ForumService {
        public void removeTopic(int topicId);
    }
    
    public class ForumServiceImpl implements ForumService{
    
        public void removeTopic(int topicId) {
            System.out.println("delete topic record:" + topicId);
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    时间统计类代码

    View Code
    public class PerformanceMonitor {
        private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>();
    
        public static void begin(String method) {
            System.out.println("begin monitor...");
            MethodPerformance mp = new MethodPerformance(method);
            performanceRecord.set(mp);
        }
    
        public static void end() {
            System.out.println("end monitor...");
            MethodPerformance mp = performanceRecord.get();
            mp.printPerformance();
        }
    }
    
    
    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+" cost " + elapse + " ms.");
        }
    }

    如果想对此service方法进行性能统计,传统方法只能在removeTopic方法中添加统计代码,记录开始,结束的时间然后计算差值。但是这样代码工作量巨大而且不易维护。

    采用动态代理之后

    View Code
    /**
     * Author: Leo Sun
     * Blog: http://fuxinci.com/
     * Date: 3/25/13
     */
    public class PerformanceHandler implements InvocationHandler {
        private Object target;
    
        public PerformanceHandler(Object target) {
            this.target = target;
        }
    
        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;
        }
    }

    测试方法:

    View Code
        public static void main(String[] args) {
            // test dynamic proxy
            ForumService target = new ForumServiceImpl();
            PerformanceHandler handler = new PerformanceHandler(target);
            ForumService proxy = (ForumService) Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    handler);
    
            proxy.removeTopic(1012);
    
        }

    采用动态代理有一个限制,那就是只能为借口创建代理,对于没有使用借口定义的业务类如何织入呢?可以采用CGLib实现。

    View Code
    /**
     * Author: Leo Sun
     * Blog: http://fuxinci.com/
     * Date: 3/25/13
     */
    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 methodProxy) throws Throwable {
            PerformanceMonitor.begin(obj.getClass().getName() + "." + method.getName());
            Object result = methodProxy.invokeSuper(obj, args);
            PerformanceMonitor.end();
    
            return null;
        }
    }

    测试方法:

    View Code
        public static void main(String[] args) {
            // test cglib
            CglibProxy cglibProxy = new CglibProxy();
            ForumServiceImpl forumService = (ForumServiceImpl)cglibProxy.getProxy(ForumServiceImpl.class);
            forumService.removeTopic(1012);
    
        }

      运行上面的代码输出以下信息:

    begin monitor...
    delete topic record:1012
    end monitor...
    org.sun.aop.ForumServiceImpl$$EnhancerByCGLIB$$a5b47e2a.removeTopic cost 38 ms.

    输出的代理名字是ForumServiceImpl$$EnhancerByCGLIB$$a5b47e2a,这个特殊的类就是CGLib为ForumServiceImpl动态创建的子类。所以CGLib不能对目标类中的final方法进行代理。

    关于性能:

      有研究表明CGLib所创建的动态代理对象性能比jdk所创建的代理对象的性能高不少(大概10倍),但是CGLib在创建代理的时候所花的时间却比jdk动态代理多(大概8倍)。

    增强类

    按照增强在目标类的方法的连接点的位置,可以分为以下5种:前置增强、后置增强、环绕增强、异常抛出增强、引介增强。

    1.前置增强:

    目标类

    View Code
    /**
     * Author: Leo Sun
     * Blog: http://fuxinci.com/
     * Date: 3/26/13
     */
    public interface Waiter {
        void greetTo(String name);
    
        void serveTo(String name);
    }
    
    public class NaiveWaiter implements Waiter {
        public void greetTo(String name) {
            System.out.println("Greet to " + name);
        }
    
        public void serveTo(String name) {
            System.out.println("Serve to " + name);
        }
    }

    增强内容

    View Code
    /**
     * Author: Leo Sun
     * Blog: http://fuxinci.com/
     * Date: 3/26/13
     */
    public class GreetingBeforeAdvice implements MethodBeforeAdvice {
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            String clientName = (String) objects[0];
            System.out.println("How are you! Mr." + clientName + ".");
        }
    }

    测试类

    View Code
    import org.springframework.aop.BeforeAdvice;
    import org.springframework.aop.framework.ProxyFactory;
    
    /**
     * Author: Leo Sun
     * Blog: http://fuxinci.com/
     * Date: 3/26/13
     */
    public class TestBeforeAdvice {
        public static void main(String[] args){
            Waiter target = new NaiveWaiter();
            BeforeAdvice advice = new GreetingBeforeAdvice();
    
            // spring 提供的代理工厂
            ProxyFactory pf = new ProxyFactory();
            // 设置代理目标
            pf.setTarget(target);
            // 为代理目标添加增强
            pf.addAdvice(advice);
    
            Waiter proxy = (Waiter)pf.getProxy();
            proxy.greetTo("John");
            proxy.serveTo("Leo");
    
        }
    }

    2.后置增强:

    实现AfterReturningAdvice

    3.环绕增强:

    实现MethodInterceptor

    4.异常抛出增强

    实现ThrowsAdvice

    5.引介增强

    它不是在目标方法周围织入增强,而是为目标类创建新的方法和属性,所以引介增强是类级别的,而非方法级别。

    切面

    在介绍增强时,增强织入到目标类的所有方法中,如果我们想织入到目标类的特定方法中,那么就要使用切点进行目标连接点的定位了。

    静态普通方法名切面,静态正则表达式方法匹配切面,动态切面,流程切面,符合切点切面,引介切面。

  • 相关阅读:
    ansible管理windows实践
    SQL server 备份/恢复/压缩 进度查询
    什么是容器
    pycharm 快捷键
    SUSE Linux--zypper程序包管理(实战命令总结)
    源码编译安装
    CentOS-yum基本使用
    rpm管理
    btrfs的精简总结版
    btrfs的介绍与使用
  • 原文地址:https://www.cnblogs.com/fuxinci/p/2982044.html
Copyright © 2011-2022 走看看