DAY05_Spring—SpringAopAop案例-缓存控制

目录

  • 1 Spring AOP
    • 1.1 AOP介绍
    • 1.2 关于AOP名词介绍
    • 1.3 通知类型
    • 1.4 切入点表达式
    • 1.5 AOP入门案例
      • 1.5.1 导入jar包
      • 1.5.2 准备代码
      • 1.5.3 配置切面类
      • 1.5.4 编辑配置类
      • 1.5.5 编辑测试代码
    • 1.6 关于表达式写法
      • 1.6.1 关于@annotation切入点相关代码
    • 1.7 关于通知方法测试
      • 1.7.1 抽取切入点表达式
      • 1.7.2 @Before
      • 1.7.3 @AfterReturning
      • 1.7.4 @AfterThrowing
      • 1.7.5 @After
      • 1.7.6 @Around
    • 1.8 关于通知方法的执行顺序
    • 1.9 关于Order注解说明
  • 2 Aop案例-缓存控制
    • 2.1 业务说明
    • 2.2 业务分析
    • 2.3 代码实现

1 Spring AOP

1.1 AOP介绍

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

  • 知识扩展:
    • 面向过程编程 C语言
    • 半面向对象的程序设计 C++
    • 面向对象的编程技术 java
  • 编程方式:
    • 面向接口编程
    • 面向切面编程
  • 总结:
    • AOP(面向切面编程) 主要利用动态代理的模式 降低程序的耦合度,扩展业务功能方法.

1.2 关于AOP名词介绍

  • 连接点:
    • 用户可以被扩展的方法 joinPoint
  • 切入点:
    • 用户实际扩展的方法(判断方法能否进入切面) pointcut 切入点表达式
  • 通知:
    • 扩展方法的具体实现 @before
  • 切面:
    • 将通知应用到切入点的过程 方法功能得到扩展全部配置(切面=切入点表达式+通知方法)

在这里插入图片描述

1.3 通知类型

  • before:
    • 在目标方法执行之前执行
  • afterReturning:
    • 在目标方法执行之后返回时执行
  • afterThrowing:
    • 在目标方法执行之后,抛出异常时执行
  • after:
    • 无论程序是否执行成功,都要最后执行的通知
  • around:
    • 在目标方法执行前后 都要执行的通知(完美体现了动态代理模式)
    • 功能最为强大 只有环绕通知可以控制目标方法的执行
  • 关于通知方法总结:
    • 环绕通知是处理业务的首选. 可以修改程序的执行轨迹
    • 另外的四大通知一般用来做程序的监控(监控系统) 只做记录

1.4 切入点表达式

当程序满足切入点表达式,才能进入切面,执行通知方法

  1. bean(“bean的ID”) 根据beanId进行拦截 只能匹配一个
  2. within(“包名.类名”) 可以使用通配符*? 能匹配多个.
    粒度: 上述的切入点表达式 粒度是类级别的. 粗粒度.
  3. execution(返回值类型 包名.类名.方法名(参数列表…))
    粒度: 控制的是方法参数级别. 所以粒度较细. 最常用的.
  4. @annotation(包名.注解名) 只拦截注解.
    粒度: 注解是一种标记 根据规则标识某个方法/属性/类 细粒度

1.5 AOP入门案例

1.5.1 导入jar包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.jt</groupId><artifactId>spring_demo_9_aop</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!--Spring核心包--><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.3.22</version></dependency><!--引入SpringBean--><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.3.22</version></dependency><!--引入context包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.22</version></dependency><!--引入表达式jar包--><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.3.22</version></dependency><!--引入日志依赖--><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><!--引入测试包--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--引入AOP包--><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>6.1.2</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.1.2</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.9.6</version></dependency></dependencies>
</project>

1.5.2 准备代码

public interface DeptService {void addDept();void updateDept();
}
import org.springframework.stereotype.Service;@Service
public class DeptServiceImpl implements DeptService{@Overridepublic void addDept() {System.out.println("添加部门的信息");}@Overridepublic void updateDept() {System.out.println("更新部门信息");}
}

1.5.3 配置切面类

  • 切面 = 切入点表达式 + 通知方法
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;//1.AOP需要被Spring容器管理
@Component
//2.标识该类为AOP切面,Spring容器默认不能识别切面注解,需要手动配置
@Aspect
public class SpringAOP {//1.定义before通知@Before("bean(deptServiceImpl)")public void before() {System.out.println("我是before通知");}
}

1.5.4 编辑配置类

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@ComponentScan("com.jt")
@EnableAspectJAutoProxy(proxyTargetClass=false) //启动AOP注解 创建代理对象//默认启用JDK动态代理,//目标对象没有实现接口时,则采用CGLIB//强制使用cglib proxyTargetClass=true//JDK代理创建速度快.运行时稍慢//CGLIB创建时速度较慢,运行时更快
public class SpringConfig {
}

1.5.5 编辑测试代码

在这里插入图片描述

1.6 关于表达式写法

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;//1.AOP需要被Spring容器管理
@Component
//2.标识该类为AOP切面,Spring容器默认不能识别切面注解,需要手动配置
@Aspect
public class SpringAOP {/** 切入点* within:*      within(com.jt.*.DeptServiceImpl)   一级包下的类*      within(com.jt..*.DeptServiceImpl)  ..代表多级包下的类*      within(com.jt..*)                  包下的所有的类* execution(返回值类型 包名.类名.方法名(参数列表))*       execution(* com.jt..*.DeptServiceImpl.add*()) 返回值类型任意的, com.jt下的所有包中的DeptServiceImpl的类的add开头的方法 ,并且没有参数.*       execution(* com.jt..*.*(..)) 返回值类型任意,com.jt包下的所有包的所有类的所有方法 任意参数.*       execution(int com.jt..*.*(int))*       execution(Integer com.jt..*.*(Integer))*       注意: 在Spring表达式中没有自动拆装箱功能! 注意参数类型* @annotation(包名.注解名)*       @Before("@annotation(com.jt.anno.Cache)") 只拦截特定注解的内容.*///1.定义before通知@Before("bean(deptServiceImpl)")//@Before("within(com.jt.service.DeptServiceImpl)")//@Before("within(com.jt..*)")//@Before("execution(* com.jt..*.DeptServiceImpl.add*())")//@Before("@annotation(com.jt.anno.Cache)")public void before() {System.out.println("我是before通知");}
}

1.6.1 关于@annotation切入点相关代码

  • 在测试的时候只有被注解标识的方法才会有通知方法
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,ElementType.TYPE})//注解的作用对象 注解对方法有效 对类有效
public @interface Cache {
}
@Override
@Cache //被注解标识
public void updateDept() {System.out.println("更新部门信息");
}

1.7 关于通知方法测试

1.7.1 抽取切入点表达式

如下代码,两个方法都相对注解进行拦截,这样显得代码很冗余

在这里插入图片描述
所以我们可以抽取切入点表达式,代码如下:

//定义切入点表达式
@Pointcut("@annotation(com.jt.anno.Cache)")
public void pointcut(){
}
@Before("pointcut()")
public void before() {System.out.println("我是before通知");
}
@AfterReturning("pointcut()")
public void afterReturning(){System.out.println("我是afterReturning通知");
}

1.7.2 @Before

  • 前置通知,在目标方法执行之前执行
    • 一般用来记录程序在方法执行前的状态
//1.定义before通知
/*** Spring为了AOP动态获取目标对象及方法中的数据,则通过joinPoint对象进行数据的传递.* getSignature : 方法签名  获取方法的参数
*/
@Before("pointcut()")
public void before(JoinPoint joinPoint) {System.out.println("获取目标对象的类型:" + joinPoint.getTarget().getClass());System.out.println("获取目标对象类名:" + joinPoint.getSignature().getDeclaringTypeName());System.out.println("获取目标对象方法名:" + joinPoint.getSignature().getName());System.out.println("获取方法参数:" + Arrays.toString(joinPoint.getArgs()));System.out.println("我是before通知");
}
  • 运行结果

在这里插入图片描述

1.7.3 @AfterReturning

  • 在目标方法执行之后执行
    • 一般用来监控方法的返回值,进行日志的记录
  1. 在DeptService和DeptServiceImpl中添加测试方法

在这里插入图片描述
在这里插入图片描述

  1. 编辑AOP配置类
//2.定义afterReturning通知
/*** pointcut:关联的切入点表达式* returning:将方法的返回值通过形参result进行传递*      如果参数中需要添加joinPoint对象时,参数必须位于第一位*      Spring在进行参数赋值时,采用index[0]下标的方式赋值
*/
@AfterReturning(pointcut = "pointcut()",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result) {System.out.println("我是afterReturning通知");System.out.println("用户的返回值结果:"+result);
}
  • 运行结果

在这里插入图片描述

  • 如果参数反了则会报以下错误

在这里插入图片描述

1.7.4 @AfterThrowing

  • 当目标方法执行时,抛出异常时可以进行记录.
  1. 在DeptService和DeptServiceImpl中添加测试方法

在这里插入图片描述
在这里插入图片描述

  1. 编辑AOP配置类
//3.定义afterReturning通知/*** throwing = "e" 动态接受程序运行时的报错信息,利用异常通知进行记录*/@AfterThrowing(pointcut = "pointcut()",throwing = "e")public void afterThrowing(Exception e) {System.out.println("我是afterReturning异常通知");System.out.println("获取异常信息"+e.getMessage());System.out.println("获取异常的类型"+e.getClass());}
  • 测试结果

在这里插入图片描述

1.7.5 @After

  • 方法执行之后执行

编辑AOP配置类

//4.定义after通知
@After("pointcut()")
public void after() {System.out.println("我是after通知");
}
  • 测试结果

在这里插入图片描述

1.7.6 @Around

  • 在目标方法执行前后都要执行
    • 实际作用可以控制目标方法是否执行.
  1. 在DeptService和DeptServiceImpl中添加测试方法

在这里插入图片描述
在这里插入图片描述

  1. 编辑AOP配置类
//5.定义afterReturning通知
/*** 关于环绕通知的说明* 作用: 可以控制目标方法是否执行.* 参数: ProceedingJoinPoint 通过proceed方法控制目标方法执行.* 注意事项: ProceedingJoinPoint is only supported for around advice* @return
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) {Object result = null;try {System.out.println("环绕通知开始");//1.执行下一个通知  2.执行目标方法 3.接收返回值Long start = System.currentTimeMillis();result = joinPoint.proceed();Long end = System.currentTimeMillis();System.out.println("耗时:" + (end - start));} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("环绕通知结束");return result;
}
  • 测试结果

在这里插入图片描述

1.8 关于通知方法的执行顺序

  1. 执行around开始
  2. 执行before
  3. 执行目标方法
  4. 执行afterReturning
  5. 执行afterThrowing
  6. 执行after
  7. 执行around通知结束

1.9 关于Order注解说明

  • 如果是多个切面,可以用于控制切面的执行顺序,该注解只可以添加在类上使用
  1. 在DeptService和DeptServiceImpl中添加测试方法

在这里插入图片描述
在这里插入图片描述

  1. 准备两个切面类
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Component
@Aspect
public class Before1 {@Before("@annotation(com.jt.anno.Cache)")public void before(){System.out.println("我是切面A");}
}
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Component
@Aspect
public class Before2 {@Before("@annotation(com.jt.anno.Cache)")public void before(){System.out.println("我是切面B");}
}
  1. 编辑切面配置类

在这里插入图片描述

  • 测试结果

在这里插入图片描述

2 Aop案例-缓存控制

2.1 业务说明

  • 用户有一个缓存的集合 static Map<k:v> key=id号 value=User对象 根据id将User对象进行了缓存
    • 当用户第二次根据Id查询用户时,如果缓存中有数据,则直接返回

2.2 业务分析

1).首选用AOP方式实现

2).通知方法: 使用环绕通知

3).切入点表达式: execution(…)

2.3 代码实现

  • User类
public class User {private Integer id;private String name;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}
}
  • UserService接口
import com.jt.pojo.User;public interface UserService {//根据user对象查询有效信息void findUser(User user);
}
  • UserServiceImpl类
import com.jt.pojo.User;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService{@Overridepublic void findUser(User user) {System.out.println("从数据库中查询数据"+user);}
}
  • SpringConfig配置类
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@ComponentScan("com.jt")
@EnableAspectJAutoProxy//创建代理对象
public class SpringConfig {
}
  • SpringAOP配置类
import com.jt.pojo.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.Map;@Component
@Aspect
public class SpringAOP {private static Map<Integer, User> map = new HashMap();/*** 需求: 用户第一次查询走目标方法* 用户第二次查询走缓存  不执行目标方法* 判断依据: 如何判断用户是否为第一次查询?* 通过map集合进行判断 有数据 证明不是第一次查询* 执行步骤:* 1.获取用户查询的参数* 2.判断map集合中是否有该数据.* true:  从map集合中get之后返回* false: 执行目标方法,之后将user对象保存到Map中*///切入点表达式: 拦截service包中的所有方法@Around("execution(* com.jt.service..*.*(..))")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {Object result = null;//1.获取目标对象的参数Object[] args = joinPoint.getArgs();//2.强制类型转化为对象User user = (User) args[0];//3.判断map集合中是否有该数据  user的Id是唯一标识int id = user.getId();if (map.containsKey(id)) {//map中有数据System.out.println("AOP缓存执行");//将获取的数据返回return map.get(id);} else {//map中没有数据 执行目标方法result = joinPoint.proceed();//将user对象保存到Map中map.put(id, user);System.out.println("AOP执行目标方法");}return result;}
}
  • 测试类
import com.jt.config.SpringConfig;
import com.jt.pojo.User;
import com.jt.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class TestSpring {@Testpublic void test01(){ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);UserService userService = context.getBean(UserService.class);User user = new User();user.setId(100);user.setName("缓存");//1.第一次查询走数据库userService.findUser(user);//2.第二次查询走缓存userService.findUser(user);}
}
  • 运行结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/417148.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Visual Studio 下载安装教程,附安装包和工具,Visual Studio 2022,Visual Studio所有版本都有

前言 Visual Studio是微软推出的一款C编译器&#xff0c;将“高级语言"翻译为"机器语言&#xff08;低级语言)"的程序&#xff0c;VS是一个非常完整的开发工具集&#xff0c;包括了所有软件生命周期中所需的大部分工具&#xff0c;如UML工具、代码管控工具、集…

Ubuntu 22.04 安装MySql

MySQL是非常常用的关系型数据库,无论是大厂还是小厂,都有它的身影。最大的优点是免费,安装起来也比较简单。 MySQL的架构 画了个简图,描述了下MySQL的架构。 其中的比较有趣的点在于连接池和存储引擎。连接池缓存了数据库和客户端的TCP连接,以减少建立连接的开销。存储引…

裸辞后找工作有多难?分享个人经历+面经+学习路线【内含免费下载初级前端面试题】- 回忆我的2023

&#x1f449; 个人博客主页 &#x1f448; &#x1f4dd; 一个努力学习的程序猿 专栏&#xff1a; HTML和CSS JavaScript jQuery Vue Vue3 React TypeScript uni-app Linux 前端面试分享 前端学习方案分享(VitePress、html2canvasjspdf、vuedraggable、videojs) 前端踩坑日记&…

Three.JS教程1 环境搭建、场景与相机

Three.JS教程1 环境搭建、场景与相机 一、Three.JS简介二、环境搭建1. 开发准备2. 安装 three.js3. 新建文件index.htmlmain.js 4. 关于附加组件5. 启动 三、创建场景1. 场景的概念2. 相机的概念3. 相机的几个相关概念&#xff08;1&#xff09;视点&#xff08;Position&#…

tcp/ip协议2实现的插图,数据结构7 (27 - 章)

(166) 166 二七1 TCP的函数 函tcp_drain,tcp_drop (167) (168)

零基础学Python(3)— 注释、代码缩进和编码规范

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。在使用Python语言进行编程的时候&#xff0c;需要遵循一定的规范标准。本节课就带大家了解下Python语言在注释、缩进和编码方面的规范!~&#x1f308; 目录 &#x1f680;1.注释 &#x1f680;2.代码缩进 &#x1f68…

vue.js安装

1:下载 Node.js 官网&#xff1a;https://nodejs.org/en/download 2:安装 node -v npm -v 3:配置 npm config set prefix "F:\node\node_global" npm config set cache "F:\node\node_cache" 按 win 键并输入“编辑系统环境变量”调出系统属性界面&a…

2024年MacBookPro电脑数据恢复软件EasyRecovery数据恢复

前天新入手了一台MacBook pro&#xff0c;第一次用Mac激动的心情简直难以言喻&#xff0c;可是随后这激动的心情顿时就烟消云散了&#xff0c;因为对Mac操作系统的不熟练&#xff0c;导致我删除了一份很重要的Word文件。MacBook pro如何恢复误删除的文件?就这件事我向朋友求助…

数据库:JDBC、自定义工具类、SQL注入问题

数据库&#xff1a;JDBC、工具类&#xff08;数据库驱动&#xff0c;连接&#xff0c;释放&#xff09;、SQL注入&#xff08;非法操作“欺骗”&#xff09; 文章目录 数据库&#xff1a;JDBC、工具类&#xff08;数据库驱动&#xff0c;连接&#xff0c;释放&#xff09;、SQL…

Qt5项目拆解第一集解决:中文乱码| 全局字体|注册表|QSS/CSS

# 一、乱码解决代码片段 QTextCodec是Qt中用于处理文本编码和字符集转换的类。它提供了一系列静态函数来实现不同编码的文本转换,包括编码转换、字符集检测和转换、以及数据流中的文本编码处理。QTextCodec类使得Qt可以在不同的编码和字符集之间进行无缝转换,从而方便地处理…

创建非模态的静态文本并更改它的位置

我是写在钩子里&#xff0c;动态显示静态文本的哦&#xff0c;效果我放在下面了&#xff0c;不知道怎么做动态图片&#xff0c;你们可以教我一下&#xff0c;哈哈。 //这个就是放在钩子里跟随鼠标动态显示坐标信息&#xff0c;或者提示信息 HWND statichandleNULL; HWND NXha…

10个必会的Linux命令

0. tldr 查看命令用法 tldr&#xff1a;Too Long; Didn’t Read。是一个简化版的man&#xff0c;可以查看命令用法。 tldr不是内置的命令&#xff0c;需要安装。可以用pip安装&#xff1a; pip install tldr 安装后就可以用tldr <命令>查看某个命令的用法&#xff0c;…