/*
设置下面方法在什么时候运行
@Before:在目标方法之前运行:前置通知
@After:在目标方法之后运行:后置通知
@AfterReturning:在目标方法正常返回之后:返回通知
@AfterThrowing:在目标方法抛出异常后开始运行:异常通知
@Around:环绕:环绕通知
当编写完注解之后还需要设置在哪些方法上执行,使用表达式
execution(访问修饰符 返回值类型 方法全称)
*/
package lwz;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Controller;
import java.lang.reflect.Method;
import java.util.Arrays;
@Aspect
@Controller
public class LogUtil {
@Before("execution(public int lwz.controller.MyCalculator.* (int,int))")
public static void beforeMethod(){
// System.out.println(method.getName()+"方法开始执行,参数是"+ Arrays.asList(object));
System.out.println("方法开始执行,参数是");
}
@AfterReturning("execution(public int lwz.controller.MyCalculator.* (int,int))")
public static void afterMethod(){
// System.out.println(method.getName()+"方法产生执行结果,参数是"+ Arrays.asList(object));
System.out.println("方法执行完成,结果是");
}
@AfterThrowing("execution(public int lwz.controller.MyCalculator.* (int,int))")
public static void exception(){
// System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
System.out.println("方法出现异常:");
}
@After("execution(public int lwz.controller.MyCalculator.* (int,int))")
public static void methodDone(){
// System.out.println(method.getName()+"方法执行结束");
System.out.println("方法执行结束");
}
}
package lwz.controller;
import org.springframework.stereotype.Service;
@Service
public class MyCalculator implements Calculator{
public int add(int i, int j) {
int result = i + j;
return result;
}
public int sub(int i, int j) {
int result = i - j;
return result;
}
public int mult(int i, int j) {
int result = i * j;
return result;
}
public int div(int i, int j) {
int result = i / j;
return result;
}
}
@Test
public void test2(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Calculator bean = context.getBean(Calculator.class);
System.out.println(bean.add(1,2));
}
切入点表达式
/**
在使用表达式的时候,除了之前的写法之外,还可以使用通配符的方式:
星号:
1、匹配一个或者多个字符
execution( public int com.mashibing.inter.My*alculator.*(int,int))
2、匹配任意一个参数,
execution( public int com.mashibing.inter.MyCalculator.*(int,*))
3、只能匹配一层路径,如果项目路径下有多层目录,那么*只能匹配一层路径
4、权限位置不能使用*,如果想表示全部权限,那么不写即可
execution( * com.mashibing.inter.MyCalculator.*(int,*))
点点:
1、匹配多个参数,任意类型参数
execution( * com.mashibing.inter.MyCalculator.*(..))
2、匹配任意多层路径
execution( * com.mashibing..MyCalculator.*(..))
在写表达式的时候,可以有N多种写法,但是有一种最偷懒和最精确的方式:
最偷懒的方式:execution(* *(..)) 或者 execution(* *.*(..))
最精确的方式:execution( public int com.mashibing.inter.MyCalculator.add(int,int))
除此之外,在表达式中还支持 &&、||、!的方式
&&:两个表达式同时
execution( public int com.mashibing.inter.MyCalculator.*(..)) && execution(* *.*(int,int) )
||:任意满足一个表达式即可
execution( public int com.mashibing.inter.MyCalculator.*(..)) && execution(* *.*(int,int) )
!:只要不是这个位置都可以进行切入
&&:两个表达式同时
execution( public int com.mashibing.inter.MyCalculator.*(..))
*/
通知方法的执行顺序
在之前的代码中大家一直对通知的执行顺序有疑问,其实执行的结果并没有错,大家需要注意:
1、正常执行:@Before—>@After—>@AfterReturning
2、异常执行:@Before—>@After—>@AfterThrowing
获取方法的详细信息
在上面的案例中,我们并没有获取Method的详细信息,例如方法名、参数列表等信息,想要获取的话其实非常简单,只需要添加JoinPoint参数即可。
上述操作获取了方法的信息,但是如果需要获取结果,还需要添加另外一个方法参数,并且告诉spring使用哪个参数来进行结果接收,也可以通过相同的方式来获取异常的信息。
@Aspect
@Controller
public class LogUtil {
@Before("execution(public int lwz.controller.MyCalculator.* (int,int))")
public static void beforeMethod(JoinPoint joinPoint){
String name = joinPoint.getSignature().getName();
// System.out.println(method.getName()+"方法开始执行,参数是"+ Arrays.asList(object));
System.out.println(name+"方法开始执行,参数是"+Arrays.asList(joinPoint.getArgs()));
}
@AfterReturning(value = "execution(public int lwz.controller.MyCalculator.* (int,int))",returning = "result")
public static void afterMethod(JoinPoint joinPoint,Object result){
String name = joinPoint.getSignature().getName();
// System.out.println(method.getName()+"方法产生执行结果,参数是"+ Arrays.asList(object));
System.out.println(name+"方法执行完成,结果是"+result);
}
@AfterThrowing(value = "execution(public int lwz.controller.MyCalculator.* (int,int))",throwing = "e")
public static void exception(JoinPoint joinPoint,Exception e){
String name = joinPoint.getSignature().getName();
// System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
System.out.println(name+"方法出现异常:"+e.getMessage());
}
@After("execution(public int lwz.controller.MyCalculator.* (int,int))")
public static void methodDone(JoinPoint joinPoint){
String name = joinPoint.getSignature().getName();
// System.out.println(method.getName()+"方法执行结束");
System.out.println(name+"方法执行结束");
}
}
spring对通过方法的要求
spring对于通知方法的要求并不是很高,你可以任意改变方法的返回值和方法的访问修饰符,但是唯一不能修改的就是方法的参数,会出现参数绑定的错误,原因在于通知方法是spring利用反射调用的,每次方法调用得确定这个方法的参数的值。
表达式的抽取
如果在实际使用过程中,多个方法的表达式是一致的话,那么可以考虑将切入点表达式抽取出来:
a、随便生成一个没有实现的返回void的空方法
b、给方法上标注@Potintcut注解
@Pointcut("execution(public int lwz.controller.MyCalculator.* (int,int))")
public void myPointCut(){}
@Before("myPointCut()")
@AfterReturning(value = "myPointCut()",returning = "result")
@AfterThrowing(value = "myPointCut()",throwing = "e")
@After("myPointCut()")
注意
环绕通知在遇到异常时先输出异常通知,之后输出结束通知
而普通通知在遇到异常时先输出结束通知,后输出异常
环绕通知的使用
@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) {
String name = proceedingJoinPoint.getSignature().getName();
Object[] args = proceedingJoinPoint.getArgs();
Object proceed=null;
try {
System.out.println(name+"1方法开始执行,参数是"+Arrays.asList(args));
//利用反射调用目标方法,就是method.invoke()
proceed = proceedingJoinPoint.proceed(args);
System.out.println(name+"1方法返回,结果是"+proceed);
} catch (Throwable throwable) {
String message = throwable.getMessage();
System.out.println(name+"1方法发生异常"+message);
} finally {
System.out.println(name+"1方法执行结束");
}
return proceed;
}
在spring中,默认是按照切面名称的字典顺序进行执行的,但是如果想自己改变具体的执行顺序的话,可以使用@Order注解来解决,数值越小,优先级越高。
@Order(1)
public class LogUtil{}
关于事务我自己的理解
1事务 2事务 3事务(调用1、2事务)
requred:
1事务requred 报错,2事务无,3事务整体回滚
1事务requred 报错, 2事务无,3事务捕获异常仍整体回滚
1事务requred 无,2事务无,3事务报错整体回滚
1事务requred 无,2事务报错,3事务整体回滚
总结:requred依赖于外部事物,外部事物回滚他也会回滚,外部事物也依赖于requred(requred是外部事务的一部分)所以子事务报错=外部事务报错导致回滚
requres_new:
1事务requres_new 报错,2事务无,3事务不会回滚
1事务requres_new 不报错,无论2、3事务发生什么,1事务不会回滚
总结:requres_new独立于外部事物(与外部事务没有关系)外部事物不影响requres_new事务,requres_new事务也不影响外部事物
nested
1事务 nested 报错,2事务无,2、3事务不会回滚,1事务回滚
1事务 nested 报错,2事务无,3事务捕获异常,1事务回滚,2、3事务不回滚
1事务 nested 无,2事务无,3事务报错整体回滚
总结:当外部事物报错时会整体回滚,如果只是单个子事务报错那就单独回滚,既依赖又独立
supports
1事务 supports报错(无2、3事务)不会回滚
1事务 supports报错 3事务捕获 不会回滚
1事务 supports报错 3事务不捕获 回滚
1事务 supports无 3事务报错 回滚
总结:如果有外部事务那就使用外部事物,如果没有外部事物就等于没有事务
not_supports
1事务 not_supports无论是否报错,3事务无论是否存在,都不回滚(等于永不开启事务)
总结:永不开启事务
mandatory
总结:如果没有外层事务就会抛异常,有外层事务则使用外层事务
never
总结:和not_support相似,只不过如果有外部事务则会报异常