最爱Java

书山有路勤为径,学海无涯苦作舟

     摘要: 一. 捕获方法调用         使用call(Signature)切入点。其语法:          pointcut <pointcut name>(<any values to be picked u...  阅读全文
posted @ 2008-07-03 22:17 Brian 阅读(1510) | 评论 (2)编辑 收藏
一. 安装AspectJ
        从最简单的方面来说,AspectJ安装很简单:
         1.从http://www.aspectj.org下载最新版本(目前版本是1.6.0)。
         2.通过双击下载下来的JAR来安装。其默认安装目录为asprctj1.6目录。
         3.可在安装目录的bin目录下调用ajc命令查看帮助。
         4.使用AspectJ只需复制aspectjrt.jar即可。

二. 第一个简单的方面
        
简单的业务逻辑Java类
package com.oreilly.aspectjcookbook;

public class MyClass {
    
public void foo(int number , String name) {
        System.out.println(
"Inside foo(int , String)");
    }

    
    
public static void main(String[] args) {
        
//Create an instance of MyClass
        MyClass myObject = new MyClass();
        
//Make the call to foo
        myObject.foo(1 , "Russ Miles");
    }

}

        AspectJ的简单的HelloWorld方面
package com.oreilly.aspectjcookbook;

public aspect HelloWorld {
    pointcut callPointcut() :
        call(
void com.oreilly.aspectjcookbook.MyClass.foo(int,String));
    
    
    before() : callPointcut() 
{
        System.out.println(
"Hello World");
        System.out.println(
"In the advice attached to the call pointcut");
    }

}

将上述两个文件保存在同一目录中,运行ajc命令,编译这两个文件,并产生方面和类的.class文件。
        ajc -classpath %MY_CLASSPATH% -d %MY_DESTINATION_DIRECTORY% com/oreilly/aspectjcookbook/MyClass.java com/oreilly/aspectjcookbook/HelloWorld.java
在使用上述命令过程中,需要确保aspectjrt.jar在你的类路径中。
ajc编译器会将产生两个.class文件:MyClass.class和HelloWorld.class。并可通过正常的java命令来运行:
        java -classpath %MY_CLASSPATH% com.oreilly.aspectjcookbook.MyClass
可得到如下结果:
        Hello World
        In the advice attached to the call pointcut  
        Inside foo(int , String)
是不是很简单呢?现在我们来分析一下方面的每一行的含义:
 1package com.oreilly.aspectjcookbook;
 2
 3public aspect HelloWorld {
 4    pointcut callPointcut() :
 5        call(void com.oreilly.aspectjcookbook.MyClass.foo(int,String));
 6    
 7    
 8    before() : callPointcut() {
 9        System.out.println("Hello World");
10        System.out.println("In the advice attached to the call pointcut");
11    }

12}


 第3行声明了一个方法。
 第4行和第5行声明单一命名的切入点的逻辑。切入点逻辑指定了应用程序中的任何连接点,本例中会捕获对void com.oreilly.aspectjcookbook.MyClass.foo(int,String)方法的调用。切入点被命名为callPointcut(),使得可以在方面的作用域内的任意位置都可以引用它。
 第8行到11行声明单一通知块。before()通知只是简单地指出它将在任何被callPointcut()切入点匹配的连接点之前执行。
注意:除了.java可作为后缀名以外,.aj也可以作为后缀名使用。ajc工具都会编译所提供的文件。两者没有区别,只是个人喜好而已。

三. 编译一个方面和多个Java文件
    
如果需要多个文件,那么按上述方法编译是一件痛苦的事情。好在我们可以编写一个AspectJ配置构建文件。配置构建文件的后缀名为.lst,其中包含了所有在编译中需要使用的类文件和方面的名称。如:
        //File in file.lst
        com/oreilly/aspectjcookbook/MyClass.java
        com/oreilly/aspectjcookbook/MyAspect.java
        com/oreilly/aspectjcookbook/AnotherClass.java
        com/oreilly/aspectjcookbook/AnotherAspect.java
然后使用如下命令编译:
        ajc -argfile file.lst -classpath %MY_CLASSPATH% -d %MY_DESTINATION_DIRECTORY%

四. 织入方面到jar中
    1.首先编译MyClass.java并打包到MyApp.jar中
        java -classpath %MY_CLASSPATH% -d %MY_DESTINATION_DIRECTORY% com/oreilly/aspectjcookbook/MyClass.java
        jar -cvf MyApp.jar com/oreilly/aspectjcookbook/MyClass.class
    2.ajc -classpath %MY_CLASSPATH% -d %MY_DESTINATION_DIRECTORY% -inpath MyApp.jar com/oreilly/aspectjcookbook/HelloWorld.java
        -inpath选项强制ajc编译器从提供的.jar文件中把Java字节码提取到-d选项所指定的目录中。然后,ajc编译器将把字节码在方面织入过程中。
    3. 上述命令并不会产生新的.jar包,如需要将方面织入到新的包中,则需要使用-ourjar选项:
        ajc -classpath %MY_CLASSPATH% -d %MY_DESTINATION_DIRECTORY% -inpath MyApp.jar -outjar MyAspectOriente的App.jar com/oreilly/aspectjcookbook/HelloWorld.java

五.其他
  aj命令可以加载时织入方面
  ajdoc则可生成Javadoc文档

六. 使用Ant构建一个AspectJ项目

<?xml version="1.0" encoding="utf-8"?>
<project basedir="." default="compile" name="test">
    
<property name="src" value="src"/>
    
<property name="build" value="build"/>
    
<taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">
        
<classpath>
            
<pathelement location="%ASPECTJ_INSTALLATION%/lib/aspectjtools.jar"/>
        
</classpath>
    
</taskdef>
    
<target name="compile">
        
<mkdir dir="${build}"/>
        
<iajc destdir="${build}" sourceroots="${src}">
            
<classpath>
                
<pathelement location="%ASPECTJ_INSTALLATION%/lib/aspectjrt.jar"/>
            
</classpath>
        
</iajc>
    
</target>
</project>

    上述代码所做的工作:
      1. 使用AspectJ任务属性定义了一个新的任务
      2. 指定aspectjtools.jar的位置
      3. 声明一个构建目标,他使用iajc任务来编译项目,这个任务反过来又依赖于aspectjrt.jarlai
posted @ 2008-06-30 23:10 Brian 阅读(840) | 评论 (0)编辑 收藏

横切关注点
        面向对象编程的基本前提就是让开发人员能够在软件中表述模块化的横切关注点(crosscutting concern)。横切关注点是跨软件特定部分使用的一种行为,通常也是一种数据。它可能是一种约束,作为软件本身的一种特征,或者只是所有类都必须执行的一种行为。

方面
        方面(aspect)是横切关注点的另一种称呼。方面提供了一种机制,利用该机制,可以用一种模块化的方式指定横切关注点。为了充分利用方面的威力,我们需要了解一些基本概念,以便用一般的方式指定和应用方面。我们必须能够:
        以模块化的方式定义方面
        动态地应用方面
        根据一组规则应用方面
        根据一种机制和一种环境,用于指定将为特定方面执行的代码
        面向方面方法提供了一组语义和语法构造来满足这些要求,使得无论编写的是哪一类软件,都可以一般地应用方面。这些构造就是通知(advice)、连接点(join point)和切入点(pointcut)。

 通知
        通知就是方面被调用时所执行的代码。通知包好自身的一组规则。这组规则规定了何时调用通知,这是与被触发的连接点相关的。

 连接点
        连接点就是可能会或者可能不会调用某个通知的应用程序内的特定点。AspectJ中支持的连接点:
        被调用方法时连接
        在方法执行期间连接
        在调用构造函数时连接
        在构造函数执行期间连接
        在方面通知执行期间连接
        在对象初始化以前连接
        在对象初始化期间连接
        在静态初始化执行期间连接
        在引用类的字段时连接
        在给类的字段赋值时连接
        在执行处理程序时连接

 切入点
        切入点是用于声明连接点中关注AspectJ机制,用来发起一份通知。

public class MyClass
{
  
public void foo(int number,String name)
  
{
    System.out.println(
"Inside foo(int,String)");
  }


  
public static void main(String[] args)
  
{
    
//Create an instance of MyClass
    MyClass myObject = new MyClass();
    
//Make the call to foo
    myObject.foo(1 , "Russ Miles");
   }

}

        上述类中每一条语句都可看做是潜在的连接点。而下述类中则分别申明了切入点和通知。

public aspect Some Aspect
{
  
//A Pointcut declaration
  pointcut somePointcut():<pointcut logic>;

  
//A block of Advise
  before:somePointcut()
  
{
    
//Do something
  }

}
posted @ 2008-06-22 23:34 Brian 阅读(474) | 评论 (0)编辑 收藏
一.归并排序的思路
        ①把 n 个记录看成 n 个长度为 l 的有序子表;
        ②进行两两归并使记录关键字有序,得到 n/2 个长度为 2 的有序子表; 
        ③重复第②步直到所有记录归并成一个长度为 n 的有序表为止。
二.归并排序算法实例
        对于归并排序算法这类的分治算法,其核心就是"分解"和"递归求解"。对于"分解"实例,会在下面分析msort()方法中给出。我们先看合并的过程。
        以下面描述的序列为例,在索引范围内[first , last)的序列还有九个整数元素,它由索引范围为[first , mid]的四个元素有序子列表A和索引范围[mid , last]的五个元素有序子列表B组成。


        步骤1:比较arr[indexA]=7与arr[indexB]=12。将较小的元素7复制到数组tempArr的索引indexC处。并将indexA和indexC都指向下一个位置。


        步骤2:比较arr[indexA]=10与arr[indexB]=12。将较小的元素10复制到数组tempArr的索引indexC处。并将indexA和indexC都指向下一个位置。

        步骤3:比较arr[indexA]=19与arr[indexB]=12。将较小的元素12复制到数组tempArr的索引indexC处。并将indexB和indexC都指向下一个位置。


        步骤4-7:依次成对比较两子表的元素将17,19,21,25复制到数组tempArr。此时,indexA到达子表A的未尾(indexA = mid),indexB引用的值为30。

        步骤8-9:将未到尾部的子表剩余数据复制到tempArr中。

        步骤10:将tempArr复制到原始数据arr中。

三.归并排序算法的实现
    了解了合并过程,那么理解下面的代码并不是一件难事。下面提供了归并算法的非泛型版本和泛型版本。
public class MergeSort {
    
    
public static void sort(Object[] arr) {
        
//create a temporary array to store partitioned elements
        Object[] tempArr = arr.clone();

        
//call msort with arrays arr and tempArr along
        
//with the index range
        msort(arr, tempArr, 0, arr.length);
    }


    
public static <extends Comparable<? super T>> void sort(T[] arr) {
        
//create a temporary aray to store partitioned elements
        T[] tempArr = (T[]) arr.clone();

        
//call msort with arrays arr and tempArr along
        
//with the index range
        msort(arr, tempArr, 0, arr.length);
    }


    
private static void msort(Object[] arr, Object[] tempArr, int first,
                              
int last) {
        
//if the sublist has more than 1 element continue
        if (first + 1 < last) {
            
//for sublists of size 2 or more, call msort()
            
//for the left and right sublists and than
            
//merge the sorted sublists using merge()
            int midpt = (last + first) / 2;

            msort(arr, tempArr, first, midpt);
            msort(arr, tempArr, midpt, last);

            
//if list is already sorted, just copy src to
            
//dest; this is an optimization that results in faster
            
//sorts for nearly ordered lists
            if (((Comparable) arr[midpt - 1]).compareTo(arr[midpt]) <= 0)
                
return;
            
//the elements in the ranges [first,mid] and
            
//[mid,last] are ordered;merge the ordered sublists
            
//into an ordered sequence in the range [first , last]
            
//using the temporary array
            int indexA, indexB, indexC;

            
//set indexA to scan sublist A with rang [first , mid]
            
//and indexB to scan sublist B with rang [mid , last]
            indexA = first;
            indexB 
= midpt;
            indexC 
= first;

            
//while both sublists are not exhausted, compare
            
//arr[indexA] and arr[indexB]; copy the smaller
            
//to tempArr
            while (indexA < midpt && indexB < last) {
                
if (((Comparable) arr[indexA]).compareTo(arr[indexB]) < 0{
                    tempArr[indexC] 
= arr[indexA]; //copyto tempArr
                    indexA++//increment indexA
                }
 else {
                    tempArr[indexC] 
= arr[indexB]; //copyto tempArr
                    indexB++//increment indexB
                }

                indexC
++//increment indexC
            }

            
//copy the tail of the sublist that is not exhausted
            while (indexA < midpt) {
                tempArr[indexC
++= arr[indexA++]; //copy to tempArr
            }
 while (indexB < last) {
                tempArr[indexC
++= arr[indexB++]; //copy to tempArr
            }

            
//copy elements form temporary array to original array
            for (int i = first; i < last; i++)
                arr[i] 
= tempArr[i];
        }

    }

}
        
        上述代码中最核心的msort()方法是一递归算法。下图说明了msort()方法中子列表的分割与合并。    

四.归并排序算法的效率
        归并排序的最坏情况与平均情况运行时间都为O(nlog2n)。假定数组具有n=2k个元素。如下图:
         
        在层数0上对msort()方法的第一个调用会产生两个递归调用,这两个递归调用产生长度为n/2的两个半部分列表,而merge()方法将上述两个半部分列表组合的一个有序的n元素列表;在层数1上存在两个msort()方法的调用,每个调用又会产生另外两个对长度为n/4的列表的递归调用。每个合并会将两个长度为n/4的子列表连接为一个长度为n/2的有序列表;在层数2上存在对merge()方法的4=22个调用,每个调用会创建一个长度为n/4的有序列表。通常,在层数i上存在对merge()方法的2i个调用,每个调用会创建一个长度为n/2i的有序子列表。
        层数0:存在对merge()方法的1=20次调用。这个调用对n个元素排序。
        层数1:存在对merge()方法的2=21次调用。这个调用对n/2个元素排序。
        层数2:存在对merge()方法的4=22次调用。这个调用对n/4个元素排序。
        ......
        层数i:存在对merge()方法的2i次调用。这个调用对n/i个元素排序。
        在树中的每一层,合并涉及具有线性运行时间的n/2i个元素,这个线性运行时间需要少于n/2i次的比较。在层数i上组合的2i个合并操作需要少于2i*n/2i=n次的比较。假定n=2k,分割过程会在n/2k=1的k层数上终止。那么所有层上完成的工作总量为:k*n = nlog2n。因此msort()方法的最坏情况效率为O(nlog2n)。
posted @ 2008-06-13 00:54 Brian 阅读(2290) | 评论 (3)编辑 收藏
一.插入排序算法的思路
        
假定这个数组的序是排好的,然后从头往后,如果有数比当前外层元素的值大,则将这个数的位置往后挪,直到当前外层元素的值大于或者等于它前面的位置为止。
二.插入排序算法实例
        用五个名字(Monroe,Chin,Flores,Stein和Dare)的列表的插入排序算法为例:
                                       Monroe    从Monroe开始

        处理名字Chin        Chine  Monroe    将Chin插入到位置0;Monroe移动至位置1

        处理名字Flores     Chine  Flores  Monroe    将Flores插入到位置1;Monroe移动至位置2

        处理名字Stein       Chine  Flores  Monroe  Stein    Stein位置正确 

        处理名字Dare       Chine  Dare  Flores  Monroe  Stein    将Dare插入在位置1;列表尾部向右移动 

三.插入排序算法的实现
public class InsertSort {
    
//sort an array of elements using insertion sort

    public static <extends Comparable<? super T>> void sort(T[] arr) {
        
int i, j, n =
 arr.length;
        T target;

        
/**
         * place element at index i into the sublist
         * from index 0 to i-1 where 1<= i,
         * so it is in the correct positon
         
*/

        
for (i = 1; i < n; i++{
            
//
index j scans down list from index i looking for
            
//
correct position to locate target; assigns it to
            
//arr at index j

            j = i;
            target 
=
 arr[i];
            
//
locate insertion point by scanning downward as long
            
//
as target < arr[j] and we have not encountered the
            
//beginning of the array

            while (j > 0 && target.compareTo(arr[j - 1]) < 0{
                
//shift elements up list to make room for insertion

                arr[j] = arr[j - 1];
                j
--
;
            }

            
//the location is found;insert target
            arr[j] = target;
        }

    }

}

四.插入排序算法的效率
        
假定n是数组的长度,那么插入排序需要n-1遍。对于通用的遍i来说,插入操作从arr[0]到arr[i-1]的子列表中,并且需要平均i/2次比较。比较的平均总数为:
                 T(n) = 1/2 + 2/2 + 3/2 + ...... + (n-2)/2 + (n-1)/2 = n(n-1)/4
        根据T(n)的主项,插入排序算法的平均运行时间为O(n2)。最好情况为O(n),最坏情况为O(n2)。
posted @ 2008-06-11 23:56 Brian 阅读(2677) | 评论 (4)编辑 收藏
仅列出标题
共5页: 上一页 1 2 3 4 5 

公告


导航

<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

统计

常用链接

留言簿(4)

随笔分类

随笔档案

收藏夹

搜索

最新评论

阅读排行榜

评论排行榜