AOP进阶二(切入点表达式与连接点)

一.切入点表达式

1.概念:描述切入点方法的表达式,主要用来决定项目中的哪些方法需要加入通知。

2.常见形式:execution(...)和@annotation(...)

3.execution

execution主要根据方法的返回值,包名,类名,方法名,方法参数等信息来匹配,语法为

execution(访问修饰符[可省略] 返回值[可省略] 包名.类名[可省略].方法名(方法参数) throws 异常(可省略))

注:

[1]可省略的部分:访问修饰符,包名.类名,throws 异常      必须有的部分:方法名

[2]可以使用通配符描述切入点:

*:单个独立的任意符号(不一定是*),可以通配任意返回值,包名,类名,方法名,任意类型的一个参数,也可以通配包,类,方法名的一部分(下面【4】的第一点需要用到)

..:多个连续的任意符号,可以通配任意层级的包,或任意类型,任意个数的参数

区别(*号是代替一个,而..是代替多个)

举几个例子就知道了:

- 未修改版 
  execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))


- 使用`*`代替返回值类型
  execution(* com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))


- 使用`*`代替包名(一层包使用一个`*`)
  execution(* com.itheima.*.*.DeptServiceImpl.delete(java.lang.Integer))


- 使用`..`省略包名
  execution(* com..DeptServiceImpl.delete(java.lang.Integer))    


- 使用`*`代替类名
  execution(* com..*.delete(java.lang.Integer))   


- 使用`*`代替方法名
  execution(* com..*.*(java.lang.Integer))   


- 使用 `*` 代替参数
  execution(* com.itheima.service.impl.DeptServiceImpl.delete(*))

  
- 使用`..`省略参数
  execution(* com..*.*(..))

[3]可以使用且(&&),或(||),非(!)来组合比较复杂的切入点表达式

[4]书写规范:

*所有业务方法名在命名时尽量规范,方便切入点表达式的快速匹配。如:查询类方法都是find开头,更新类方法都是update开头。

  execution(* com.itheima.service.impl.DeptServiceImpl.find*(..))

*描述切入点方法通常基于接口描述,而不是直接表述实现类,增强拓展性。

  execution(* com.itheima.service.DeptService.*(..))

*在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用..,而是使用*匹配单个包。

  execution(* com.itheima.*.*.DeptServiceImpl.find*(..))

4.@annotation

如果我们要匹配多个无规则的方法,比如:list()和delete()这两个方法,用切入点表达式要将两个切入点组合在一起完成需求,比较繁琐,我们可以借助与另一种切入点表达式annotation来描述这一类的切入点,从而来简化切入点表达式的书写。

实现步骤:

1.编写自定义注解

2.在业务类要作为连接点的方法上添加自定义注解

3.在切面类使用@annotation注解中书写自定义注解的方式来指定连接点。

例子:

**自定义注解**:MyLog

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}



**业务类**:DeptServiceImpl

@Slf4j
@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    private DeptMapper deptMapper;

    @Override
    @MyLog //自定义注解(表示:当前方法属于目标方法)
    public List<Dept> list() {
        List<Dept> deptList = deptMapper.list();
        //模拟异常
        //int num = 10/0;
        return deptList;
    }

    @Override
    @MyLog  //自定义注解(表示:当前方法属于目标方法)
    public void delete(Integer id) {
        //1. 删除部门
        deptMapper.delete(id);
    }


    @Override
    public void save(Dept dept) {
        dept.setCreateTime(LocalDateTime.now());
        dept.setUpdateTime(LocalDateTime.now());
        deptMapper.save(dept);
    }

    @Override
    public Dept getById(Integer id) {
        return deptMapper.getById(id);
    }

    @Override
    public void update(Dept dept) {
        dept.setUpdateTime(LocalDateTime.now());
        deptMapper.update(dept);
    }
}



**切面类**

@Slf4j
@Component
@Aspect
public class MyAspect6 {
    //针对list方法、delete方法进行前置通知和后置通知

    //前置通知
    @Before("@annotation(com.itheima.anno.MyLog)")
    public void before(){
        log.info("MyAspect6 -> before ...");
    }

    //后置通知
    @After("@annotation(com.itheima.anno.MyLog)")
    public void after(){
        log.info("MyAspect6 -> after ...");
    }
}

两种方法各有优缺点,如果方法名有规则,选第一种合适,如果方法名没有规则,选第二种合适。

二.连接点

1.连接点可以简单理解为可以被AOP控制的方法,在SpringAOP中,连接点又特指方法的执行。

2.在Spring中类抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名,方法名,方法参数等。
*对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint类型(有proceed方法)
*对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型(没有proceed方法)

例子:

@Slf4j
@Component
@Aspect
public class MyAspect7 {

    @Pointcut("@annotation(com.itheima.anno.MyLog)")
    private void pt(){}
   
    //前置通知
    @Before("pt()")
    public void before(JoinPoint joinPoint){
        log.info(joinPoint.getSignature().getName() + " MyAspect7 -> before ...");
    }
    
    //后置通知
    @Before("pt()")
    public void after(JoinPoint joinPoint){
        log.info(joinPoint.getSignature().getName() + " MyAspect7 -> after ...");
    }

    //环绕通知
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //获取目标类名
        String name = pjp.getTarget().getClass().getName();
        log.info("目标类名:{}",name);

        //目标方法名
        String methodName = pjp.getSignature().getName();
        log.info("目标方法名:{}",methodName);

        //获取方法执行时需要的参数
        Object[] args = pjp.getArgs();
        log.info("目标方法参数:{}", Arrays.toString(args));

        //执行原始方法
        Object returnValue = pjp.proceed();

        return returnValue;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值