zoukankan      html  css  js  c++  java
  • Java的面向AOP编程

    这里写图片描写叙述

    一、 引言

    AOP(Aspect-Oriented Programming,面向切面的编程),是一种新型的编程范式,主张关注软件流程中的一个切面,将相同功能的代码整合打包在一起,减少系统的耦合性,增强其扩展性。

    传统的软件设计,往往採取事件驱动模型带来相似的效果,通过在可能的事件切入点插入事件回调函数,将相应位置插入外置代码。

    函数式编程,也有相似的解决方式,通过函数传递,将相应位置的扩展上新的功能。

    Java作为一门严谨的传统式开发语言。以安全性和可靠性为第一标准。语言并没有过多的新特性支持,Java8仅支持到lambda表达式,为了使Java具有更强大的编程模型,Spring等框架使用gclib库实现了面向切面的编程模型。

    二、 CGLIB 和 ASM

    CGLIB 是一个强大的,高性能,高质量的Code生成类库。被广泛的用作动态代理技术。CGLIB 包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类,新的字节码能够被Java虚拟机直接载入运行。

    事实上动态代理并非CGLIB的专利。早在JDK1.3版起,就引入了动态代理库。Spring AOP 编程时就能够进行选择,使用JDK提供的动态代理库。或者是引入CGLIB库。

    以下举一个实例,来说明一些怎样使用CGLIB库,将我们本来应该正常运行的函数调用,进行截断操作。

    package com.abs.testcglib;
    
    public class Service {
        String name;
    
        public Service(String name) {
            this.name = name;
        }
    
        public void sayHello() {
            System.out.println("Hello "+name);
        }
    }

    首先,我们创建一个服务类,当中有一个sayHello() 方法。我们希望将这种方法截断,以加入其余组件的一些处理功能,比如持久化组件希望在此加入一条记录一类的功能。

    package com.abs.testcglib;
    
    public class Main {
        public static void main(String[] args) {
            Service s = new Service("Sxf");
            s.sayHello();
        }
    }

    在Main函数中调用一下,能够看的Hello Sxf 的输出。

    但我们怎么截断呢?首先就要创建一个代理类,所谓代理,就是你让这个代理类,代你调用这个类的函数。

    创建一个代理类:

    package com.abs.testcglib;
    
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class CglibProxy implements MethodInterceptor {
    
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("方法名:"+method.getName());
            Service a = (Service) o;
            a.name = "Wah";
            System.out.println("哈哈,我要改名");
            Object result = methodProxy.invokeSuper(o, args);
            return result;
        }
    }

    这个代理类的功能,就是将传统的Java直接的函数调用,包上一次外壳,由于Java本身的函数调用是系统完毕的。非常难由你大段他,但代理类不同,你能够明白的看的调用了哪个函数。而且能够依据这点,轻松的在函数调用前后。插入你希望插入的代码。

    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy)
    这个函数大概是整个代理调用中最关键的一个函数了。o这个參数表示了该函数所在的对象,args是调用的參数。Method则是反射到的方法。最后一个则是代理的实例。
    我们对函数的打断功能。则都在这个函数里实现。

    当然,由于是通过代理实现,对象的构建也有所不同,所以我们自己写一个static函数作为构造函数使用。

    package com.abs.testcglib;
    
    import net.sf.cglib.proxy.Enhancer;
    
    public class Service {
        String name;
    
        public Service(String name) {
            this.name = name;
        }
    
        public void sayHello() {
            System.out.println("Hello "+name);
        }
    
        public static Service getProxyInstance(CglibProxy myProxy, String name) {
            Enhancer en = new Enhancer();
            // 设置父类和回调
            en.setSuperclass(Service.class);
            en.setCallback(myProxy);
            // 调用其构造函数,须要传入相应的Class列表和參数Object列表
            return (Service) en.create(new Class[] {String.class}, new Object[] {name});
        }
    }

    而Main函数中也应该这样使用该对象:

    package com.abs.testcglib;
    
    public class Main {
        public static void main(String[] args) {
            Service s = new Service("Sxf");
            s.sayHello();
    
            Service s2 = Service.getProxyInstance(new CglibProxy(), "Sxf");
            s2.sayHello();
        }
    }

    我们发现,两种方式创建出的对象,使用上差点儿一样,唯一不同的就是构造函数时,我们进行了部分改动,其余部分,不影响我们的对象正常传递。存储等功能。

    终于效果:
    终于效果

    三、 Spring AOP 的实现

    事实上看来刚才CGLIB的实现。再看著名的Spring框架。就会发现两者的实现方式差点儿全然一样,仅仅只是Spring框架多添加了一些概念和功能。

    以下我们写一个Target 类。这是一个被代理的目标对象,当中有一个execute()方法。如今使用 AOPexecute()方法做日志输出。在运行execute()方法前。做日志输出。

    public class Target {
        public void execute(String name){
            System.out.println("executeMethod is here" + name);
        }
    }

    通知能够拦截目标对象的 execute()方法,并运行日志输出。创建通知的代码例如以下:

    public class LoggerExecute implements MethodInterceptor {
        public Object invoke(MethodInvocation arg0) throws Throwable {
            before();
            arg0.proceed();
            return null;
        }
        private void before() {
            System.out.println("executeMethod is exe!");
        }
    }

    创建代理的方法也差点儿一样:

    public static void main(String[] args) {
        //创建目标对象
        Target target = new Target();
        //创建代理
        ProxyFactory di=new ProxyFactory();
        di.addAdvice(new BeforeExecute());
        di.setTarget(target);
        Target proxy=(Target)di.getProxy();
        //代理运行execute()方法
        proxy.execute(" ni hao");
    }

    当然Spring的切入点和其配置文件关联十分紧密。用Spring框架能够将系统的很多其它固定參数丢到配置文件里去,或者直接使用注解也能够。

  • 相关阅读:
    虚拟机设置静态ip
    hadoop2.5重新编译问题
    python(2)-函数相关
    python(1)
    python中raw_input输入数字问题
    多线程-AsyncTask
    Handler消息处理机制
    01-Java多线程
    01.JS基本语法-数据类型和变量
    (4)RecyclerVIew
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5086568.html
Copyright © 2011-2022 走看看