Spring AOP AspectJ注解示例

Spring框架推荐使用Spring AspectJ AOP实现,而不是使用Spring 1.2旧式基于DTD的AOP实现,因为它提供了更多的控制,并且更容易使用。

使用Spring AOP AspectJ实现有两种方式:

通过注解:我们将在这里学习它。
1.通过XML配置(基于schema):我们将在下一页学习它。
2.要了解AOP的概念、优势等,请访问这里的AOP概念教程。

下载所有示例(使用MyEclipse IDE开发)

Spring AspectJ AOP实现提供了许多注解:

1.@Aspect 声明类为切面。
2.@Pointcut 声明切入点表达式。

用于创建增强的注解如下:

1.@Before 声明前置通知。在调用实际方法之前应用。
2.@After 声明后置通知。在调用实际方法之后,返回结果之前应用。
3.@AfterReturning 声明返回通知。在调用实际方法之后,返回结果之前应用。但是你可以在通知中获取结果值。
4.@Around 声明环绕通知。在调用实际方法之前和之后应用。
5.@AfterThrowing 声明异常通知。如果实际方法抛出异常,则应用该通知。

理解切入点

切入点是Spring AOP的表达语言。

@Pointcut 注解用于定义切入点。我们也可以通过名称引用切入点表达式。让我们看一个简单的切入点表达式示例。

@Pointcut("execution(* Operation.*(..))")
private void doSomething() {}

切入点表达式的名称是doSomething()。它将应用于Operation类的所有方法,不管返回类型是什么。

理解切入点表达式

让我们尝试通过以下示例理解切入点表达式:

@Pointcut("execution(public * *(..))")

它将应用于所有public方法。

@Pointcut("execution(public Operation.*(..))")

它将应用于Operation类的所有public方法。

@Pointcut("execution(* Operation.*(..))")

它将应用于Operation类的所有方法。

@Pointcut("execution(public Employee.set*(..))")

它将应用于Employee类的所有public setter方法。

@Pointcut("execution(int Operation.*(..))")

它将应用于Operation类的所有返回int值的方法。

@Before示例

AspectJ的@Before通知在实际业务逻辑方法之前应用。您可以在此处执行任何操作,例如转换、认证等。
创建一个包含实际业务逻辑的类。

文件:Operation.java

package cn.javatiku;  
public  class Operation{  
    public void msg(){System.out.println("msg method invoked");}  
    public int m(){System.out.println("m method invoked");return 2;}  
    public int k(){System.out.println("k method invoked");return 3;}  
}  

现在创建包含@Before通知的切面类。

文件:TrackOperation.java

package cn.javatiku;  
  
import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.aspectj.lang.annotation.Pointcut;  
  
@Aspect  
public class TrackOperation{  
    @Pointcut("execution(* Operation.*(..))")  
    public void k(){}//pointcut name  
      
    @Before("k()")//applying pointcut on before advice  
    public void myadvice(JoinPoint jp)//it is advice (before advice)  
    {  
        System.out.println("additional concern");  
        //System.out.println("Method Signature: "  + jp.getSignature());  
    }  
}  

现在创建定义Bean的applicationContext.xml文件。

文件:applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xmlns:aop="http://www.springframework.org/schema/aop"   
       xsi:schemaLocation="http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans.xsd   
       http://www.springframework.org/schema/aop   
       http://www.springframework.org/schema/aop/spring-aop.xsd">  
  
  
    <bean id="opBean" class="cn.javatiku.Operation">   </bean>  
    <bean id="trackMyBean" class="cn.javatiku.TrackOperation"></bean>  
      
    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"></bean>  
          
</beans>  

现在,调用实际方法。

文件:Test.java

package cn.javatiku;  
  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;  
public class Test{  
    public static void main(String[] args){  
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  
        Operation e = (Operation) context.getBean("opBean");  
        System.out.println("calling msg...");  
        e.msg();  
        System.out.println("calling m...");  
        e.m();  
        System.out.println("calling k...");  
        e.k();  
    }  
}  

输出:

calling msg...  
additional concern  
msg() method invoked  
calling m...  
additional concern  
m() method invoked  
calling k...  
additional concern  
k() method invoked  

您可以看到,在调用msg(),m()和k()方法之前,额外关注点被打印出来。

如果现在将切点表达式更改为如下所示:

@Pointcut("execution(* Operation.m*(..))")  

现在,额外关注点将仅在调用以m开头的方法时应用。输出如下:

calling msg...  
additional concern  
msg() method invoked  
calling m...  
additional concern  
m() method invoked  
calling k...  
k() method invoked  

现在可以看到在调用k()方法时不会打印出额外关注点。

@After示例

AspectJ的@After通知在调用实际业务逻辑方法之后应用。它可以用于维护日志、安全性、通知等。

假设Operation.java,applicationContext.xml和Test.java文件与上面的@Before示例中给出的相同。

现在创建包含@After通知的切面类。

文件:TrackOperation.java

package cn.javatiku;  
  
import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.After;  
import org.aspectj.lang.annotation.Pointcut;  
  
@Aspect  
public class TrackOperation{  
    @Pointcut("execution(* Operation.*(..))")  
    public void k(){}//pointcut name  
      
    @After("k()")//applying pointcut on after advice  
    public void myadvice(JoinPoint jp)//it is advice (after advice)  
    {  
        System.out.println("additional concern");  
        //System.out.println("Method Signature: "  + jp.getSignature());  
    }  
}  

输出:

calling msg...  
msg() method invoked  
additional concern  
calling m...  
m() method invoked  
additional concern  
calling k...  
k() method invoked  
additional concern  

您可以看到,在调用msg(),m()和k()方法后,额外关注点被打印出来。

@AfterReturning Example

使用后返回通知,我们可以在通知中获取方法的返回结果。
创建包含业务逻辑的类。

File: Operation.java

package cn.javatiku;  
public  class Operation{  
    public int m(){System.out.println("m() method invoked");return 2;}  
    public int k(){System.out.println("k() method invoked");return 3;}  
}  

创建包含后返回通知的切面类。

File: TrackOperation.java

package cn.javatiku;  
  
import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.annotation.AfterReturning;  
import org.aspectj.lang.annotation.Aspect;  
  
@Aspect  
public class TrackOperation{  
    @AfterReturning(  
              pointcut = "execution(* Operation.*(..))",  
              returning= "result")  
                
    public void myadvice(JoinPoint jp,Object result)//it is advice (after returning advice)  
    {  
        System.out.println("additional concern");  
        System.out.println("Method Signature: "  + jp.getSignature());  
        System.out.println("Result in advice: "+result);  
        System.out.println("end of after returning advice...");  
    }  
}  

File: applicationContext.xml
与@Before示例中的内容相同。

File: Test.java
现在创建Test类来调用实际方法。

package cn.javatiku;  
  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;  
public class Test{  
    public static void main(String[] args){  
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  
        Operation e = (Operation) context.getBean("opBean");  
        System.out.println("calling m...");  
        System.out.println(e.m());  
        System.out.println("calling k...");  
        System.out.println(e.k());  
    }  
}  

输出结果:

calling m...  
m() method invoked  
additional concern  
Method Signature: int cn.javatiku.Operation.m()  
Result in advice: 2  
end of after returning advice...  
2  
calling k...  
k() method invoked  
additional concern  
Method Signature: int cn.javatiku.Operation.k()  
Result in advice: 3  
end of after returning advice...  
3  

可以看到返回值被打印了两次,一次是由TrackOperation类打印的,另一次是由Test类打印的。

4) @Around Example

AspectJ的环绕通知应用于调用实际业务逻辑方法的前后。

假设applicationContext.xml文件与@Before示例中的内容相同。

创建包含实际业务逻辑的类。

File: Operation.java

package cn.javatiku;  
public  class Operation{  
    public void msg(){System.out.println("msg() is invoked");}  
    public void display(){System.out.println("display() is invoked");}  
}  

创建包含环绕通知的切面类。

切面类需要传入ProceedingJoinPoint引用,以便我们可以通过调用proceed()方法来处理请求。

File: TrackOperation.java

package cn.javatiku;  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Pointcut;  
  
@Aspect  
public class TrackOperation  
{  
    @Pointcut("execution(* Operation.*(..))")  
    public void abcPointcut(){}  
      
    @Around("abcPointcut()")  
    public Object myadvice(ProceedingJoinPoint pjp) throws Throwable   
    {  
        System.out.println("Additional Concern Before calling actual method");  
        Object obj=pjp.proceed();  
        System.out.println("Additional Concern After calling actual method");  
        return obj;  
    }  
}  

File: Test.java
现在创建Test类来调用实际方法。

package cn.javatiku;  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;  
public class Test{  
    public static void main(String[] args){  
        ApplicationContext context = new classPathXmlApplicationContext("applicationContext.xml");  
          
        Operation op = (Operation) context.getBean("opBean");  
        op.msg();  
        op.display();  
    }  
}  

输出结果:

Additional Concern Before calling actual method  
msg() is invoked  
Additional Concern After calling actual method  
Additional Concern Before calling actual method  
display() is invoked  
Additional Concern After calling actual method  

可以看到在调用msg()和display()方法前后都打印了额外的关注点。

@AfterThrowing Example

使用后抛出通知,我们可以在TrackOperation类中打印异常。让我们看一个AspectJ AfterThrowing通知的示例。
创建包含业务逻辑的类。

File: Operation.java

package cn.javatiku;  
public  class Operation{  
    public void validate(int age)throws Exception{  
    if(age<18){  
        throw new ArithmeticException("Not valid age");  
    }  
    else{  
        System.out.println("Thanks for vote");  
    }  
    }  
      
}  

创建包含后抛出通知的切面类。

在这里,我们还需要传入Throwable引用,以便在这里拦截异常。

File: TrackOperation.java

package cn.javatiku;  
import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.annotation.AfterThrowing;  
import org.aspectj.lang.annotation.Aspect;  
@Aspect  
public class TrackOperation{  
    @AfterThrowing(  
              pointcut = "execution(* Operation.*(..))",  
              throwing= "error")  
                
    public void myadvice(JoinPoint jp,Throwable error)//it is advice  
    {  
        System.out.println("additional concern");  
        System.out.println("Method Signature: "  + jp.getSignature());  
        System.out.println("Exception is: "+error);  
        System.out.println("end of after throwing advice...");  
    }  
}  

File: applicationContext.xml
与@Before示例中的内容相同。

File: Test.java
现在创建Test类来调用实际方法。

package cn.javatiku;  
  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;  
public class Test{  
    public static void main(String[] args){  
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  
        Operation op = (Operation) context.getBean("opBean");  
        System.out.println("calling validate...");  
        try{  
            op.validate(19);  
        }catch(Exception e){System.out.println(e);}  
        System.out.println("calling validate again...");  
          
        try{  
            op.validate(11);  
        }catch(Exception e){System.out.println(e);}  
    }  
}  

输出结果:

calling validate...  
Thanks for vote  
calling validate again...  
additional concern  
Method Signature: void cn.javatiku.Operation.validate(int)  
Exception is: java.lang.ArithmeticException: Not valid age  
end of after throwing advice...  
java.lang.ArithmeticException: Not valid age  

可以看到,在调用validate方法时,如果年龄不符合条件,将会抛出异常,并在TrackOperation类中打印出相应的信息。

标签: spring, Spring教程, Spring技术, Spring语言学习, Spring学习教程, Spring下载, Spring框架, Spring框架入门, Spring框架教程, Spring框架高级教程, Spring面试题, Spring笔试题, Spring编程思想