开始使用AspectJ
1. [掌握]@AfterReturning 后置通知-注解有 returning 属性
在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后 置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变 量最好为 Object 类型,因为目标方法的返回值可能是任何类型。
项目结构如下:
2.实现步骤
2.1 首先还是接口(但是在其中增加方法)
package com.bjpowernode.ba02;
public interface SomeService {
void doSome(String name, Integer age);
String doOther(String name,Integer age);
Student doOther2(String name,Integer age);
}
2.2 接口实现类如下:
package com.bjpowernode.ba02;
//目标类
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name,Integer age) {
//给doSome方法增加一个功能,在doSome()执行之前, 输出方法的执行时间
System.out.println("====目标方法doSome()====");
}
@Override
public String doOther(String name, Integer age) {
System.out.println("====目标方法doOther()====");
return "abcd";
}
@Override
public Student doOther2(String name, Integer age) {
Student student = new Student();
student.setName("lisi");
student.setAge(20);
return student;
}
}
2.3 定义切面类
其中在@AfterReturning中还可以使用 JoinPoint 但是必须放在第一个参数的位置,并且还可以在参数中通过returning = "res"拿到被代理对象的返回值
package com.bjpowernode.ba02;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.Date;
/**
* @Aspect : 是aspectj框架中的注解。
* 作用:表示当前类是切面类。
* 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
* 位置:在类定义的上面
*/
@Aspect
public class MyAspect {
/**
* 后置通知定义方法,方法是实现切面功能的。
* 方法的定义要求:
* 1.公共方法 public
* 2.方法没有返回值
* 3.方法名称自定义
* 4.方法有参数的,推荐是Object ,参数名自定义
*/
/**
* @AfterReturning:后置通知
* 属性:1.value 切入点表达式
* 2.returning 自定义的变量,表示目标方法的返回值的。
* 自定义变量名必须和通知方法的形参名一样。
* 位置:在方法定义的上面
* 特点:
* 1。在目标方法之后执行的。
* 2. 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
* Object res = doOther();
* 3. 可以修改这个返回值
*
* 后置通知的执行
* Object res = doOther();
* 参数传递: 传值, 传引用
* myAfterReturing(res);
* System.out.println("res="+res)
*
*/
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",
returning = "res")
public void myAfterReturing( JoinPoint jp ,Object res ){
// Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
System.out.println("后置通知:方法的定义"+ jp.getSignature());
System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);
if(res.equals("abcd")){
//做一些功能
} else{
//做其它功能
}
//修改目标方法的返回值, 看一下是否会影响 最后的方法调用结果
if( res != null){
res = "Hello Aspectj";
}
}
//作业
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther2(..))",
returning = "res")
public void myAfterReturing2(Object res){
// Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);
//修改目标方法的返回值, 看一下是否会影响 最后的方法调用结果
//如果修改了res的内容,属性值等,是不是会影响最后的调用结果呢
}
}
2.4 最终结果执行如下:
3.掌握]@Around 环绕通知-增强方法有 ProceedingJoinPoint 参数使用方法就类似动态代理的那种
在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并 且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个 proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法 的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。 接口增加方法:
3.1 首先是接口
package com.bjpowernode.ba04;
import com.bjpowernode.ba02.Student;
public interface SomeService {
void doSome(String name, Integer age);
String doOther(String name, Integer age);
Student doOther2(String name, Integer age);
String doFirst(String name, Integer age);
void doSecond();
}
3.2 其次是实现类
package com.bjpowernode.ba04;
import com.bjpowernode.ba02.Student;
//目标类
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name,Integer age) {
//给doSome方法增加一个功能,在doSome()执行之前, 输出方法的执行时间
System.out.println("====目标方法doSome()====");
}
@Override
public String doOther(String name, Integer age) {
System.out.println("====目标方法doOther()====");
return "abcd";
}
@Override
public Student doOther2(String name, Integer age) {
Student student = new Student();
student.setName("lisi");
student.setAge(20);
return student;
}
@Override
public String doFirst(String name, Integer age) {
System.out.println("====业务方法doFirst()====");
return "doFirst";
}
@Override
public void doSecond() {
System.out.println("执行业务方法doSecond()" + (10/0));
}
}
3.3 接下来我们看代理方法
里面方法很多,除了可以在自己的方法之前或者之后执行新增的业务逻辑,可以通过传入对象做出判断,然后输出不同的内容。
package com.bjpowernode.ba03;
import org.apache.ibatis.session.SqlSession;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.Date;
/**
* @Aspect : 是aspectj框架中的注解。
* 作用:表示当前类是切面类。
* 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
* 位置:在类定义的上面
*/
@Aspect
public class MyAspect {
/**
* 环绕通知方法的定义格式
* 1.public
* 2.必须有一个返回值,推荐使用Object
* 3.方法名称自定义
* 4.方法有参数,固定的参数 ProceedingJoinPoint
*/
/**
* @Around: 环绕通知
* 属性:value 切入点表达式
* 位置:在方法的定义什么
* 特点:
* 1.它是功能最强的通知
* 2.在目标方法的前和后都能增强功能。
* 3.控制目标方法是否被调用执行
* 4.修改原来的目标方法的执行结果。 影响最后的调用结果
*
* 环绕通知,等同于jdk动态代理的,InvocationHandler接口
*
* 参数: ProceedingJoinPoint 就等同于 Method
* 作用:执行目标方法的
* 返回值: 就是目标方法的执行结果,可以被修改。
*
* 环绕通知: 经常做事务, 在目标方法之前开启事务,执行目标方法, 在目标方法之后提交事务
*/
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
String name = "";
//获取第一个参数值
Object args [] = pjp.getArgs();
if( args!= null && args.length > 1){
Object arg= args[0];
name =(String)arg;
}
//实现环绕通知
Object result = null;
System.out.println("环绕通知:在目标方法之前,输出时间:"+ new Date());
//1.目标方法调用
if( "zhangsan".equals(name)){
//符合条件,调用目标方法
result = pjp.proceed(); //method.invoke(); Object result = doFirst();
}
System.out.println("环绕通知:在目标方法之后,提交事务");
//2.在目标方法的前或者后加入功能
//修改目标方法的执行结果, 影响方法最后的调用结果
if( result != null){
result = "Hello AspectJ AOP";
}
//返回目标方法的执行结果
return result;
}
}
3.4然后我们看执行结果
其实这种方式就是对动态代理又做了一层封装,看起来很吊,实际上吊的一批 妙,实在是妙!