AOP通知
Spring中常用的AOP通知有五种:
前置通知:在某方法调用之前执行;
后置通知:在后方法调用之后执行;
异常通知:在某方法发生异常时执行;
返回通知:在某方法进行返回时执行;
环绕通知:可手动进行控制以上四种通知的执行;
AOP配置
加入一下spring的jar包:
配置文件中引入xmlns:aop,加入<aop:aspectj-autoproxy></aop:aspectj-autoproxy>,具体配置如下:
<?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:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
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.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<context:component-scan base-package="cn.net.bysoft.lesson7">
</context:component-scan>
<!-- 自动生成aop代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
接下来编写测试类与切面类,做一个简单的接口,其中有4个方法,有是加减乘除四个函数,需求是在执行这4个函数的时候进行日志输入:
package cn.net.bysoft.lesson7;
public interface Arithmentic {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
package cn.net.bysoft.lesson7;
import org.springframework.stereotype.Component;
@Component()
public class ArithmenticImpl implements Arithmentic {
@Override
public int add(int i, int j) {
return i + j;
}
@Override
public int sub(int i, int j) {
return i - j;
}
@Override
public int mul(int i, int j) {
return i * j;
}
@Override
public int div(int i, int j) {
return i * j;
}
}
前置通知
编写一个日志切面类,提供前置通知:
package cn.net.bysoft.lesson7;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// 调用前
@Before("execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("The method " + joinPoint.getSignature().getName()
+ " begins with" + Arrays.asList(joinPoint.getArgs()));
}
}
其中beforeMethod使用@Before装饰,代表前置通知,在方法调用之前执行,@Before的参数是代表对cn.net.bysoft.lesson7这个包中的Arithmentic类的所有方法进行前置通知。
参数JoinPoint是切面的连接点,可以获得执行的方法的信息,如名称和参数值等,在前置通知时获得不到返回值,因为Arithmentic的方法还未执行。
编写测试类:
package cn.net.bysoft.lesson7;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Lesson7Test {
private AbstractApplicationContext ctx;
@Before
public void initApplicationContext() {
// 创建Spring容器
ctx = new ClassPathXmlApplicationContext(
"applicationContext-lesson7.xml");
}
@Test
public void test(){
Arithmentic arithmentic = ctx.getBean(Arithmentic.class);
int result = arithmentic.add(3, 6);
System.out.println(result);
ctx.close();
}
}
后置通知
加入后置通知,使用@After修饰方法,后置通知表示无论调用的方法是否异常,都会执行:
// 调用后,无论是否异常
@After("execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))")
public void afterMethod(JoinPoint joinPoint) {
System.out.println("The method " + joinPoint.getSignature().getName()
+ " begins with" + Arrays.asList(joinPoint.getArgs()));
}
@Test
public void testAfter(){
Arithmentic arithmentic = ctx.getBean(Arithmentic.class);
int result = arithmentic.div(3, 6);
System.out.println(result);
ctx.close();
}
进行测试,是否如果方法发生异常,也会执行后置通知,将除数设置为0:
异常通知
若方法如上面的代码会发生异常,也可以进行通知。使用@AfterThrowing修饰方法,value等于要调用的方法,throwing等于异常,如下:
@AfterThrowing(value = "execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))", throwing = "ex")
public void exceptionMethod(JoinPoint joinPoint, Exception ex) {
System.out.println("The method " + joinPoint.getSignature().getName()
+ " exception: " + ex);
}
返回通知
当方法执行成功,没有发生异常,并返回结果,可以使用返回通知进行日志输出。使用@AfterReturning修饰方法,value等于要进行切面的方法,returing等于返回值,如下:
// 返回通知
@AfterReturning(value = "execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))", returning = "result")
public void returnMethod(JoinPoint joinPoint, Object result) {
System.out.println("The method " + joinPoint.getSignature().getName()
+ " returning: " + result);
}
环绕通知
环绕通知功能很强大,可以将上面的几种通知进行自定义控制,它相当于我们编写的动态代理,使用@Around对方法进行修饰,传递一个ProceedingJoinPoint方法,可以决定是否决定执行目标方法,并且环绕通知必须有返回值,该返回值即为目标方法的返回值。代码如下:
// 环绕通知
@Around("execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))")
public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("aroundMethod");
return 100;
}
为了区分环绕通知与之前的通知,现将之前的通知代码注释掉:
结果为100,从此处可以看出环绕通知功能非常强大,接下来,完成环绕通知的代码,调用参数proceedingJoinPoint对象的proceed方法,可以执行目标方法,并获得返回值,并在该方法之前、之后、异常和返回时加入想要的通知:
// 环绕通知
@Around("execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))")
public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
Object result = null;
String methodName = proceedingJoinPoint.getSignature().getName();
try {
// 前置通知。
System.out.println("after " + methodName);
result = proceedingJoinPoint.proceed();
// 返回通知。
System.out.println("returing " + methodName);
} catch (Throwable e) {
// 异常通知。
System.out.println("exception " + e);
throw new RuntimeException(e);
}
// 后置通知。
System.out.println("afte " + methodName);
return result;
}
测试一下,执行目标方法,不带有异常,结果如下:
在测试一个目标方法发生异常的情况:
以上,就是spring常用的5种aop通知。
切面的优先级
如果有多个切面类,比如一个用来输出日志,一个用来验证参数是否合法,还有一些其他的……如何设置切面的优先级,比如验证参数的切面类要在输出日志的切面类之前执行,具体来看代码,编写一个验证切面类:
package cn.net.bysoft.lesson7;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ValidationAspect {
@Before("execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("validate args" + joinPoint.getSignature().getName()
+ " begins with" + Arrays.asList(joinPoint.getArgs()));
}
}
进行测试:
目前日志切面类在验证切面类之前执行,可以使用@Order对切面类进行修饰,输入需要进行排序:
@Order(1)
public class ValidationAspect {
@Order(2)
public class LoggingAspect {
再一次进行测试:
以上就是spring的aop的简单使用。
来源:oschina
链接:https://my.oschina.net/u/2450666/blog/668558