SpringAOP

 /*
    设置下面方法在什么时候运行
        @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相似,只不过如果有外部事务则会报异常

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值