标签:
http://examples.javacodegeeks.com/enterprise-java/spring/aop/spring-aop-aspectj-example/
http://oss.org.cn/ossdocs/framework/spring/zh-cn/aop.html
面向方面编程 (AOP) 提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。 面向对象将应用程序分解成 各个层次的对象,而AOP将程序分解成各个方面 或者说 关注点 。 这使得可以模块化诸如事务管理等这些横切多个对象的关注点。(这些关注点术语称作 横切关注点。)
Spring的一个关键组件就是AOP框架。 Spring IoC容器(BeanFactory 和ApplicationContext)并不依赖于AOP, 这意味着如果你不需要使用,AOP你可以不用,AOP完善了Spring IoC,使之成为一个有效的中间件解决方案,。
AOP在Spring中的使用:
提供声明式企业服务,特别是作为EJB声明式服务的替代品。这些服务中最重要的是 声明式事务管理,这个服务建立在Spring的事务管理抽象之上。
允许用户实现自定义的方面,用AOP完善他们的OOP的使用。
这样你可以把Spring AOP看作是对Spring的补充,它使得Spring不需要EJB就能提供声明式事务管理;或者 使用Spring AOP框架的全部功能来实现自定义的方面。
各种通知类型包括:
Around通知: 包围一个连接点的通知,如方法调用。这是最强大的通知。Aroud通知在方法调用前后完成自定义的行为。它们负责选择继续执行连接点或通过 返回它们自己的返回值或抛出异常来短路执行。
Before通知: 在一个连接点之前执行的通知,但这个通知 不能阻止连接点前的执行(除非它抛出一个异常)。
Throws通知: 在方法抛出异常时执行的通知。Spring提供强类型的Throws通知,因此你可以书写代码捕获感兴趣的异常(和它的子类),不需要从Throwable 或Exception强制类型转换。
Around通知是最通用的通知类型。大部分基于拦截的AOP框架,如Nanning和JBoss4,只提供 Around通知。
如同AspectJ,Spring提供所有类型的通知,我们推荐你使用最为合适的通知类型来实现需 要的行为。例如,如果只是需要用一个方法的返回值来更新缓存,你最好实现一个after returning 通知而不是around通知,虽然around通知也能完成同样的事情。使用最合适的通知类型使编程模型变 得简单,并能减少潜在错误。例如你不需要调用在around通知中所需使用的的MethodInvocation的 proceed()方法,因此就调用失败。
切入点的概念是AOP的关键,使AOP区别于其它使用拦截的技术。切入点使通知独立于OO的 层次选定目标。例如,提供声明式事务管理的around通知可以被应用到跨越多个对象的一组方法上。 因此切入点构成了AOP的结构要素。
建议使用Maven来新建项目,从而很容易解决这些第三方类库直接的依赖关系。
使用Maven的pom.xml代码如下。
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 3 <modelVersion>4.0.0</modelVersion> 4 <groupId>com.javacodegeeks.snippets.enterprise</groupId> 5 <artifactId>springexample</artifactId> 6 <version>0.0.1-SNAPSHOT</version> 7 8 <dependencies> 9 <dependency> 10 <groupId>org.springframework</groupId> 11 <artifactId>spring-core</artifactId> 12 <version>${spring.version}</version> 13 </dependency> 14 15 <dependency> 16 <groupId>org.springframework</groupId> 17 <artifactId>spring-context</artifactId> 18 <version>${spring.version}</version> 19 </dependency> 20 21 <dependency> 22 <groupId>org.springframework</groupId> 23 <artifactId>spring-aop</artifactId> 24 <version>3.2.4.RELEASE</version> 25 </dependency> 26 27 <dependency> 28 <groupId>aspectj</groupId> 29 <artifactId>aspectjrt</artifactId> 30 <version>1.5.4</version> 31 </dependency> 32 33 <dependency> 34 <groupId>aspectj</groupId> 35 <artifactId>aspectjweaver</artifactId> 36 <version>1.5.4</version> 37 </dependency> 38 39 </dependencies> 40 41 <properties> 42 <spring.version>3.2.3.RELEASE</spring.version> 43 </properties> 44 </project>
为了演示,我们先需要创建一个业务类bean.通常我们先建一个借口类SimpleService.java,然后我们写另外一个实现类
SimpleServiceImpl.java
. 这个simpleService Bean会含有几个方法,其中有一个方法我们用AspectJ进行拦截。
SimpleSrvice.java
1 package com.rubyyu.enterprise; 2 3 public interface SimpleService { 4 public void printNameId(); 5 public void checkName(); 6 public String sayHello(String message); 7 }
SimpleServiceImpl.java
1 package com.rubyyu.enterprise.impl; 2 3 import com.rubyyu.enterprise.SimpleService; 4 5 public class SimpleServiceImpl implements SimpleService { 6 7 private String name; 8 9 private int id; 10 11 public String getName() { 12 return name; 13 } 14 15 public void setName(String name) { 16 this.name = name; 17 } 18 19 public int getId() { 20 return id; 21 } 22 23 public void setId(int id) { 24 this.id = id; 25 } 26 27 public void printNameId() { 28 System.out.println("SimpleService : Method printNameId() : My name is " + name 29 + " and my id is " + id); 30 } 31 32 public void checkName() { 33 if (name.length() < 20) { 34 throw new IllegalArgumentException(); 35 } 36 } 37 38 public String sayHello(String message){ 39 System.out.println("SimpleService : Method sayHello() : Hello! " + message); 40 return message; 41 } 42 }
接下来我们用注解形式通过AspectJ对bean的方法进行拦截。方面(Aspects)是有方法和作用域的java 类,方面可能会包括切点(pointcut),通知(advice)和引入(introduction: 添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存). 为了把一个类定义为一个切面,我们需要使用@Aspect注解。对应的通知注解有
在切面里面@Before注解是用于定义一个before通知的。Before通知是在一个连接点之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。切面的切入点表达式必须同连接点匹配才能使得通知的动作生效。在我们的例子中,切入点表达式声明了sayHello()方法需要被拦截com.javacodegeeks.snippets.enterprise.SimpleService.sayHello(..)
DoBeforeAspect.java
1 package com.rubyyu.enterprise.aspect; 2 3 import org.aspectj.lang.JoinPoint; 4 import org.aspectj.lang.annotation.Aspect; 5 import org.aspectj.lang.annotation.Before; 6 7 @Aspect 8 public class DoBeforeAspect { 9 10 @Before("execution(* com.rubyyu.enterprise.SimpleService.sayHello(..))") 11 public void doBefore(JoinPoint joinPoint) { 12 13 System.out.println("***AspectJ*** DoBefore() is running!! intercepted : " + joinPoint.getSignature().getName()); 14 } 15 16 }
切面以及simpleServiceBean的定义都需要在applicationContext-annotation.xml中完成。为了使得@Aspect注解生效,我们必须在xml配置文件中加入<aop:aspectj-autoproxy>
元素.
applicationContext-annotation.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"> <aop:aspectj-autoproxy /> <bean id="simpleServiceBean" class="com.rubyyu.enterprise.impl.SimpleServiceImpl"> <property name="name" value="Ruby" /> <property name="id" value="1314" /> </bean> <bean id="doBeforeAspect" class="com.rubyyu.enterprise.aspect.DoBeforeAspect" /> </beans>
然后我们便向一个测试类App.java来调用simpleServiceBean的方法。
App.java
1 package com.rubyyu.enterprise; 2 3 import org.springframework.context.ConfigurableApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 public class App { 7 8 public static void main(String[] args) { 9 10 ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-annotation.xml"); 11 SimpleService simpleService = (SimpleService) context.getBean("simpleServiceBean"); 12 simpleService.printNameId(); 13 System.out.println("---------------"); 14 try{ 15 simpleService.checkName(); 16 } catch(Exception e){ 17 System.out.println("SimpleService checkName() : Exception thrown.."); 18 } 19 System.out.println("---------------"); 20 simpleService.sayHello("美女!"); 21 System.out.println("---------------"); 22 context.close(); 23 } 24 }
输出结果如下
SimpleService : Method printNameId() : My name is Ruby and my id is 1314 --------------- SimpleService checkName() : Exception thrown.. --------------- ***AspectJ*** DoBefore() is running!! intercepted : sayHello SimpleService : Method sayHello() : Hello! 美女! ---------------
切面中的@After
注解是用于定义After通知。它是在一个连接点之后执行的通知,但这个通知不能阻止连接点之前的其他执行(除非它抛出一个异常)。
DoAfterAspect.java
1 package com.rubyyu.enterprise.aspect; 2 3 import org.aspectj.lang.JoinPoint; 4 import org.aspectj.lang.annotation.Aspect; 5 import org.aspectj.lang.annotation.After; 6 7 @Aspect 8 public class DoAfterAspect { 9 10 @After("execution(* com.rubyyu.enterprise.SimpleService.sayHello(..))") 11 public void doAfter(JoinPoint joinPoint) { 12 13 System.out.println("***AspectJ*** DoAfter() is running!! intercepted : " + joinPoint.getSignature().getName()); 14 15 } 16 17 }
把新定义的bean加入到applicationContext-annotation.xml中
<bean id="doAfterAspect" class="com.rubyyu.enterprise.aspect.DoAfterAspect" />
运行结果如下
SimpleService : Method printNameId() : My name is Ruby and my id is 1314 --------------- SimpleService checkName() : Exception thrown.. --------------- ***AspectJ*** DoBefore() is running!! intercepted : sayHello ***AspectJ*** DoAfter() is running!! intercepted : sayHello ---------------
@AfterReturnning是在连接点正常完成后执行的通知, 例如,一个方法正常返回,没有抛出异常。新增加一个切面类如下
DoAfterReturningAspect.java
1 package com.rubyyu.enterprise.aspect; 2 3 import org.aspectj.lang.JoinPoint; 4 import org.aspectj.lang.annotation.Aspect; 5 import org.aspectj.lang.annotation.AfterReturning; 6 7 @Aspect 8 public class DoAfterReturningAspect { 9 10 @AfterReturning( 11 pointcut = "execution(* com.rubyyu.enterprise.SimpleService.sayHello(..))", returning= "result") 12 public void doAfterReturning(JoinPoint joinPoint, Object result) { 13 14 System.out.println("***AspectJ*** DoAfterReturning() is running!! intercepted : " + joinPoint.getSignature().getName()); 15 System.out.println("Method returned value is : " + result); 16 } 17 }
把新定义的bean加入到applicationContext-annotation.xml中
<bean id="doAfterReturningAspect" class="com.rubyyu.enterprise.aspect.DoAfterReturningAspect" />
测试结果如下
SimpleService : Method printNameId() : My name is Ruby and my id is 1314 --------------- SimpleService checkName() : Exception thrown.. --------------- ***AspectJ*** DoBefore() is running!! intercepted : sayHello ***AspectJ*** DoAfterReturning() is running!! intercepted : sayHello Method returned value is : null ***AspectJ*** DoAfter() is running!! intercepted : sayHello ---------------
@AfterThrowing是在连接点的方法发生异常之后执行的通知, 例如,我们为了测试,在simpleServiceBean里面特意抛出了IllegalArgumentException()。
1 public void checkName() { 2 if (name.length() < 20) { 3 throw new IllegalArgumentException(); 4 } 5 }
定义一个新的切面类
DoAfterThrowingAspect.java
1 package com.rubyyu.enterprise.aspect; 2 3 import org.aspectj.lang.JoinPoint; 4 import org.aspectj.lang.annotation.Aspect; 5 import org.aspectj.lang.annotation.AfterThrowing; 6 7 @Aspect 8 public class DoAfterThrowingAspect { 9 10 @AfterThrowing(pointcut = "execution(* com.rubyyu.enterprise.SimpleService.checkName(..))",throwing= "error") 11 public void doAfterThrowing(JoinPoint joinPoint, Throwable error) { 12 13 System.out.println("***AspectJ*** DoAfterThrowing() is running!! intercepted : " + joinPoint.getSignature().getName()); 14 System.out.println("Exception : " + error); 15 System.out.println("******"); 16 } 17 }
把新定义的bean加入到applicationContext-annotation.xml中
<bean id="doAfterThrowingAspect" class="com.rubyyu.enterprise.aspect.DoAfterThrowingAspect" />
测试结果如下
SimpleService : Method printNameId() : My name is Ruby and my id is 1314 --------------- ***AspectJ*** DoAfterThrowing() is running!! intercepted : checkName Exception : java.lang.IllegalArgumentException ****** SimpleService checkName() : Exception thrown.. --------------- ***AspectJ*** DoBefore() is running!! intercepted : sayHello ***AspectJ*** DoAfterReturning() is running!! intercepted : sayHello Method returned value is : null ***AspectJ*** DoAfter() is running!! intercepted : sayHello ---------------
Around通知是包围一个连接点的通知(如方法调用)。这是最强大的通知。Aroud通知在方法调用前后完成自定义的行为。它们负责选择继续执行连接点或通过,返回它们自己的返回值或抛出异常来短路执行。定义一个新的Around切面类。
DoAroundAspect.java
1 package com.rubyyu.enterprise.aspect; 2 3 import java.util.Arrays; 4 5 import org.aspectj.lang.ProceedingJoinPoint; 6 import org.aspectj.lang.annotation.Aspect; 7 import org.aspectj.lang.annotation.Around; 8 9 @Aspect 10 public class DoAroundAspect { 11 12 @Around("execution(* com.rubyyu.enterprise.SimpleService.sayHello(..))") 13 public void doAround(ProceedingJoinPoint joinPoint) throws Throwable { 14 15 System.out.println("***AspectJ*** DoAround() is running!! intercepted : " + joinPoint.getSignature().getName() 16 + " \narguments : " + Arrays.toString(joinPoint.getArgs())); 17 18 System.out.println("***AspectJ*** DoAround() before is running!"); 19 joinPoint.proceed(); // continue on the intercepted method 20 System.out.println("***AspectJ*** DoAround() after is running!"); 21 22 } 23 }
把新定义的bean加入到applicationContext-annotation.xml中
<bean id="doAroundAspect" class="com.rubyyu.enterprise.aspect.DoAroundAspect" />
输出结果如下
SimpleService : Method printNameId() : My name is Ruby and my id is 1314 --------------- ***AspectJ*** DoAfterThrowing() is running!! intercepted : checkName Exception : java.lang.IllegalArgumentException ****** SimpleService checkName() : Exception thrown.. --------------- ***AspectJ*** DoBefore() is running!! intercepted : sayHello ***AspectJ*** DoAround() is running!! intercepted : sayHello arguments : [美女!] ***AspectJ*** DoAround() before is running! SimpleService : Method sayHello() : Hello! 美女! ***AspectJ*** DoAround() after is running! ***AspectJ*** DoAfterReturning() is running!! intercepted : sayHello Method returned value is : null ***AspectJ*** DoAfter() is running!! intercepted : sayHello ---------------
XML方式的AspectJ可以不适用任何注解,每个切面都被简单的定义为一个普通的java对象。下面给出完整的XML配置文件例子applicationContext-declaration.xml. 使用<aop:aspect>定义切面,对应的bean使用ref属性来指定。在切面元素内,切点使用
<aop:pointcut>来定义,切点有两个属性,一个是id,是指切点的名字,另外一个是expression,用于提供切点表达式。切面和切点都被定义在
<aop:config>内部。
<aop:aspect>内,
使用<aop:before>,它有2个属性,method和pointcut-ref. method的值是通知的方法,pointcut-ref的值是切点的名字。
<aop:aspect>内,
使用<aop:after>,它有2个属性,method和pointcut-ref. method的值是通知的方法,pointcut-ref的值是切点的名字。
<aop:aspect>内,
使用<aop:after-returning
>,它有2个属性,method和pointcut-ref. method的值是通知的方法,pointcut-ref的值是切点的名字。
<aop:aspect>内,
使用<aop:after-throwing
>,它有2个属性,method和pointcut-ref. method的值是通知的方法,pointcut-ref的值是切点的名字。
<aop:aspect>内,
使用<aop:around
>,它有2个属性,method和pointcut-ref. method的值是通知的方法,pointcut-ref的值是切点的名字。
applicationContext-declaration.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"> <bean id="simpleServiceBean" class="com.rubyyu.enterprise.impl.SimpleServiceImpl"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doBeforeAspect" class="com.rubyyu.enterprise.aspect.DoBeforeAspect" /> <bean id="doAfterAspect" class="com.rubyyu.enterprise.aspect.DoAfterAspect" /> <bean id="doAfterReturningAspect" class="com.rubyyu.enterprise.aspect.DoAfterReturningAspect" /> <bean id="doAfterThrowingAspect" class="com.rubyyu.enterprise.aspect.DoAfterThrowingAspect" /> <bean id="doAroundAspect" class="com.rubyyu.enterprise.aspect.DoAroundAspect" /> <aop:config> <aop:aspect id="aspects" ref="doBeforeAspect"> <aop:pointcut id="pointCutBefore" expression="execution(* com.rubyyu.enterprise.SimpleService.sayHello(..))" /> <aop:before method="doBefore" pointcut-ref="pointCutBefore" /> </aop:aspect> <aop:aspect id="aspects" ref="doAfterAspect"> <aop:pointcut id="pointCutAfter" expression="execution(* com.rubyyu.enterprise.SimpleService.sayHello(..))" /> <aop:after method="doAfter" pointcut-ref="pointCutAfter" /> </aop:aspect> <aop:aspect id="aspects" ref="doAfterReturningAspect"> <aop:pointcut id="pointCutAfterReturning" expression="execution(* com.rubyyu.enterprise.SimpleService.sayHello(..))" /> <aop:after-returning method="doAfterReturning" returning="result" pointcut-ref="pointCutAfterReturning" /> </aop:aspect> <aop:aspect id="aspects" ref="doAfterThrowingAspect" > <aop:pointcut id="pointCutAfterThrowing" expression="execution(* com.rubyyu.enterprise.SimpleService.checkName(..))" /> <aop:after-throwing method="doAfterThrowing" throwing="error" pointcut-ref="pointCutAfterThrowing" /> </aop:aspect> <aop:aspect id="aspects" ref="doAroundAspect"> <aop:pointcut id="pointCutAround" expression="execution(* com.rubyyu.enterprise.SimpleService.sayHello(..))" /> <aop:around method="doAround" pointcut-ref="pointCutAround" /> </aop:aspect> </aop:config> </beans>
在App.java中使用XML配置,并注释掉注解配置
//ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-annotation.xml"); ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-declaration.xml");
输出结果如下,同使用注解的方式输出完全一样。
SimpleService : Method printNameId() : My name is Hello and my id is 12345 --------------- ***AspectJ*** DoAfterThrowing() is running!! intercepted : checkName Exception : java.lang.IllegalArgumentException ****** SimpleService checkName() : Exception thrown.. --------------- ***AspectJ*** DoBefore() is running!! intercepted : sayHello ***AspectJ*** DoAround() is running!! intercepted : sayHello arguments : [美女!] ***AspectJ*** DoAround() before is running! SimpleService : Method sayHello() : Hello! 美女! ***AspectJ*** DoAround() after is running! ***AspectJ*** DoAfterReturning() is running!! intercepted : sayHello Method returned value is : null ***AspectJ*** DoAfter() is running!! intercepted : sayHello ---------------
标签:
原文地址:http://www.cnblogs.com/xiaoduaiduai/p/4325708.html