前置通知
@Before 前面已经实例过了,略
后置通知
后置通知方法实现切面功能:
1.公共方法
2.方法没有返回值
3.方法名称自定义
4.方法可以有参数,推荐是Object,参数名称自定义
@AfterReturning后置通知
属性:
1、value切入点表达式
2、returning自定义的变量,表示目标方法的返回值的
自定义变量名必须和通知方法的形参一样
特点:
1、在目标方法后执行
2、能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
3、可以修改这个返回值
@AfterReturning(
value = "execution(public void org.example.Hello.sayHello(String))",
returning = "res"
)
public void after(Object res){
//Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
//功能代码
System.out.println("切面功能,执行之后"+new Date());
}
后置通知修改目标方法返回值
@AfterReturning
如果要用JoinPoint也可以,但必须写在第一个参数
此时给hello一个返回值
public String sayHello(String name){
System.out.println(name+":pasaki");
return name;
}
在切面方法中利用后置通知修改返回值
@AfterReturning(
value = "execution(public String org.example.Hello.sayHello(String))",
returning = "res"
)
public void after(Object res){
//Object res:是 目标方法执行后的返回值,根据返回值做你的切面的功能处理
if(res!=null){
res="yongen";
}
//功能代码
System.out.println("切面功能,执行之后"+new Date());
}
测试
//1、指定spring配置文件的名称
String config="spring_total" + ".xml";
//2、创建表示spring容器的对象,ApplicationContext
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
//3、通过容器获取对象
HelloInterface hello = (HelloInterface) context.getBean("hello");
String result=hello.sayHello("yasuo");
System.out.println(result);
然后我们发现他并没有变!!!!
因为这个执行相当于是执行
hello.sayHello(String name)
然后把name当作参数传递到我的 after(String name)
name是一个String类型
如果参数是一个对象的引用类呢?
假设有个Student对象
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
现在在Hello里创建一个学生把他作为返回对象
public Student sayHello(String name){
Student riven=new Student("riven",18);
System.out.println(name+":pasaki");
return riven;
}
在切面类中修改返回对象
@AfterReturning(
value = "execution(public Student org.example.Hello.sayHello(String))",
returning = "res"
)
public void myBefore(Object res){
//Object res:是 目标方法执行后的返回值,根据返回值做你的切面的功能处理
if(res!=null){
((Student) res).setName("liliya");
((Student) res).setAge(23);
}
//功能代码
System.out.println("切面功能,执行之后"+new Date());
}
测试
//1、指定spring配置文件的名称
String config="spring_total" + ".xml";
//2、创建表示spring容器的对象,ApplicationContext
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
//3、通过容器获取对象
HelloInterface hello = (HelloInterface) context.getBean("hello");
Student result=hello.sayHello("yasuo");
System.out.println(result);
变成liliya了,嘿嘿!
综上:
我们的String没有改变是因为String类型是不可变的,变的是引用,改变了后不是同一个对象
但是!!!
把对象引用作为参数后,他们的引用指向同一个对象,修改后,那当然原对象被修改啦,也就能看到改变
环绕通知
环绕通知方法实现切面功能:
1.公共方法
2.方法必须有返回值,推荐使用Object
3.方法名称自定义
4.方法可以有参数,固定参数ProceedingJoinPoint
特点:
1、是功能最强的通知
2、在目标方法的前后都能增强功能
3、控制目标方法是否被调用执行
4、修改原来的目标方法的执行结果。影响最后的调用结果
环绕通知等用于Jdk的动态代理,invocationHandler接口
参数: ProceedingJoinPoint等同于Method
返回值: 目标方法的执行结果,可以修改
@Around(value = "execution(public Student org.example.Hello.sayHello(String))")
public Object around(ProceedingJoinPoint point) throws Throwable {
Object result=null;
System.out.println("环绕通知:在方法执行之前:"+new Date());
//1、目标方法调用
result=point.proceed();//method.invoke
System.out.println("环绕通知:在方法执行之后:"+new Date());
//修改
((Student)result).setName("liliya");
return result;
}
环绕通知强大之处在于可以决定切面方法在什么情况下需要执行,什么情况下不需要执行
例如用ProceedingJoinPoint获取到方法的参数,通过参数判断是否需要执行目标方法。
@Around(value = "execution(public Student org.example.Hello.sayHello(String))")
public Object around(ProceedingJoinPoint point) throws Throwable {
String name="";
//获取第一个参数值
Object args[]=point.getArgs();
if(args!=null && args.length>=1){
Object arg=args[0];
name=(String)arg;
}
System.out.println(name);
Object result=null;
System.out.println("环绕通知:在方法执行之前:"+new Date());
//判断是否需要目标方法调用
if(name.equals("yasuo")){
result=point.proceed();//method.invoke
}
System.out.println("环绕通知:在方法执行之后:"+new Date());
return result;
}
这个时候只有参数是"yasuo"的时候才能调用目标方法。
环绕通知修改返回值
环绕通知强大在可以修改String
public String sayHello(String name){
System.out.println(name+":pasaki");
return name;
}
修改result
@Around(value = "execution(public String org.example.Hello.sayHello(String))")
public Object around(ProceedingJoinPoint point) throws Throwable {
Object result=null;
System.out.println("环绕通知:在方法执行之前:"+new Date());
//1、目标方法调用
result=point.proceed();//method.invoke
result="hhhhhhhhhhhhhh";
System.out.println("环绕通知:在方法执行之后:"+new Date());
return result;
}
public void shouldAnswerWithTrue()
{
//1、指定spring配置文件的名称
String config="spring_total" + ".xml";
//2、创建表示spring容器的对象,ApplicationContext
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
//3、通过容器获取对象
HelloInterface hello = (HelloInterface) context.getBean("hello");
String result=hello.sayHello("yasuo");
System.out.println(result);
}
==这里调用的sayHello实际上被代理替换成了around方法,所以最后得到的是around里的result,区别于AfterReturning ==
异常通知
异常通知:发生异常时才执行,不发生异常不执行
相当于执行try{ ……}catch{……}
异常通知方法实现切面功能:
1.公共方法
2.没有返回值
3.方法名称自定义
4.方法可以没有参数,有就是JpinPoint
@AfterThrowing异常通知
属性:
1、value切入点表达式
2、throwing:自定义变量,表示目标方法抛出的异常对象
变量名必须和方法的参数名一样
特点:
1、在目标方法抛出异常时执行的
2、可以做异常的监控程序,监控目标方法执行时是不是有异常。
如果有异常,可以发送邮件,短信进行通知
在目标方法中抛出一个异常
public String sayHello(String name){
System.out.println(name+":pasaki"+10/0);
return name;
}
异常通知接收异常
@AfterThrowing(value = "execution(public String org.example.Hello.sayHello(String))",throwing="ex")
public void afterThrowing(Exception ex){
System.out.println("异常通知:方法发生异常时执行"+ex.getMessage());
}
最终通知
最终通知方法实现切面功能:
1.公共方法
2.没有返回值
3.方法名称自定义
4.方法可以没有参数,有就是JpinPoint
特点:
1、总会执行(无论是否有异常)
2、在目标方法之后执行的
相当于try{}catch{}finally{}
一般做资源的清除工作
@After(value = "execution(public String org.example.Hello.sayHello(String))")
public void after(){
System.out.println("最终通知");
}
复用切入点表达式
@Pointcut: 定义和管理切入点的,项目中有多个切入点表达式需要复用
特点:
当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名
其他通知中,value属性就可以使用这个方法名称,代替切入点表达式了
//此时cut就是切入点表达式的别名
@Pointcut("execution(public String org.example.Hello.sayHello(String))\"")
public void cut(){}
@After(value = "cut()")
public void after(){
System.out.println("最终通知");
}
@Before(value = "cut()")
public void before(){
System.out.println("前置通知");
}