AOP的基本概念理解

本文部分引用了网络资源,仅为学习,如有冒犯,请见谅。

1.1  相关概念

1、AOP技术起源(转)

AOP技术的诞生并不算晚,早在1990年开始,来自Xerox Palo Alto Research Lab(即PARC)的研究人员就对面向对象思想的局限性进行了分析。他们研究出了一种新的编程思想,借助这一思想或许可以通过减少代码重复模块从而帮助开发人员提高工作效率。随着研究的逐渐深入,AOP也逐渐发展成一套完整的程序设计思想,各种应用AOP的技术也应运而生。

AOP技术在Java平台下是最先得到应用的。就在PARC对于面向方面编程进行研究的同时,美国Northeastern University的博士生Cristina Lopes和其同事也开始了类似的思考。最终,美国国防先进技术研究计划署(Defense Advanced Research Projects Agency即DARPA)注意到了这项工作,并提供了科研经费,鼓励将二者的工作成果结合起来。他们通过定义一套Java语言的扩展系统,使开发者可以方便的进行面向方面的开发,这套扩展系统被称为AspectJ。之后,AspectJ在2002年被转让给Eclipse Foundation,从而成为在开源社区中AOP技术的先锋,也是目前最为流行的AOP工具。

AspectWerkz则是基于Java的动态的、轻量级AOP框架。AspectWerkz仍然是开源社区中的产品,由BEA System提供赞助,开发者则是BEA的两名员工Jonas Bonér和Alexandre Vasseur。最近版本是AspectWerkz 2.0。2005年1月,AspectJ和AspectWerkz达成协议,同意将二者的成果综合到一起,取其精华创建一个单一的工具。他们合作的第一个发布版本为AspectJ 5,它扩展了AspectJ语言,以支持基于Annotation开发风格而又支持类似AspectJ代码风格。AspectJ 5也为Java 5的语言特性提供完全的AOP支持。

在Java阵营中,商用软件制造商JBoss在其2004年推出的JBoss 4.0中,引入了AOP框架和组件。在JBoss 4.0中,用户可以在JBoss应用服务器外部单独使用JBoss AOP,该版本为JBoss AOP 1.0,是在2004年10月发布的。在2005年,JBoss AOP框架又发布了1.3.0版本,新版本对加载期织入(Weev)和切点(point cut)匹配的性能做了很大的优化,使应用程序的启动时间大大缩短。

作为轻型的Framework,Spring在开发轻量级的J2EE时,应用是非常广泛的。它通过IoC模式(Inversion of Control,控制反转模式)来实现AOP,通常被称为Spring AOP。在2004年,被作为Spring框架的扩展而发布。Spring AOP作为一种非侵略性的,轻型的AOP框架,开发者无需使用预编译器或其他的元标签,在Java程序中应用AOP。目前,AOP的功能完全集成到了Spring事务管理、日志和其他各种特性的上下文中。

在.Net的阵营中,AOP技术的应用远不如Java阵营对AOP的关注。2005年1月,微软发布的Enterprise Library提供了7种不同的“应用程序块(application blocks)”。有个别专家认为,这些组件可以被认为是方面。但该观点并未得到一致的认同。事实上,在.Net平台下,推动AOP技术发展的原动力并非微软,而是开源社区。虽然,微软的技术专家们亦然听到了在.Net Framework中增加AOP技术的群众呼声,但作为如此巨大的软件公司,要让它灵活地转变战略方向,显然是不太现实的。正因为此,才赐予了开源社区在AOP技术的研究与探索上一个巨大的发展空间。

与Java阵营中的AOP技术不同,目前在.Net平台下的各种AOP工具,基本上还停留在实验室阶段。但一些在技术上领先且逐渐成熟的AOP产品,也在开源社区中渐露峥嵘。这其中主要包括Aspect#,AspectDNG,Eos AOP等。

Aspect#是基于Castle动态代理技术来实现的。Castle源于Apache Avalon项目,其目的在于实现一个轻量级的IoC容器。Aspect#于2005年6月被收录为Castle的其中一个子项目。它是针对CLI(.Net和Mono)实现的AOP框架,利用了反射、代理等机制。目前的Aspect#版本为2.1.1。

AspectDNG目前的版本为0.7,仍然处于beta版的阶段。它的实现技术是基于rail的静态织入。Rail属于IL级别下的代码织入,它自定义的一套xml格式的ILML语言,能够将原有的程序集拆散成ILML格式,以便于对静态程序集进行修改和扩展,从而达到静态织入的目的。因为AspectDNG是属于IL级别下的代码织入,因此在.Net平台下,并不受具体的编程语言限制。

Eos AOP与AspectDNG一样,仍然采用静态织入的方式,但从语法定义上,它更近似于AspectJ关于AOP的实现。它扩展了C#语法,引入了aspect、introduce、before、after等关键字,并且提供了专用的Eos编译器。Eos项目是于2004年9月开始启动,2005年6月推出的0.3.3版本为最新版本,主要的开发人员为Hridesh Rajan和Kevin Sullivan。前者为Virginia大学计算机系的研究生,Eos项目最初是由Hridesh Rajan提出的;而后者则为该计算机系的副教授(Associate Professor)。所以自Eos诞生之初,就带有浓厚的学院派特色。

从AOP技术的整体发展来看,高性能、稳定、可扩展、易用的AOP框架是其趋势与目标。从上述对各种AOP技术的分析来看,AOP技术无疑是具有共同特点的,而各种实现技术就是围绕着这些共性深入与延伸。接下来,我将概要地介绍AOP的本质,以及它的技术要素。

2、  AOP

AOP是设计思想,一个规范,本身并没有设定具体语言的实现,是对oop的一个扩展。简单地说,AOP就是将那些与业务无关,却为业务模块所共同需要的功能(或者逻辑,比如权限认证、日志、事务处理)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。从业务上说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。

3、  AOP能做哪些事情

l  性能监控:在方法调用前后记录调用时间,方法执行太长或超时报警。

l  缓存代理:缓存某方法的返回值,下次执行该方法时,直接从缓存里获取。

l  软件破解:使用AOP修改软件的验证类的判断逻辑。

l  工作流系统:工作流系统需要将业务代码和流程引擎代码混合在一起执行,可以使用AOP将其分离,并动态挂接业务。

l  权限验证:方法执行前验证是否有权限执行当前方法。

l  事物管理

1.2  实现方法(与spring无关)

1.2.1  静态aop(静态织入,静态代理)

原理:在编译期,切面直接以字节码形式编译到目标字节码文件中。也就是说,aop的处理逻辑直接体现在编译后的class文件中。这种实现方式有静态代理方式、aspectj

优点:对系统性能无影响; 

缺点:不够灵活;

静态代理方式的代码示例:

1、  定义接口:

/**

 * 抽象主题角色:声明了真实主题和代理主题的共同接口。

 *

 * @author yanbin

 *

 */

public interface ITalk {

 

    public void talk(String msg);

 

}

2、  定义真实的实现类:

/**

 * 真实主题角色:定义真实的对象。

 *

 * @author yanbin

 *

 */

public class PeopleTalkimplementsITalk {

 

    public String username;

    public String age;

 

    public PeopleTalk(String username, String age) {

       this.username = username;

       this.age = age;

    }

 

    public void talk(String msg) {

       System.out.println(msg + "!你好,我是" + username + ",我年龄是" + age);

    }

 

    public String getName() {

       return username;

    }

 

    public void setName(String name) {

       this.username = name;

    }

 

    public String getAge() {

       return age;

    }

 

    public void setAge(String age) {

       this.age = age;

    }

 

}

3、  定义代理类:

/**

 * 代理主题角色:内部包含对真实主题的引用,并且提供和真实主题角色相同的接口。

 *

 * @authoryanbin

 *

 */

public class TalkProxy implements ITalk {

 

    private ITalk talker;

 

    publicTalkProxy(ITalk talker) {

        //super();

       this.talker = talker;

    }

 

    public voidtalk(String msg) {

        //dosome other job like loging

        talker.talk(msg);

      //do some other job

    }

 

}

4、  测试类:

/**

 * 代理测试类,使用代理

 *

 * @authoryanbin

 *

 */

public class ProxyPattern {

 

    publicstatic void main(String[] args) {

        // 不需要执行额外方法的。

        ITalkpeople = new PeopleTalk("AOP", "18");

       people.talk("No ProXY Test");

       System.out.println("-----------------------------");

        // 需要执行额外方法的(切面)

       TalkProxy talker = new TalkProxy(people);//使用代理类代理原始类来使用

       talker.talk("ProXY Test", "代理");

    }

 

}

1.2.2  动态aop(包括动态代理、动态字节码生成、自定义类加载器、字节码转换)

1.2.2.1  动态代理 (java动态代理)

原理:在运行期,目标类加载后,为接口动态生成代理类。将切面织入到代理类中。java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。只能对实现了接口的类进行代理。

优点:更灵活;

缺点:切入的关注点要实现接口(即目标类要基于接口实现)

实现方式参见1.5 动态代理。

1.2.2.2  动态字节码生成(cglib)

原理:在运行期,目标类加载后,动态构建字节码文件生成目标类的子类,将切面逻辑加入到子类中。

cglib(CodeGeneration Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。cglib封装了asm,可以在运行期动态生成新的class。cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。

优点:没有接口也可以织入;

缺点:扩展类的实例方法为final时,无法进行织入; 

示例代码:

1、创建业务类:

public class People {
public void talk(String msg) {
       System.out.println("people talk" + msg);
   }
}

2、创建cglib代理类:

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;


public class CglibProxy implements MethodInterceptor {
    private Object target;
    /**
     * 创建代理对象
     * 
     * @param target
     * @return
     */
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }


    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result = null;
        System.out.println("方法开始执行");
        result = methodProxy.invokeSuper(proxy, args);
        System.out.println("事方法执行解锁");
        return result;
    }

}

3、测试类:

public class Test {
public static void main(String[] args){

People people = (People) new CglibProxy().getInstance(new People());
       people.talk("业务方法");
}
}

执行结果如下:

事物开始
people talk业务方法
事物结束

1.2.2.3  自定义类加载器

原理:在运行期,目标加载前,将切面逻辑加到目标字节码里;

优点:可以对绝大部分类进行织入;

缺点:代码中若使用了其它类加载器,则这些类将不会被织入; 

1.2.2.4  字节码转换

原理:在运行期,所有类加载器加载字节码前进行拦截;

优点:可以对所有类进行织入;

缺点:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章