[Spring实战系列](18)注解切面
2016-02-13 21:40
411 查看
使用注解来创建切面是AspectJ5所引入的关键特性。在AspectJ5之前,编写AspectJ切面需要学习一种Java语言的扩展,但是AspectJ面向注解的模型可以非常简单的通过少量注解把任意类转变为切面。
回顾一下Audience类,没有任何地方让它成为一个切面,我们不得不使用XML声明通知和切点。
我们通过@AspectJ注解,我们再看看Audience类,不需要任何额外的类或Bean声明就能将它转换为一个切面。
新的Audience类现在已经使用@AspectJ注解进行标注。该注解标示了Audience不仅仅是一个POJO,还是一个切面。
@Pointcut注解用于定义一个可以在@AspectJ切面内可重用的切点。@Pointcut注解的值是一个AspectJ切点表达式(这里标示切点必须匹配Singer的perform()方法)。切点的名称来源于注解所应用的方法名称。因为,该切点的名称为SingerPerform()。SingerPerform()方法的实际内容并不重要,它只是一个标示,供@Pointcut注解依附。
Audience的每一个方法都是用通知注解来标注。takeSeats()方法使用@Before注解来标示它们是前置通知方法。applaud()方法使用@AfterReturning注解来标示它是后置通知方法。demandRefund()方法使用@AfterThrowing注解标示它在抛出异常时该方法被会调用。SingerPerform()切点的名称作为参数的值赋予给所有的通知注解,来标示每一个通知方法应该应用在哪。
注意:
因为Audience类本身包含了所有它所需要定义的切点和通知,所以我们不在需要在XML配置中声明切点和通知。为了让Spring将Audience应用为一个切面,我们需要在Spring上下文中声明一个自动代理Bean,该Bean知道如何把@AspectJ注解所标注的Bean转变为代理通知。
为此,Spring自带了名为AnnotationAwareAspectJProxyCreator的自动代理创建类。我们可以在Spring上下文中把AnnotationAwareAspectJProxyCreator注册为一个Bean,但是这个类文字太长,不宜使用。因此,我们使用Spring的aop空间提供的一个自定义配置元素(<aop:aspectj-autoproxy/>)来代替前者,这个更易使用。
回顾一下Audience类,没有任何地方让它成为一个切面,我们不得不使用XML声明通知和切点。
我们通过@AspectJ注解,我们再看看Audience类,不需要任何额外的类或Bean声明就能将它转换为一个切面。
[code]packagecom.sjf.bean;
/**
*歌手实体类
*@authorsjf0115
*
*/
publicclassSinger{
publicvoidperform(){
System.out.println("正在上演个人演唱会...");
}
}
[code]packagecom.sjf.bean;
importorg.aspectj.lang.annotation.AfterReturning;
importorg.aspectj.lang.annotation.AfterThrowing;
importorg.aspectj.lang.annotation.Aspect;
importorg.aspectj.lang.annotation.Before;
importorg.aspectj.lang.annotation.Pointcut;
/**
*观众实体类(注解为切面)
*@authorsjf0115
*
*/
@Aspect
publicclassAudience{
//定义切点
@Pointcut("execution(*com.sjf.bean.Singer.perform(..))")
publicvoidSingerPerform(){
//空方法
}
//表演之前
@Before("SingerPerform()")
publicvoidtakeSeats(){
System.out.println("theaudienceistakingtheirseats...");
}
//表演成功之后
@AfterReturning("SingerPerform()")
publicvoidapplaud(){
System.out.println("verygood,clapclapclap...");
}
//表演失败之后
@AfterThrowing("SingerPerform()")
publicvoiddemandRefund(){
System.out.println("verybad,Wewantourmoneyback...");
}
}
新的Audience类现在已经使用@AspectJ注解进行标注。该注解标示了Audience不仅仅是一个POJO,还是一个切面。
@Pointcut注解用于定义一个可以在@AspectJ切面内可重用的切点。@Pointcut注解的值是一个AspectJ切点表达式(这里标示切点必须匹配Singer的perform()方法)。切点的名称来源于注解所应用的方法名称。因为,该切点的名称为SingerPerform()。SingerPerform()方法的实际内容并不重要,它只是一个标示,供@Pointcut注解依附。
Audience的每一个方法都是用通知注解来标注。takeSeats()方法使用@Before注解来标示它们是前置通知方法。applaud()方法使用@AfterReturning注解来标示它是后置通知方法。demandRefund()方法使用@AfterThrowing注解标示它在抛出异常时该方法被会调用。SingerPerform()切点的名称作为参数的值赋予给所有的通知注解,来标示每一个通知方法应该应用在哪。
注意:
除了注解和无操作的SingerPerform()方法,Audience类在实现上并没有任何改变,Audience类仍然是一个简单的Java对象,能够像以前一样使用(在Spring中使用Bean进行配置)。 |
为此,Spring自带了名为AnnotationAwareAspectJProxyCreator的自动代理创建类。我们可以在Spring上下文中把AnnotationAwareAspectJProxyCreator注册为一个Bean,但是这个类文字太长,不宜使用。因此,我们使用Spring的aop空间提供的一个自定义配置元素(<aop:aspectj-autoproxy/>)来代替前者,这个更易使用。
[code]<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop 'target='_blank'>http://www.springframework.org/schema/aop/spring-aop-4.2.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"> <beanid="singer"class="com.sjf.bean.Singer"></bean><beanid="audience"class="com.sjf.bean.Audience"></bean><aop:aspectj-autoproxy/></beans>
<aop:aspectj-autoproxy/>配置元素将在Spring上下文中创建一个AnnotationAwareAspectJProxyCreator类,它会自动代理一些Bean,这些Bean的方法需要与使用@AspectJ注解的Bean中所定义的切点相匹配。
运行结果:注意:
theaudienceistakingtheirseats...
正在上演个人演唱会...
verygood,clapclapclap...1.注解环绕通知
<aop:aspect>元素和@AspectJ注解都是把一个POJO转变为一个切面的有效方式。但是<aop:aspect>相对@AspectJ的一个明显优势是:不需要实现切面功能的代码(本例中是Audience类代码)。通过@AspectJ,我们必须标注类和方法,它需要有源码
像Spring基于XML的AOP一样,@AspectJ注解的使用不仅仅限于定义前置和后置通知类型。我们还可以创建环绕通知。[code]@Around("SingerPerform()")publicvoidPerformTime(ProceedingJoinPointjoinPoint){//演出之前System.out.println("theaudienceistakingtheirseats...");try{longstart=System.currentTimeMillis();//执行演出操作joinPoint.proceed();longend=System.currentTimeMillis();//演出成功System.out.println("verygood,clapclapclap...");System.out.println("该演出共需要"+(end-start)+"milliseconds");}catch(Throwablee){//演出失败System.out.println("verybad,Wewantourmoneyback...");e.printStackTrace();}}
在这里,@Around注解标示了PerformTime()方法将被作为环绕通知应用与SingerPerform()切点。和之前使用XML方式唯一的区别就是用@Around注解所标注的。简单的使用@Around注解来标注方法并不足以调用proceed()方法,因此,被环绕通知的方法必须接受一个ProceedingJoinPoint对象作为方法入参,并在对象上调用proceed()方法。
2.传递参数给所标注的通知
之前我们曾经使用Spring基于XML的切面声明为通知传递参数,而是@AspectJ注解为通知传递参数,与之相比并没有太大的区别。[code]packagecom.sjf.bean;/***歌手实体类*@authorsjf0115**/publicclassSinger{publicvoidperform(Stringsong){System.out.println("正在上演个人演唱会..."+song);}}[code]packagecom.sjf.bean;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Before;importorg.aspectj.lang.annotation.Pointcut;/***主办方实体类*@authorsjf0115**/@AspectpublicclassOrganizers{//定义切点@Pointcut("execution(*com.sjf.bean.Singer.perform(String))andargs(song)")publicvoidSingerPerform(){//}//表演之前@Before("SingerPerform()andargs(song)")publicvoidBeforeSong(Stringsong){System.out.println("演唱会马上就开始了,演唱歌曲为"+song);}}
<aop:pointcut>元素变为@Pointcut注解,<aop:before>元素变为@Before注解。
不知道下面配置出现报错如何解决?求解.....[code]//定义切点@Pointcut("execution(*com.sjf.bean.Singer.perform(String))andargs(song)")publicvoidSingerPerform(Stringsong){//}//表演之前@Before("SingerPerform(song)")publicvoidBeforeSong(Stringsong){System.out.println("演唱会马上就开始了,演唱歌曲为"+song);}
来源于:《Spring实战》
相关文章推荐
- 蓝桥杯 算法训练 Anagrams问题
- 从头认识Spring-2.3 注解装配-@autowired(3)-通过构造器方法注入
- 蓝桥杯 算法训练 出现次数最多的整数
- 从头认识Spring-2.3 注解装配-@autowired(2)-通过set方法或者其他方法注入
- 第一章 Java工具类目录
- 从头认识Spring-2.3 注解装配-@autowired(1)-通过属性域注入
- 第二章 Java浮点数精确计算
- JavaSript模块规范 - AMD规范与CMD规范介绍
- Java Gis 拓扑图(Google 地图)
- windows java环境变量配置
- SSH进阶(4)——Spring框架入门及环境搭建
- Java中泛型中的几个符号
- 对一致性Hash算法,Java代码实现的深入研究
- 八:Java之I/O
- java枚举的概念与应用
- ubuntu下java web项目的环境配置和发布
- java 多线程实现 哲学家进餐问题
- java常量池理解
- 第一个用记事本写的java代码
- 深入浅析Java 循环中标签的作用