面向切面编程

markdown 复制代码
1. 增删改查  三层干啥
2. 文件上传  第三方api(接口) 调用准备 -- 官方代码  -- 工具类
3. 登录鉴权  登录(查询 密码) 鉴权(拦截器)
	登录完成后--后台生成token返回给浏览器--每次浏览器发请求时候携带token--服务器需要使用拦截器拦截请求,获取token--校验
	token生成   token校验
4. Springboot自动装配

面向切面编程

AOP( 面向切面编程 )是一种思想,它的目的就是在不修改源代码的基础上,对原有功能进行增强。

在你给大部分方法需要添加一部分统一功能的时候

入门案例

使用Aop完成在业务层类中的方法上打印日志

导入初始工程

创建日志类

单元测试

通知类型

四大通知

四大通知描述的就是增强方法在切点方法的什么位置上执行

  • 前置通知(before):增强方法在切点运行之前执行
  • 返回后通知(after-returning):增强方法在某切点正常完成后执行的通知,不包括抛出异常的情况
  • 异常后通知(after-throwing):增强方法在某切点抛出异常退出时执行的通知
  • 后置通知(after):增强方法在某切点退出的时候执行的通知(不论是正常返回还是异常退出)
java 复制代码
try{
	前置通知(before)
      
	//切点执行位置
      
	返回后通知(after-returning)
}catch(Execption e){
	异常后通知(after-throwing)
}finally{
	后置通知(after)
}

环绕通知

它是一种特殊的通知,他允许以编码的形式实现四大通知

切点表达式

切点表达式用于挑选切点

execution

execution() :指定一组规则来匹配某些类中的方法,匹配中的就是切点

*  代表一个或多个位置
.. 代表0个或多个位置

@annotation

@annotation:指定一个注解,凡是标有此注解的方法都是切点

① 自定义注解

② 在需要作为切点的方法上添加注解

③ 设置切点挑选

日志记录

本小节我们要实现的功能是,要记录controller中增 删 该方法的运行日志保存到日志表中

日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法作用、方法运行时参数、返回值、方法执行时长

准备工作

创建数据表

sql 复制代码
-- 操作日志表
create table operate_log(
    id bigint unsigned primary key auto_increment comment 'ID',
    class_name varchar(100) comment '操作的类名',
    method_name varchar(100) comment '操作的方法名',
    method_desc varchar(100) comment '方法用途',
    method_params varchar(1000) comment '方法参数',
    return_value varchar(2000) comment '返回值',
    operate_user int unsigned comment '操作人ID',
    operate_time datetime comment '操作时间',
    cost_time bigint comment '方法执行耗时, 单位:ms'
) comment '操作日志表';

创建日志类

java 复制代码
package com.itheima.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
    private Integer id; //ID
    private String className; //操作类名
    private String methodName; //操作方法名
    private String methodDesc; //方法用途
    private String methodParams; //操作方法参数
    private String returnValue; //操作方法返回值
    private Integer operateUser; //操作人ID
    private LocalDateTime operateTime; //操作时间
    private Long costTime; //操作耗时
}

创建日志的Mapper

java 复制代码
package com.itheima.mapper;

import com.itheima.pojo.OperateLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OperateLogMapper {

    //插入日志数据
    @Insert("insert into operate_log (operate_user, operate_time, class_name, method_name,method_desc, method_params, return_value, cost_time) " +
            "values (#{operateUser}, #{operateTime}, #{className}, #{methodName},#{methodDesc}, #{methodParams}, #{returnValue}, #{costTime});")
    public void insert(OperateLog log);
}

制作切面

自定义注解

java 复制代码
package com.itheima.anno;

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

//自定义注解
@Retention(RetentionPolicy.RUNTIME)//此注解保留到运行阶段
@Target(ElementType.METHOD)//此注解标注在方法上
public @interface LogAnno {
    
    String methodDesc() default "";//用于声明方法的作用
} 

标识切点

创建切面

java 复制代码
package com.itheima.aspect;

import com.itheima.anno.LogAnno;
import com.itheima.mapper.OperateLogMapper;
import com.itheima.pojo.OperateLog;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;

@Component
@Aspect//切面
public class LogAspect {

    @Autowired
    private OperateLogMapper operateLogMapper;

    //自定义注解
    @Pointcut("@annotation(com.itheima.anno.LogAnno)")
    public void pt() {
    }

    //环绕通知
    @Around("pt()")
    public Object logAround(ProceedingJoinPoint pjp) {
        OperateLog operateLog = new OperateLog();

        //操作时间
        operateLog.setOperateTime(LocalDateTime.now());
        //全类名
        operateLog.setClassName(pjp.getTarget().getClass().getName());
        //请求参数
        operateLog.setMethodParams(Arrays.toString(pjp.getArgs()));

        MethodSignature ms = (MethodSignature) pjp.getSignature();
        Method method = ms.getMethod();

        //方法名
        operateLog.setMethodName(method.getName());
        //方法作用(从LogAnno注解属性中获取)
        operateLog.setMethodDesc(method.getAnnotation(LogAnno.class).methodDesc());

        //todo 操作人id

        Object obj = null;
        long start = new Date().getTime();
        try {
            obj = pjp.proceed();

            //方法的执行结果
            operateLog.setReturnValue(obj.toString());
            return obj;
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            long end = new Date().getTime();
            //方法执行时长
            operateLog.setCostTime(end - start);

            //保存日志
            operateLogMapper.insert(operateLog);
        }
    }
}

ThreadLocal

ThreadLocal称为线程局部变量,可以为每个线程单独提供一份存储空间

markdown 复制代码
* 特点:线程之内,数据共享;线程之间,数据隔离
* 方法:
	public void set(T value)    设置当前线程的线程局部变量的值
	public T get() 		 	   返回当前线程所对应的线程局部变量的值
	public void remove()        移除当前线程的线程局部变量

添加工具类

ThreadLocal操作工具类,可以完成对其添加、获取、清理工作

修改拦截器

修改拦截器代码,添加ThreadLocal相关代码

修改切面

修改切面代码,从ThreadLocal中获取用户id

声明式事务

事务回顾

事务是一组操作的集合,它是一个不可分割的工作单位,这些操作 要么同时成功,要么同时失败。

markdown 复制代码
开启事务(一组操作开始前,开启事务):start transaction / begin ;

提交事务(这组操作全部成功后,提交事务):commit ;

回滚事务(中间任何一个操作出现异常,回滚事务):rollback ;
java 复制代码
try{
    
    begin;//告诉mysql后面的这些sql语句的提交 你就别管了,我自己来管理
    
    //业务操作 增  删  改
    //sql
    //sql
    //....
    
    commit;//自己提交
    
}catch(ex){
    
    rollback;//自己回滚
}

事务管理

注解:@Transactional

位置:业务(service)层的方法上、类上、接口上

作用:将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务

事务属性

rollbackFor

默认情况下,只有出现 RuntimeException 才回滚异常,rollbackFor属性用于控制让非运行时异常也回滚。

propagation

propagation称为事务传播行为,表示当一个事务方法被另一个事务方法调用时,应该如何进行事务控制

Spring支持通过配置的形式来实现7种事务传播行为,我们需要掌握其中的前两种

属性值 含义
REQUIRED 【默认值】需要事务,有则加入,无则创建新事务
REQUIRES_NEW 需要新事务,无论有无,总是创建新事务
SUPPORTS 支持事务,有则加入,无则在无事务状态中运行
NOT_SUPPORTED 不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务
MANDATORY 必须有事务,否则抛异常
NEVER 必须没事务,否则抛异常
NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则开启一个新的事务。 内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。 而内层事务操作失败并不会引起外层事务的回滚。

事务,有则加入,无则在无事务状态中运行 |

| NOT_SUPPORTED | 不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务 |

| MANDATORY | 必须有事务,否则抛异常 |

| NEVER | 必须没事务,否则抛异常 |

| NESTED | 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则开启一个新的事务。

内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。

而内层事务操作失败并不会引起外层事务的回滚。 |

相关推荐
前端Hardy4 小时前
HTML&CSS:MacBook Air 3D 动画跃然屏上
前端·javascript·css·3d·html
loey_ln5 小时前
观察者模式和发布订阅模式
javascript·观察者模式·react.js
ZL_5677 小时前
uniapp中使用uni-forms实现表单管理,验证表单
前端·javascript·uni-app
sunly_7 小时前
Flutter:启动屏逻辑处理02:启动页
android·javascript·flutter
EasyNTS8 小时前
H5流媒体播放器EasyPlayer.js网页直播/点播播放器如果H.265视频在播放器上播放不流畅,可以考虑的解决方案
javascript·音视频·h.265
莘薪8 小时前
JQuery -- 第九课
前端·javascript·jquery
在路上`8 小时前
vue实现列表滑动下拉加载数据
javascript·vue.js·ecmascript
蒜蓉大猩猩9 小时前
Vue.js --- Vue3中其他组合式API
前端·javascript·vue.js·前端框架·node.js·html
珹洺9 小时前
从 HTML 到 CSS:开启网页样式之旅(开篇之一)——CSS 初体验与网页样式新征程
前端·javascript·css·前端框架·html
前端Hardy9 小时前
HTML&CSS:翻书加载效果
前端·javascript·css·3d·html·css3