一、使用@AspectJ
Spring在处理@AspectJ注解表达式时,需要将Spring的asm模块添加到类路径中。Spring采用AspectJ提供的@AspectJ注解类库及相应的解析类库,需要在pom.xml文件中添加aspectj.weaver和aspectj.tools类包的依赖。
1、@AspectJ使用实例:
首先是业务类的代码:
public class NaiveWaiter implements Waiter {
public void greetTo(String clientName) {
System.out.println("NaiveWaiter:greet to "+clientName+"...");
}
}
然后使用@AspectJ注解定义一个切面:
@AspectJ
public class PreGreetingAspect{
@Before("execution(* greetTo(..))")
public void beforeGreeting(){
System.out.println("How are you");
}
}
最后通过AspectJProxyFactory为NativeWaiter生成织入PreGreetingAspect切面的代理:
public class AspectJProxyTest {
@Test
public void proxy(){
Waiter target = new NaiveWaiter();
AspectJProxyFactory factory = new AspectJProxyFactory();
factory.setTarget(target);
factory.addAspect(PreGreetingAspect.class);
Waiter proxy = factory.getProxy();
proxy.greetTo("John");
proxy.serveTo("John");
}
}
2、通过配置使用@AspectJ切面
AnnotationAwareAspectJAutoProxyCreator能够将@AspectJ注解切面类自动织入目标Bean中,PreGreetingAspect是使用@AspectJ注解描述的切面类,而NativeWaiter是匹配切点的目标类。
以下使用基于Schema的aop命名空间进行配置:
<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-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<aop:aspectj-autoproxy/>
<bean id="waiter" class="com.smart.NaiveWaiter" />
<bean class="com.smart.aspectj.example.PreGreetingAspect" />
</beans>
首先在配置文件中引入aop命名空间,然后通过aop命名空间的<aop:aspectj-autoproxy/>自动为Spring容器中那些匹配@AspectJ切面的Bean创建代理,完成切面织入。<aop:aspectj-autoproxy/>有一个属性,默认为false,表示使用JDK动态代理技术织入增强;配置为true时,使用CGLib动态代理技术。
二、@AspectJ语法基础
1、切点表达式函数
Spring支持9个@AspectJ切点表达式函数,大致分为4种类型:
(1)方法切点函数:通过描述目标类方法 的信息定义连接点
(2)方法入参切点函数:通过描述目标类方法入参的信息定义连接点
(3)目标类切点函数:通过描述目标类类型的信息定义连接点
(4)代理类切点函数:通过描述目标类的代理类的信息定义连接点
这4种类型的切点函数如下:
2、在函数入参中使用通配符
@AspectJ支持3种通配符
(1)*:匹配上下文中任意一个字符
(2)..:匹配上下文中任意多个字符,在表示类时,必须和*联合使用
(3)+:按类型匹配指定类的所有类(继承或扩展指定类的所有类还有类本身),必须跟在类名后面。
@AspectJ函数按其是否支持通配符及支持程度,分为3类:
(1)支持所有的通配符:execution()和within(),如within(com.smart.*)、within(com.smart.service..*.*Service+)
(2)仅支持“+”通配符:args()、this()和target()
(3)不支持通配符:@args()、@within()、@target()和@annotation()
3、逻辑运算符
Spring支持一下切点运算符:
&&:与操作符,相当于切点的交集运算。由于&是XML特殊字符,Spring提供了and,如within(com.smart..*)and args(String)表示在com,smart包下所有类(当前包及子孙包)拥有一个String入参的方法。
||:或操作符,相当于切点的并集运算,or是等效的操作符。
!:非操作符,相当于切点的反集运算,not是等效的操作符。
4、不同增强类型的注解类
注解类的成员中value和argNames的含义是相同的:
value:用于定义切点
argNames:指定注解所标注增强方法的参数名,多个参数名用逗号分隔。
(1)@Before:有两个成员value和argNames
(2)@AfterReturning:有4个成员value和argNames,还有
pointcut:表示切点的信息,显示指定pointcut值,会覆盖value的设置值
returning:将目标对象方法的返回值绑定给增强的方法
(3)@Around:有两个成员:value和argNames
(4)@AfterThrowing:有4个成员value和argNames,还有
pointcut:表示切点的信息,显示指定pointcut值,会覆盖value的设置值
throwing:将抛出的异常绑定到增强方法中
(5)@After:Final增强,不管是抛出异常还是正常退出,该增强都会得到执行。有两个成员value和argNames
(6)@DeclareParents:引介增强,相当于IntroductionInterceptor,有两个成员value和defaultImpl,defaultImpl是默认的接口实现类
5、引介增强的用法
下面通过切面技术将SmartSeller加入到NativeWaiter中,让NativeWaiter实现Seller的接口:
@Aspect
public class EnableSellerAspect {
@DeclareParents(value="com.smart.NaiveWaiter",defaultImpl=SmartSeller.class)
public Seller seller;
}
在Spring配置文件中配置好切面和NativeWaiter Bean
<aop:aspectj-autoproxy/>
<bean id="waiter" class="com.smart.NaiveWaiter"/>
<bean class="com.smart.aspectj.basic.EnableSellerAspect"/>
测试代码:
public class DeclaredParentsTest{
public static void main(String[] args){
String configPath = "com/smart/aspectj/basic/beans.xml";
ApplicationContext ctx = new ClassPathApplicationContext(configPath);
Waiter waiter = (Waiter)ctx.getBean("waiter");
waiter.greetTo("John");
Seller seller = (Seller)waiter;
seller.sell("Beer","John");
}
}
三、切点函数详解
1、@annotation()
2、execution()
3、args()和@args()
4、within()
5、@within()和@target()
6、target()和this()
四、@AspectJ进阶
1、切点符合运算
@Aspect
public class TestAspect {
@Before("!target(com.smart.NaiveWaiter) && execution(* serveTo(..)))")
public void notServeInNaiveWaiter() {
System.out.println("--notServeInNaiveWaiter() executed!--");
}
@After("within(com.smart.*) && execution(* greetTo(..)))")
public void greeToFun() {
System.out.println("--greeToFun() executed!--");
}
@AfterReturning("target(com.smart.Waiter) || target(com.smart.Seller)")
public void waiterOrSeller(){
System.out.println("--waiterOrSeller() executed!--");
}
}
2、命名切点
命名切点的使用类方法作为切点的名称,此外方法的访问修饰符还控制了切点的可引用性。如下是命名切点的结构:
命名切点定义好后,可以在定义切面时通过名称引用切点,如下:
3、增强织入的顺序
(1)如果增强在同一个切面类中声明,则依照增强在切面类中定义的顺序进行织入
(2)如果增强位于不同的切面类中,且这些切面类都实现了org.springframework.core.Ordered接口,则由接口方法的顺序号决定(顺序号小的先织入)
(3)如果增强位于不同的切面类中,且这些切面类没有实现org.springframework.core.Ordered接口,则织入的顺序是不确定的。
五、基于Schema配置切面
1、基于Schema配置切面的实例
使用<aop:aspect>元素标签定义切面,其内部可以定义多个增强。在<aop:config>元素中可以定义多个切面。
2、配置命名切点
<aop:pointcut>如果位于<aop:aspect>元素中,则命名切点只能被当前<aop:aspect>的元素访问到,定义在<aop:config>元素中,可以被整个<aop:aspect>元素中定义的所有增强访问。如果在<aop:config>元素下直接定义<aop:pointcut>则必须保证<aop:pointcut>在<aop:aspect>之前定义。而在<aop:aspect>中没有先后顺序的要求。
3、各种增强类型的配置
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="testAdvice" pointcut="execution(* com..*.Waiter.greetTo(..))"/>
<aop:aspect ref="adviceMethods">
<aop:after-returning method="afterReturning"
pointcut="target(com.smart.SmartSeller)" returning="retVal" /><!--后置增强-->
<aop:around method="aroundMethod"
pointcut="execution(* serveTo(..)) and within(com.smart.Waiter)" /><!--环绕增强-->
<aop:after-throwing method="afterThrowingMethod"
pointcut="target(com.smart.SmartSeller) and execution(* checkBill(..))"
throwing="iae" /><!--抛出异常增强-->
<aop:after method="afterMethod"
pointcut="execution(* com..*.Waiter.greetTo(..))" /><!--Final增强-->
<aop:declare-parents
implement-interface="com.smart.Seller"
default-impl="com.smart.SmartSeller"
types-matching="com.smart.Waiter+" /><!--引介增强-->
<aop:before method="bindParams"
pointcut="target(com.smart.NaiveWaiter) and args(name,num,..)"/>
</aop:aspect>
</aop:config>
<bean id="testAdvice" class="com.smart.schema.TestBeforeAdvice"/>
<bean id="adviceMethods" class="com.smart.schema.AdviceMethods" />
<bean id="naiveWaiter" class="com.smart.NaiveWaiter" />
<bean id="naughtyWaiter" class="com.smart.NaughtyWaiter" />
<bean id="seller" class="com.smart.SmartSeller" />
4、Advisor配置
通过<aop:advisor>配置一个Advisor,通过advice-ref属性引用基于接口定义的增强,通过pointcut定义切点表达式,或者通过pointcut-ref引用一个命名的切点。如下:
六、各种切面类型总结
(1)基于@AspectJ注解的方式:适用项目采用Java5.0
(2)基于<aop:aspect>的方式:适用项目只能使用低版本的JDK
(3)基于<aop:advisor>的方式:适用正在升级一个基于低版本Spring AOP开发的项目,<aop:advisor>可以复用已经存在的Advice类
(4)基于Advisor类的方式:适用项目只能使用低版本的Spring
切面不同定义方式具体实现比较: