AspectJ实现了AOP的功能,且实现方式更为简洁,使用更为方便,而且还支持注解式开发
在Spring中使用AOP开发时,一般使用AspectJ的实现方式
一、AspectJ简介
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守java字节码规范的Class文件。
二、AspectJ的通知类型
AspectJ中常用的通知有五种类型:
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知:无论程序执行是否正常,该通知都会执行。类似于try…catch中的finally代码块。
三、AspectJ的切入点表达式
AspectJ除了提供了5种通知外,还定义了专门的表达式用于指定切入点。表达式的原型是:
execution([modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
[declaring-type-pattern 全限定性类名
name-pattern(param-pattern) 方法名(参数名)
[throws-pattern] 抛出异常类型)
切入点表达式要匹配的对象就是目标方法的方法名。所以execution表达式中明显就是方法的签名。注意,表达式中加[]的部分表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:
符号 | 意义 |
---|---|
* | 0至多个任意字符 |
. . | 用在方法参数中,表示任意多个参数/用在包名后,表示当前包及其子包路径 |
+ | 用在类名后,表示当前类及其子类/用在接口后,表示当前接口及其实现类 |
例:
execution(public * * (. .))
指定切入点为:任意公共方法
execution(* set * (. .))
指定切入名为:任何一个以“set”开始的方法
execution(* com.xyz.service. * . * (. .))
指定切入名为:定义在service包里的任意类的任意方法
execution(* com.xyz.service. . * . * (. .))
指定切入点为:定义在service包或者子包里的任意类的任意方法。". ."出现在类名中时,后面必须跟“ * ”,表示包、子包下的所有类
execution(* *.service. * . *(. .))
指定只有一级包下的service子包下所有类(接口)中所有方法为切入点
execution(* * . .service. * . * (. .)) 重点掌握
指定所有包下的service子包下所有类(接口)中所有方法为切入点
execution(* * . . ISomeService . * (. .)) 重点掌握
指定所有包下的ISomeService接口中所有方法为切入点
四、AspectJ的开发环境
- 导入jar包
aopalliance.jar
aspectjrt.jar
aspectjweaver.jar
spring-aop.jar
spring-aspects.jar(spring与aspectj整合的jar包)
五、AspectJ基于注解的AOP实现
AspectJ提供了以注解方式对于AOP的实现
接口类
//主业务接口
public interface ISomeService {
//目标方法
void doFirst();
//目标方法
String doSecond();
//目标方法
void doThird();
}
实现类
public class SomeServiceImpl implements ISomeService {
@Override
public void doFirst() {
System.out.println("执行doFirst()方法");
}
@Override
public String doSecond() {
System.out.println("执行doSecond()方法");
return "abcde";
}
@Override
public void doThird() {
System.out.println("执行doThird()方法" + 3 / 0);
System.out.println("执行doThird()方法");
}
}
切面类
@Aspect //表示当前类为切面
public class MyAspect {
@Before("execution(* *..ISomeService.doFirst(..))") //放入切入点表达式
public void before() {
System.out.println("执行前置通知方法");
}
@AfterReturning(value = "execution(* *..ISomeService.doSecond(..))",returning = "result")
public void myAfterReturning(Object result){
System.out.println("执行后置通知方法 result = " + result);
}
@Around("execution(* *..ISomeService.doSecond(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("执行环绕通知方法,目标方法执行之前");
//执行目标方法
Object result = pjp.proceed();
System.out.println("执行环绕通知方法,目标方法执行之后");
if (result != null) {
result = ((String)result).toUpperCase();
}
return result;
}
@AfterThrowing("execution(* *..ISomeService.doThird(..))")
public void myAfterThrowing() {
System.out.println("执行异常通知方法");
}
@AfterThrowing(value = "execution(* *..ISomeService.doThird(..))", throwing = "ex")
public void myAfterThrowing(Exception ex) {
System.out.println("执行异常通知方法 ex = " + ex.getMessage());
}
@After("doThirdPointcut()")
public void myAfter() {
System.out.println("执行最终通知方法");
}
//定义了一个切入点,叫doThirdPointcut()
@Pointcut("execution(* *..ISomeService.doThird(..))")
public void doThirdPointcut() {}
}
xml配置
<!--注册切面-->
<bean id="myAspect" class="com.chen.service.MyAspect"/>
<!--注册目标对象-->
<bean id="someService" class="com.chen.service.SomeServiceImpl"/>
<!--注册AspectJ的自动代理-->
<aop:aspectj-autoproxy/>
测试及结果
public class MyTest {
@Test
public void test01() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ISomeService service = (ISomeService) ac.getBean("someService");
service.doFirst();
System.out.println("- - - - - -");
service.doSecond();
System.out.println("- - - - - -");
service.doThird();
}
}
/*
执行前置通知方法
执行doFirst()方法
- - - - - -
执行环绕通知方法,目标方法执行之前
执行doSecond()方法
执行环绕通知方法,目标方法执行之后
执行后置通知方法 result = ABCDE
- - - - - -
执行最终通知方法
执行异常通知方法
执行异常通知方法 ex = / by zero
*/
六、AspectJ基于xml的AOP实现(重点!!!)
切面类
public class MyAspect {
public void before() {
System.out.println("执行前置通知方法");
}
public void myAfterReturning(Object result){
System.out.println("执行后置通知方法 result = " + result);
}
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("执行环绕通知方法,目标方法执行之前");
//执行目标方法
Object result = pjp.proceed();
System.out.println("执行环绕通知方法,目标方法执行之后");
if (result != null) {
result = ((String)result).toUpperCase();
}
return result;
}
public void myAfterThrowing() {
System.out.println("执行异常通知方法");
}
public void myAfterThrowing(Exception ex) {
System.out.println("执行异常通知方法 ex = " + ex.getMessage());
}
public void myAfter() {
System.out.println("执行最终通知方法");
}
}
xml配置
<!--注册切面-->
<bean id="myAspect" class="com.chen.service.MyAspect"/>
<!--注册目标对象-->
<bean id="someService" class="com.chen.service.SomeServiceImpl"/>
<!--AOP配置-->
<aop:config>
<aop:pointcut id="doFirstPointcut" expression="execution(* *..ISomeService.doFirst(..))"/>
<aop:pointcut id="doSecondPointcut" expression="execution(* *..ISomeService.doSecond(..))"/>
<aop:pointcut id="doThirdPointcut" expression="execution(* *..ISomeService.doThird(..))"/>
<aop:aspect ref="myAspect">
<aop:before method="before" pointcut-ref="doFirstPointcut"/>
<aop:after-returning method="myAfterReturning(java.lang.Object)" pointcut-ref="doSecondPointcut" returning="result"/>
<aop:around method="myAround" pointcut-ref="doSecondPointcut"/>
<aop:after-throwing method="myAfterThrowing" pointcut-ref="doThirdPointcut"/>
<aop:after-throwing method="myAfterThrowing(java.lang.Exception)" pointcut-ref="doThirdPointcut" throwing="ex"/>
<aop:after method="myAfter" pointcut-ref="doThirdPointcut"/>
</aop:aspect>
</aop:config>
测试及结果
@Test
public void test01() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ISomeService service = (ISomeService) ac.getBean("someService");
service.doFirst();
System.out.println("- - - - - -");
service.doSecond();
System.out.println("- - - - - -");
service.doThird();
}
/*
执行前置通知方法
执行doFirst()方法
- - - - - -
执行环绕通知方法,目标方法执行之前
执行doSecond()方法
执行环绕通知方法,目标方法执行之后
执行后置通知方法 result = ABCDE
- - - - - -
执行doThird()方法
执行最终通知方法
*/