在结束了上一篇Spring 1.x中AOP的使用之后,我又马不停蹄的打开Eclipse,做着Spring2.X下了AOP的Sample。在上一篇文章中的配置过程中,由于对自动代理不是很熟,出现了循环引用的异常信息。当初在阅读PicoContainer源码时看到循环引用不以为然,后来在学习AspectJ时小有印象,这次在折腾了半个多小时后可加深了印象。
好啦,闲言少叙,奔向文本的主题吧。和Spring1.X相比,Spring2.X使用AspectJ的语法来声明AOP,这使得它更“标准”,更灵活了。还是那句话,如果你不了解AspectJ并且打算使用Spring2.X的AspectJ式AOP,那就学学AspectJ吧,这方面的书还是很多了。
Spring2.X下的切面有两种实现方式,一种是以Java文件定义切面(其只是普通的Java类),然后在配置文件中声明定义的切面;另一种是在Java类中引入和AOP相关的元数据(注释)。
先介绍第一种配置方式。需要指出的是,Spring2.X的beans名称空间和Spring1.X有所不同,它采用了Schema而不是DTD(也可采用DTD方式,具体的请参考文档)。还是引入毫无意义的日志切面,定义的切面类LogingAspect 如下:
public
class
LogingAspect {
public
void
logMethod(JoinPoint jp){
System.err.println(jp.getTarget().getClass());
System.err.println(jp.getSignature().getName());
}
}
同时在配置文件中如下配置:
<
bean id
=
"
logAspectTarget
"
class
=
"
hibernatesample.service.util.LogingAspect
"
></
bean
>
<
aop:config
>
<
aop:aspect id
=
"
logAspect
"
ref
=
"
logAspectTarget
"
>
<
aop:pointcut id
=
"
businessService
"
expression
=
"
execution(* hibernatesample.service.*.*(..))
"
/>
<
aop:after pointcut
-
ref
=
"
businessService
"
method
=
"
logMethod
"
/>
</
aop:aspect
>
</
aop:config
>
对于上面的切面,切入点businessService是在配置文件中声明的,其表达式采用了 AspectJ的语法,LogingAspect 类中logMethod(JoinPoint jp)方法根据配置文件信息可知其是after通知,方法的JoinPoint参数不是必须的,它是来自于AspectJ的实用类,这里用它不过输出一些和连接点有关的信息。当然,在Spring2.X中,切入点和通知能更灵活的使用,我们可以如AspectJ一样传递参数给通知。如果需要在Service中引入事务功能,需要如下配置事务通知:
<
tx:advice id
=
"
txAdvice
"
transaction
-
manager
=
"
transactionManager
"
>
<
tx:attributes
>
<
tx:method name
=
"
get*
"
read
-
only
=
"
true
"
/>
<
tx:method name
=
"
find*
"
read
-
only
=
"
true
"
/>
<
tx:method name
=
"
*
"
/>
</
tx:attributes
>
</
tx:advice
>
<
aop:config
>
<
aop:pointcut id
=
"
demoServiceMethods
"
expression
=
"
execution(* hibernatesample.service.*.*(..))
"
/>
<
aop:advisor advice
-
ref
=
"
txAdvice
"
pointcut
-
ref
=
"
demoServiceMethods
"
/>
<
aop:aspect id
=
"
logAspect
"
ref
=
"
logAspectTarget
"
>
<
aop:pointcut id
=
"
businessService
"
expression
=
"
execution(* hibernatesample.service.*.*(..))
"
/>
<
aop:after pointcut
-
ref
=
"
businessService
"
method
=
"
logMethod
"
/>
</
aop:aspect
>
</
aop:config
>
完成上面的工作相当于完成了
Spring1.X 的
自动代理。
我们接下来需要定义的任何
Service Bean
都可以很纯粹很纯粹了:
<
bean
id
="accountService"
class
="hibernatesample.service.impl.AccountServiceImpl"
>
<
property
name
="accountDAO"
ref
="accountDAO"
></
property
>
</
bean
>
第二种实现
AOP
的方式和第一种相比,只是在
LogingAspect
中加入了注释,而省去了配置文件中和
LogingAspect
相关的配置。重新编写的
LogingAspect
如下:
@Aspect
public
class
LogingAspect {
@Pointcut(
"
execution(* hibernatesample.service.*.*(..))
"
)
public
void
businessService(){}
@After(
"
businessService()
"
)
public
void
logMethod(JoinPoint jp){
System.err.println(jp.getTarget().getClass());
System.err.println(jp.getSignature().getName());
}
}
而简化后的配置文件可以去除上面的如下和
logAspect
相关的配置信息:
<
aop:aspect
id
="logAspect"
ref
="logAspectTarget"
>
<
aop:pointcut
id
="businessService"
expression
="execution(* hibernatesample.service.*.*(..))"
/>
<
aop:after
pointcut-ref
="businessService"
method
="logMethod"
/>
</
aop:aspect
>
<
bean
id
="logAspectTarget"
class
="hibernatesample.service.util.LogingAspect"
></
bean
>
还没有完,为了使
Spring
应用
LogingAspect
的注释,需要在配置文件中添上
<aop:aspectj-autoproxy/>
如果觉得事务的配置没有使用注释更简洁(我倒不会有这样的想法,毕竟在配置文件中声明的事务只是那么固定的几段,除非作用于类上的事务逻辑上很复杂),也可以使用Spring提供的事务注释作用于类文件上,这可是更细粒度的事务声明了。
坦率的说,由于时间有限,该文写的比较粗糙。对于Spring AOP有兴趣并有疑问的朋友,可以参考Spring的文档,它的文档做的还是不错的。