Spring AOP是基于动态代理的,所以只支持方法连接点
Spring仅支持Aspectj切点指示器的一个子集
1
public interface Performance {
public void perform();
}
编写切点
execution(* concert.Performance.perform(..)) && within(concert.*)
*
任意返回类型concert.Performance
方法所属类perform
方法(..)
任意参数&&
=and 前后指示器都要满足(后一个表示concert包下任意类的方法被调用时)||
=or!
=not
XML中可以使用单词代替避免特殊含义字符
execution(* concert.Performance.perform(..)) && bean('woodstock')
bean()
限定bean的ID为woodstock
定义切面
使用注解
@Before
@After
@Around
@AfterReturning
@AfterThrowing
定义一个切点表达式多次使用
@PoingCut("execution(* concert.Performance.perform(..)) ")
public void performance() {}
@Before("performance()")
public void before() {...}
在Java配置类中上使用EnableAspectjAutoProxy
启用自动代理
或者xml中
<aop:aspectj-autoproxy />
为@Aspectj
注解的bean创建一个代理,这个代理会围绕所有该切面的切点所匹配的bean
环绕通知方法需要接收ProceedingJoinPoint
参数,用来在通知方法中调用被通知的方法
@Around
public void around(ProceedingJoinPoint jp) {
before();
jp.proceed();
after();
}
处理通知中的参数
@Pointcut("execution(* com.wang.count(int)) && args(number)")
public void count(int number) {};
@Before("count(number)")
public void before(int number) {...}
count(int)
接受int类型参数args(number)
指定参数,参数会传递到通知方法中,参数名称一样,可以在通知方法中使用
引入增强
已经定义好的A类,需要使用B接口的功能,在不修改A类实现的情况下使用B的方法功能
@DeclareParents(value="concert.Performance+", defaultImpl=DefaultImpl.class)
public AnotherIntf anotherintf;
value
指定需要引入新接口的bean(+
表示所有Performance的子类型,不包括本身)defaultImpl
指定为引入功能提供实现的类- 注解标注了要引入的接口
xml中声明切面
<aop:advisor>
定义aop通知器<aop:after>
定义后置通知<aop:after-returning>
定义返回通知<aop:after-throwing>
定义异常通知<aop:arounf>
定义环绕通知<aop:aspectj>
定义一个切面<aop:aspectj-autoproxy>
启用@Aspectj注解的切面<aop:before>
定义前置通知<aop:config>
大多数<aop:*>
元素都要包含在其中<aop:declare-parents>
引入增强<aop:pointcut>
定义一个切点
<aop:config>
<aop:aspectj ref="audience"> <!--通知beanID-->
<aop:pointcut id="performance" expression="execution(* concert.Performance.perform(..))" />
<aop:before method=“before” pointcut-ref="performance"/>
</aop:aspectj>
</aop:config>
如果不定义<aop:pointcut/>
,<aop:before/>
中添加属性expression="execution(* concert.Performance.perform(..))"
传递参数:
加了and args(number)
<aop:config>
<aop:aspectj ref="audience"> <!--通知beanID-->
<aop:pointcut id="performance" expression="execution(* concert.Performance.perform(int)) and args(number)" />
<aop:before method=“before” pointcut-ref="performance"/>
</aop:aspectj>
</aop:config>
引入增强:
<aop:config>
<aop:aspectj ref="audience"> <!--通知beanID-->
<aop:declare-parents
types-matching="concert.Performance+"
implement-interface="concert.Encorable"
default-impl="concert.DefaultEncorable" />
</aop:aspectj>
</aop:config>
·····························································································································································································
Aspectj切面中注入bean
当SpringAOP不能满足需求时,就需要更为强大的Aspectj,如何使用Spring为Aspectj注入依赖:
public aspect CriticAspect {
}
<bean class="com.springinaction.springidol.CriticAspect" factory-method="aspectOf">
<property name="criticismEngine" ref="criticismEnfine" />
</bean>
通常情况下,bean由Spring容器初始化,但是Aspectj切面是由Aspectj在运行期创建的,等到Spring有机会为CriticAspect
注入criticismEnfine
时,CriticAspect
已经被实例化了。
因为Spring不负责创建CriticAspect
,那就不能在Spring中简单的把CriticAspect
声明为一个bean,我们需要一种方式获得由Aspectj创建的CriticAspect
实例的句柄,来注入CriticismEnfine
。
所有的Aspectj切面都提供一个静态的aspectOf()
方法,该方法返回切面的一个单例,所以要使用factory-method
来调用aspectOf()
方法,而不是调用构造器方法。
简而言之,Aspectj切面实例在运行时已经由Aspectj创建完成了,Spring需要通过aspectOf()
工厂方法获得切面的引用,然后在该对象上执行注入。
p107 ↩︎