public static void LogStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs(); //获取到参数信息
Signature signature = joinPoint.getSignature(); //获取到方法签名
String name = signature.getName(); //获取到方法名
System.out.println(“【” + name + “】记录开始…执行参数:” + Arrays.asList(args));
}
对于有些目标方法在执行完之后可能会有返回值,或者方法中途异常抛出,那么对于这些情况,我们应该如何获取到这些信息呢?
首先我们来获取当方法执行完之后获取返回值,
在这里我们可以使用@AfterReturning注解,该注解表示的通知方法是在目标方法正常执行完之后执行的。
在返回通知中,只要将returning属性添加到@AfterReturning注解中,就可以访问连接点的返回值。
该属性的值即为用来传入返回值的参数名称,但是注意必须在通知方法的签名中添加一个同名参数。
在运行时Spring AOP会通过这个参数传递返回值,由于我们可能不知道返回值的类型,所以一般将返回值的类型设置为Object型。
与此同时,原始的切点表达式需要出现在pointcut属性中,
如下所示:
// 方法正常执行完之后
/**
-
在程序正常执行完之后如果有返回值,我们可以对这个返回值进行接收
-
returning用来接收方法的返回值
-
*/
@AfterReturning(pointcut=“public int com.spring.inpl..(int, int)”,returning=“result”)
public static void LogReturn(JoinPoint joinPoint,Object result) {
System.out.println(“【” + joinPoint.getSignature().getName() + “】程序方法执行完毕了…结果是:” + result);
}
对于接收异常信息,方法其实是一样的。
我们需要将throwing属性添加到@AfterThrowing注解中,也可以访问连接点抛出的异常。Throwable是所有错误和异常类的顶级父类,所以在异常通知方法可以捕获到任何错误和异常。
如果只对某种特殊的异常类型感兴趣,可以将参数声明为其他异常的参数类型。然后通知就只在抛出这个类型及其子类的异常时才被执行。
实例如下:
// 异常抛出时
/**
-
在执行方法想要抛出异常的时候,可以使用throwing在注解中进行接收,
-
其中value指明执行的全方法名
-
throwing指明返回的错误信息
-
*/
@AfterThrowing(pointcut=“public int com.spring.inpl..(int, int)”,throwing=“e”)
public static void LogThowing(JoinPoint joinPoint,Object e) {
System.out.println(“【” + joinPoint.getSignature().getName() +“】发现异常信息…,异常信息是:” + e);
}
我们在上面介绍通知注解的时候,大家应该也看到了其实还有一个很重要的通知——环绕通知,
环绕通知是所有通知类型中功能最为强大的,能够全面地控制连接点,甚至可以控制是否执行连接点。
对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。这就意味着我们需要在方法中传入参数ProceedingJoinPoint来接收方法的各种信息。
注意:
环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。
具体使用可以看下面这个实例:
/**
-
环绕通知方法
-
使用注解@Around()
-
需要在方法中传入参数proceedingJoinPoint 来接收方法的各种信息
-
使用环绕通知时需要使用proceed方法来执行方法
-
同时需要将值进行返回,环绕方法会将需要执行的方法进行放行
-
@throws Throwable
-
*/
@Around(“public int com.spring.inpl..(int, int)”)
public Object MyAround(ProceedingJoinPoint pjp) throws Throwable {
// 获取到目标方法内部的参数
Object[] args = pjp.getArgs();
System.out.println(“【方法执行前】”);
// 获取到目标方法的签名
Signature signature = pjp.getSignature();
String name = signature.getName();
Object proceed = null;
try {
// 进行方法的执行
proceed = pjp.proceed();
System.out.println(“方法返回时”);
} catch (Exception e) {
System.out.println(“方法异常时” + e);
}finally{
System.out.println(“后置方法”);
}
//将方法执行的返回值返回
return proceed;
}
那么现在这五种通知注解的使用方法都已经介绍完了,
我们来总结一下这几个通知注解都在同一个目标方法中时的一个执行顺序。
在正常情况下执行:
@Before(前置通知)—>@After(后置通知)---->@AfterReturning(返回通知)
在异常情况下执行:
@Before(前置通知)—>@After(后置通知)---->@AfterThrowing(异常通知)
当普通通知和环绕通知同时执行时:
执行顺序是:
环绕前置----普通前置----环绕返回/异常----环绕后置----普通后置----普通返回/异常
对于上面的通知注解,我们都是在每一个通知注解上都定义了一遍切入点表达式,
但是试想一个问题,如果我们不想给这个方法设置通知方法了,或者我们想要将这些通知方法切入到另一个目标方法,那么我们岂不是要一个一个的更改注解中的切入点表达式吗?这样也太麻烦了吧?
所以spring就想到了一个办法,重用切入点表达式。
也就是说将这些会重复使用的切入点表达式用一个方法来表示,那么我们的通知注解只需要调用这个使用了该切入点表达式的方法即可实现和之前一样的效果,这样的话,我们即使想要更改切入点表达式的接入方法,也不用一个一个的去通知注解上修改了。
获取可重用的切入点表达式的方法是:
-
随便定义一个void的无实现的方法
-
为方法添加注解@Pointcut()
-
在注解中加入抽取出来的可重用的切入点表达式
-
使用value属性将方法加入到对应的切面函数的注解中
完整实例如下:
@Aspect //切面注解
@Component //其他业务层
public class LogUtli {
/**
-
定义切入点表达式的可重用方法
-
*/
@Pointcut(“execution(public int com.spring.inpl.MyMathCalculator.*(int, int))”)
public void MyCanChongYong() {}
// 方法执行开始
@Before(“MyCanChongYong()”)
public static void LogStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs(); //获取到参数信息
Signature signature = joinPoint.getSignature(); //获取到方法签名
String name = signature.getName(); //获取到方法名
System.out.println(“【” + name + “】记录开始…执行参数:” + Arrays.asList(args));
}
// 方法正常执行完之后
/**
-
在程序正常执行完之后如果有返回值,我们可以对这个返回值进行接收
-
returning用来接收方法的返回值
-
*/
@AfterReturning(value=“MyCanChongYong()”,returning=“result”)
public static void LogReturn(JoinPoint joinPoint,Object result) {
System.out.println(“【” + joinPoint.getSignature().getName() + “】程序方法执行完毕了…结果是:” + result);
}
// 异常抛出时
/**
-
在执行方法想要抛出异常的时候,可以使用throwing在注解中进行接收,
-
其中value指明执行的全方法名
-
throwing指明返回的错误信息
-
*/
@AfterThrowing(value=“MyCanChongYong()”,throwing=“e”)
public static void LogThowing(JoinPoint joinPoint,Object e) {
System.out.println(“【” + joinPoint.getSignature().getName() +“】发现异常信息…,异常信息是:” + e);
}
// 结束得出结果
@After(value = “execution(public int com.spring.inpl.MyMathCalculator.add(int, int))”)
public static void LogEnd(JoinPoint joinPoint) {
System.out.println(“【” + joinPoint.getSignature().getName() +“】执行结束”);
}
/**
-
环绕通知方法
-
@throws Throwable
-
*/
@Around(“MyCanChongYong()”)
public Object MyAround(ProceedingJoinPoint pjp) throws Throwable {
// 获取到目标方法内部的参数
Object[] args = pjp.getArgs();
System.out.println(“【方法执行前】”);
// 获取到目标方法的签名
Signature signature = pjp.getSignature();
String name = signature.getName();
Object proceed = null;
try {
// 进行方法的执行
proceed = pjp.proceed();
System.out.println(“方法返回时”);
} catch (Exception e) {
System.out.println(“方法异常时” + e);
}finally{
System.out.println(“后置方法”);
}
//将方法执行的返回值返回
return proceed;
}
}
以上就是使用AspectJ注解实现AOP切面的全部过程了,
在这里还有一点特别有意思的规定提醒大家,就是当你有多个切面类时,切面类的执行顺序是按照类名的首字符先后来执行的(不区分大小写)。
接下来我来和大家讲解一下实现AOP切面编程的另一种方法——基于XML配置的AOP实现,
==================================================================================
基于XML配置的AOP切面顾名思义就是摒弃了注解的使用,转而在IOC容器中配置切面类,这种声明是基于aop名称空间中的XML元素来完成的,
在bean配置文件中,所有的Spring AOP配置都必须定义在< aop:config>元素内部。对于每个切面而言,都要创建一个< aop:aspect>元素来为具体的切面实现引用后端bean实例。
切面bean必须有一个标识符,供< aop:aspect>元素引用。
所以我们在bean的配置文件中首先应该先将所需切面类加入到IOC容器中去,之后在aop的元素标签中进行配置。我们在使用注解进行开发的时候,五种通知注解以及切入点表达式这些在xml文件中同样是可以配置出来的。
切入点使用
< aop:pointcut>元素声明。
切入点必须定义在< aop:aspect>元素下,或者直接定义在< aop:config>元素下。
定义在< aop:aspect>元素下:只对当前切面有效
定义在< aop:config>元素下:对所有切面都有效
基于XML的AOP配置不允许在切入点表达式中用名称引用其他切入点。
在aop名称空间中,每种通知类型都对应一个特定的XML元素。
通知元素需要使用< pointcut-ref>来引用切入点,或用< pointcut>直接嵌入切入点表达式。
method属性指定切面类中通知方法的名称
具体使用可以看下面这里实例:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns=“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 http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">