AOP&面向切面编程

定义

面向切面编程。Aspect

好处

业务型代码和非业务型代码 解耦。

在不改变原有业务代码的基础上做增强!

入门操作

1、导入依赖



    org.springframework.boot
    spring-boot-starter-aop

2、编写切面类

XxxAspect

3、在类头上

加注解

@Aspet

@Component

4、方法头上

加注解

@Around(xxxxxx)

指定要增强的方法有哪些,划定范围

通知类型

@Around

目标方法执行前通知

目标方法执行后通知,有异常则不会执行

@Aspect
@Component
@Slf4j
public class MyAspect {

    //环绕通知
    @Around("execution(* com.itheima.springboot_web.service.*.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("around before ...");

        //调用目标对象的原始方法执行
        Object result = proceedingJoinPoint.proceed();

        //原始方法如果执行时有异常,环绕通知中的后置代码不会在执行了

        log.info("around after ...");
        return result;
    }
}

@Before

目标方法执行前通知

@Aspect
@Component
@Slf4j
public class MyAspect {

    //前置通知
    @Before("execution(* com.itheima.springboot_web.service.*.*(..))")
    public void before(JoinPoint joinPoint){
        log.info("before ...");

    }
}

@AfterReturning

目标方法返回后通知,有异常则不会执行

@Aspect
@Component
@Slf4j
public class MyAspect {

    //返回后通知(程序在正常执行的情况下,会执行的后置通知)
    @AfterReturning("execution(* com.itheima.springboot_web.service.*.*(..))")
    public void afterReturning(JoinPoint joinPoint){
        log.info("afterReturning ...");
    }
}

@After

目标方法后置通知,有无异常都会执行!

@Aspect
@Component
@Slf4j
public class MyAspect {

    //后置通知
    @After("execution(* com.itheima.springboot_web.service.*.*(..))")
    public void after(JoinPoint joinPoint){
        log.info("after ...");
    }
}

@AfterThrowing

有异常时才会通知

@Aspect
@Component
@Slf4j
public class MyAspect {

    //异常通知(程序在出现异常的情况下,执行的后置通知)
    @AfterThrowing("execution(* com.itheima.springboot_web.service.*.*(..))")
    public void afterThrowing(JoinPoint joinPoint){
        log.info("afterThrowing ...");
    }
}

通知顺序

默认按照类名排序

@Order(数字) 数字越小越优先执行

切入点表达式

execution(权限 返回值 多级报名.类名.方法名(形参列表))

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

符号含义

*可以代表:

任意一个形参

任意一个返回值

任意一个方法名

任意一个类名

任意一个包名

..可以代表:

任意多个参数

任意多级包名

提取切入点表达式

@PointCut("execution(* com.. * . *(..)")

注意事项

可以使用&& || !

注解实现切入点表达式

1、定义一个注解

package com.zsh.springboot_web.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

2、@annotation(自定义注解全类名)

package com.zsh.springboot_web.aspect;

import com.alibaba.fastjson.JSONObject;
import com.itheima.springboot_web.mapper.OperateLogMapper;
import com.itheima.springboot_web.pojo.OperateLog;
import com.itheima.springboot_web.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private OperateLogMapper operateLogMapper;

    @Around("@annotation(com.itheima.springboot_web.annotation.Log)")
    public Object log(ProceedingJoinPoint pjp) throws Throwable {
        // 操作人ID - 当前登录员工ID
        // 获取请求头中的jwt令牌,解析令牌
        String jwt = request.getHeader("token");
        Claims claims = JwtUtils.parseJWT(jwt);
        Integer operateUser = (Integer) claims.get("id");

        // 操作时间
        LocalDateTime operateTime = LocalDateTime.now();

        // 操作类名
        String className = pjp.getTarget().getClass().getName();

        // 操作方法名
        String methodName = pjp.getSignature().getName();

        // 操作方法参数
        Object[] args = pjp.getArgs();
        String methodParams = Arrays.toString(args);

        long begin = System.currentTimeMillis();

        // 调用原始目标方法运行
        Object result = pjp.proceed();
        long end = System.currentTimeMillis()

        //方法返回值
        String returnValue = JSONObject.toJSONString(result);

        //操作耗时
        Long costTime = end - begin;

        //记录操作日志
        OperateLog operateLog = new OperateLog(null,operateUser,operateTime,className,methodName,methodParams,returnValue,costTime);

        operateLogMapper.insert(operateLog);

        log.info("AOP记录操作日志: {}" , operateLog);
        return result;
    }

}

3、在需要被增强的方法上加入注解

package com.zsh.springboot_web.service.impl;

import com.itheima.springboot_web.annotation.Log;
import com.itheima.springboot_web.mapper.DeptMapper;
import com.itheima.springboot_web.mapper.EmpMapper;
import com.itheima.springboot_web.pojo.Dept;
import com.itheima.springboot_web.service.DeptLogService;
import com.itheima.springboot_web.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class DeptServiceImpl implements DeptService {

    @Autowired
    private DeptMapper deptMapper;

    @Autowired
    private EmpMapper empMapper;

    @Autowired
    private DeptLogService deptLogService;

    @Override
    public List depts() {
        List depts = deptMapper.depts();
        return depts;
    }

    /*
    * rollbackfor:默认为运行时异常回滚:RuntimeException.class
    *             可以赋值为Exception.class
    * */
    @Transactional(rollbackFor = Exception.class)   // 方法开启事务,方法内没有任何异常
    @Override
    @Log
    public void deleteById(Integer id) {
        try {
            // 部门删除
            deptMapper.deleteById(id);
            // 删除该部门的员工
            empMapper.deleteDeptId(id);
            int i = 3/0;
        }finally {
            // 记录日志
            deptLogService.add(id);
        }
    }

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

    @Override
    public Dept findById(Integer id) {
        Dept dept = deptMapper.findById(id);
        return dept;
    }

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

}

你可能感兴趣的:(java,前端,服务器,spring)