当前位置: 首页 > news >正文

中国建设网站官方网站我的世界做图片的网站

中国建设网站官方网站,我的世界做图片的网站,中企动力大连分公司,怎么做一个电子商务网站一、什么是 AOP AOP,即 Aspect Oriented Programming(面向切面编程),简单来说就是针对一类事情的集中处理。在我们之前讲到拦截器,就是针对用户登录进行了统一处理,统一数据返回、统一异常处理&#xff0c…

一、什么是 AOP

AOP,即 Aspect Oriented Programming(面向切面编程),简单来说就是针对一类事情的集中处理。在我们之前讲到拦截器,就是针对用户登录进行了统一处理,统一数据返回、统一异常处理,就实现了 AOP。

上述的拦截器、统一数据返回、统一异常处理,虽然是 Spring 进行实现的,但并不代表 AOP 就是 Spring 的。也就是说,AOP 是一种思想,任何人都可以实现。常见的实现方式有 Spring AOP、AspectJ,CGLIB等。下面针对 Spring AOP 进行讲解。

二、使用 Spring AOP

下面我们来看一下如何使用 AOP 来进行编写代码。

现有下面场景,需要针对 controller 类中的每一个方法记录其运行时间,这段逻辑的代码虽然只有几行,但如果需要在每一个代码中进行实现,就使得代码变得冗余,这里我们就可以使用 Spring AOP 来进行开发。

在使用 Spring AOP 之前需要引入以下依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

记录方法耗时代码如下:

@Slf4j
@Aspect
@Component
public class TimeRecordAspect {/*** 记录 controller 下的方法运行的时间* @param pjp* @return* @throws Throwable*/@Around("execution(* com.gjm.demo.controller.*.*(..))")public Object timeRecord(ProceedingJoinPoint pjp) throws Throwable {log.info("开始记录时间");long begin = System.currentTimeMillis();Object proceed = pjp.proceed();long end = System.currentTimeMillis();log.info("方法耗时: " + (end - begin) + "ms");log.info("记录时间结束");return proceed;}
}

controller 测试代码如下:

@Slf4j
@RequestMapping("/test")
@RestController
public class TestController {@RequestMapping("/method1")public void method1() {log.info("运行 method1");for (int i = 0; i < 1000000; i++) {}}
}

代码运行结果如下:

在这里我们看到,method1 方法的运行时间成功记录。下面针对 Spring AOP 进行详细讲解。

三、Spring AOP核心概念

核心概念有:切点、切面、通知、连接点。下面是这几个概念在具体的代码中表示哪一部分。

 1、切点

切点的作用就是告诉程序对哪些方法记录时间。

execution(* com.gjm.demo.controller.*.*(..))

上面这一段代码就是切点表达式,表示的意思是对于 controller 下的所有类、所有方法、不论参数个数均生效。切点表达式在下面会进行详细解释。 

2、连接点

连接点就是 AOP 程序对哪些方法生效,这些方法就是连接点。

连接点就是满足切点的各个元素,切点就是所有连接点的集合。

3、通知

通知就是 AOP 具体要执行的逻辑,也就是上面的记录方法耗时。

4、切面

切面 = 切点 + 通知,即当前 AOP 程序针对哪些方法,具体实行什么操作。

四、通知类型

通知类型一共有五种:

@Around : 环绕通知,在连接点前运行前后均会执行;

@Before : 前置通知,在连接前运行之前执行;

@After : 后置通知,在连接点运行之后执行,无论是否有异常都会执行;

@AfterReturning : 返回后通知,在连接点运行结束后执行,有异常就不会执行;

@AfterThrowing : 异常后通知,当连接点运行时发生异常时执行。

AOP 代码如下:

@Slf4j
@Aspect
@Component
public class TimeRecordAspect {/*** 记录 controller 下的方法运行的时间* @param pjp* @return* @throws Throwable*/@Around("execution(* com.gjm.demo.controller.*.*(..))")public Object timeRecord(ProceedingJoinPoint pjp) throws Throwable {log.info("开始记录时间");long begin = System.currentTimeMillis();Object proceed = pjp.proceed();long end = System.currentTimeMillis();log.info("方法耗时: " + (end - begin) + "ms");log.info("记录时间结束");return proceed;}@Before("execution(* com.gjm.demo.controller.*.*(..))")public void doBefore() {log.info("do before");}@After("execution(* com.gjm.demo.controller.*.*(..))")public void doAfter() {log.info("do after");}@AfterReturning("execution(* com.gjm.demo.controller.*.*(..))")public void doAfterReturning() {log.info("do afterReturning");}@AfterThrowing("execution(* com.gjm.demo.controller.*.*(..))")public void doAfterThrowing() {log.info("do afterThrowing");}
}

连接点代码如下:

@Slf4j
@RequestMapping("/test")
@RestController
public class TestController {@RequestMapping("/method1")public void method1() {log.info("运行 method1");for (int i = 0; i < 1000000; i++) {}}@RequestMapping("/method2")public void method2() {log.info("运行 method2");int a = 1 / 0;}
}

method1 运行结果如下:

method2 运行结果如下:

 上面是程序正常运行和运行时发生异常的结果。

从第二个结果可以看出,当连接点发生异常时,@Around 后面的逻辑就不会执行,但 @After 会执行。

通过上面的结果可以看出,我们可以将 @Before,@After,@AfterRunning,@AfterThrowing 中的逻辑移至 @Around 中 :

“do before” 就相当于 “开始记录时间”;

“do after”、“do afterRunning” 就相当于 “记录时间结束”;

Object proceed = pjp.proceed();

这一段代码就相当于执行连接点方法,我们可以加上 try - catch,这样就能捕获住连接点运行时发生的异常,但必须使用 Throwable 进行捕获,于是,“do afterThrowing” 就相当于 try - catch,改动过的代码如下:

@Slf4j
@Aspect
@Component
public class TimeRecordAspect {@Around("execution(* com.gjm.demo.controller.*.*(..))")public Object timeRecord(ProceedingJoinPoint pjp) throws Throwable {log.info("do before");long begin = System.currentTimeMillis();Object proceed = null;try {proceed = pjp.proceed();} catch (Throwable e){log.info("do afterThrowing");return proceed;}long end = System.currentTimeMillis();log.info("方法耗时: " + (end - begin) + "ms");log.info("do after");return proceed;}}

代码运行结果如下:

根据上面的运行结果,可以看出:@Around 可以代替 @Before、@After、@AfterRunning、@AfterThrowing。

 五、@Pointcut

在上面的代码中,当我们使用了多个相同的切点表达式时,就会显得代码过于冗余,这样我们就可以将切点表达式单独定义出来,代码如下:

@Slf4j
@Aspect
@Component
public class TimeRecordAspect {@Pointcut("execution(* com.gjm.demo.controller.*.*(..))")private void pt() {}@Around("pt()")public Object timeRecord(ProceedingJoinPoint pjp) throws Throwable {log.info("do before");long begin = System.currentTimeMillis();Object proceed = null;try {proceed = pjp.proceed();} catch (Throwable e){log.info("do afterThrowing");return proceed;}long end = System.currentTimeMillis();log.info("方法耗时: " + (end - begin) + "ms");log.info("do after");return proceed;}@Before("pt()")public void doBefore() {log.info("do before");}@After("pt()")public void doAfter() {log.info("do after");}@AfterReturning("pt()")public void doAfterReturning() {log.info("do afterReturning");}@AfterThrowing("pt()")public void doAfterThrowing() {log.info("do afterThrowing");}
}

如果在别的类中也想使用这个切点时,就需要将 pt 设为 public,引用方式为:全限定类名 . 方法名(),代码如下:

@Slf4j
@Aspect
@Component
public class AspectDemo {@Pointcut("com.gjm.demo.aspect.TimeRecordAspect.pt()")public void pt(){}@Before("pt()")public void doBefore() {log.info("do before2");}@After("com.gjm.demo.aspect.TimeRecordAspect.pt()")public void doAfter() {log.info("do after2");}
}

可以重新定义一个切点 pt ,也可以在注解中直接使用 com.gjm.demo.aspect.TimeRecordAspect.pt() 。

六、切面优先级

当我们定义了多个切点类时,这些切点类的执行顺序时怎样的呢?

现有如下代码:

@Slf4j
@Aspect
@Component
public class AspectDemo1 {@Before("execution(* com.gjm.demo.controller.*.*(..))")public void doBefore() {log.info("do before1");}@After("execution(* com.gjm.demo.controller.*.*(..))")public void doAfter() {log.info("do after1");}
}

另外还有三个与之相同的类,类名依次为AspectDemo2,AspectDemo3,AspectDemo4,代码运行结果如下:

这里我们可以看到,依次执行了 AspectDemo1,AspectDemo2,AspectDemo3,AspectDemo4。这是 Spring 给我们排的序,那如果我们想要自定义切面类的执行顺序呢?

可以使用 @Order 注解,这是类注解,注解中的数字越小,切面的优先级越高。

现将 AspectDemo1 的 @Order 设为4,AspectDemo2 的 @Order 设为3,AspectDemo3 的 @Order 设为2,AspectDemo4 的 @Order 设为1,代码运行结果如下:

这样就依次执行了AspectDemo4,AspectDemo3, AspectDemo2, AspectDemo1。

代码执行图如下:

七、切点表达式

在上面的代码中,我们使用切点表达式来描述 AOP 作用与哪些连接点,常用的切点表达式有两种,分别为:

execution:根据方法名进行匹配;

@annotation:根据注解匹配。

下面进行一一介绍。

1、execution

execution 的标准语法如下:

execution(<访问修饰符> <返回类型> <包名.类名.⽅法(⽅法参数)> <异常>)

上面的代码中我们使用的切点表达式如下:

execution(* com.gjm.demo.controller.*.*(..))

二者的关系如下图所示:

访问修饰符:public、private、protected、default,可省略;

返回类型:连接点方法的返回类型;

包名:连接点所在的包的全限定名;

类名:连接点所在的类;

方法:即连接点;

异常:连接点所抛出的异常,可省略。

切点表达式支持通配符表达。

* :匹配任意字符,只匹配一个元素

包名使用 * 表示匹配任意包,一个包名使用一个 *;

类名使用 * 表示任意类,一个 * 可以匹配多个类;

返回值使用 * 表示任意类型的返回值;

方法名使用 * 表示任意方法;

参数使用 * 表示一个任意类型的参数,只匹配一个参数。

. . :匹配多个连续的 * 

使用 . .  配置包名,即匹配该包下的多个子包;

使用 . .  配置参数,即匹配多个任意类型的参数。

2、@annotation

execution 适用与匹配有规则的连接点,例如一个类下的多个方法,但如果想要匹配两个类下的多个方法,就需要使用注解来完成。

这个注解不是 Spring 为我们提供的,而是我们自己创造的。

下面是自定义注解的方法:

① 先声明自定义的注解,需表明该注解的作用对象(使用 @Target 注解)、生命周期(使用 @Retention 注解)。

常见的作用对象有以下几个:

ElementType.TYPE :描述类、接口或 enum 声明;

ElementType.METHOD :描述方法;

ElementType.PARAMETER :描述参数

ElementType.TYPE_USE :描述任意类型

在这里我们使用 @Target(ElementType.METHOD)。

常见的声明周期有以下几个:

RetentionPolicy.SOURCE:注解仅存在于源代码中,编译后会被丢弃

RetentionPolicy.CLASS:编译时注解,仅存在于源代码和字节码中,运行时会被丢弃

RetentionPolicy.RUNTIME:运行时注解,存在于源代码、字节码和运行中

在这里我们使用 @Retention(RetentionPolicy.RUNTIME)。

注解声明代码如下:

@Target(ElementType.METHOD) //作用范围
@Retention(RetentionPolicy.RUNTIME) //生命周期
public @interface MyAspect {
}

② 定义该注解的用途,即加上这个注解后需要做什么。

在这里,我们需要记录方法运行的时间,代码如下:

@Slf4j
@Aspect
@Component
public class MyTimeRecordAspect {@Around("@annotation(com.gjm.demo.aspect.MyAspect)")public Object timeRecord(ProceedingJoinPoint pjp) throws Throwable {log.info("do before");long begin = System.currentTimeMillis();Object proceed = null;try {proceed = pjp.proceed();} catch (Throwable e){log.info("do afterThrowing");return proceed;}long end = System.currentTimeMillis();log.info("方法耗时: " + (end - begin) + "ms");log.info("do after");return proceed;}
}

在 @Around 注解中需要使用 @annotation + 自定义注解的全限定类名,也可以使用 @Before 等注解,代码运行结果如下:

 

当然,在 @annotation 后面也可以使用别的注解的全限定类名,这样在使用别的该注解时就可以执行下面的逻辑。

八、Spring AOP 的实现方式(常见面试题)

1、基于 @Aspect 进行实现,即 @Around、@Before、@After、@AfterRunning、@AfterThrowing;

2、基于自定义注解,即 @annotation;

3、基于 Spring API(少见);

4、基于代理模式(更少见)。

http://www.dtcms.com/a/415763.html

相关文章:

  • 张店网站建设定制中山网站建设最好的公司
  • 【开题答辩全过程】以 Springboot恒星科技学院学科竞赛管理系统的设计与实现为例,包含答辩的问题和答案
  • Kafka 安全性认证、加密、授权与落地最佳实践
  • 学校网站定位群晖nas wordpress
  • MySQL索引基础详细介绍
  • IDEA配置tomcat运行JavaWeb工程(附Tomcat8下载地址)
  • 强化学习(4)策略梯度与TD Learning
  • 多语言建设外贸网站中怎么做网站上下载图片的功能
  • 北京的网站建设公司有哪些怎么制作html文档
  • 资源网站平台建设方案帝国cms入门到精通企业门户网站制作建站视频教程
  • C++20中线程类std::jthread的使用
  • 拍卖行 网站建设爱奇艺的网站是用什么做的
  • 智能问答场景下的AI算力平台建设指南——从硬件选型到弹性扩展的全流程实践
  • 绍兴网站建设优化网页设计制作
  • 网站降权了怎么办天元建设集团有限公司2008年招聘
  • (二)routeros命令笔记:无线篇
  • 网站开发 高级认证专业的营销型网站企业文化
  • 富文本编辑器Tinymce的使用、图片可编辑尺寸、自定义plugin(数学公式)、自定义icons
  • [创业之路-611]:半导体行业供应链 - 半导体原材料 - 电子特气
  • DreamBoards 推出 DreamHAT+ 雷达扩展板,为树莓派带来 60GHz 毫米波雷达
  • 做旅游网站都需要的调查在哪个网站可做网络夫妻
  • Coze源码分析-资源库-删除数据库-后端源码-领域服务/数据访问层
  • 游戏网站平台大全游戏网网站团购活动页面怎么做
  • Elasticsearch 从入门到实战
  • 网站如何调用手机淘宝做淘宝客佛山品牌策划设计
  • 氛围编程有哪些成功案例?
  • 网站优化外包推荐衡水企业做网站多少钱
  • 有哪些网站程序重庆网站建设的意义
  • Android开发-应用广播
  • AI 算力加速指南:Figma AI/Canva AI 全场景优化,从 “闪退卡顿” 到 “流畅创作”(一)