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

赤峰网站建设建站公司企业门户网站运营推广

赤峰网站建设建站公司,企业门户网站运营推广,外国网站手机dns,wordpress代码打包一、什么是 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/402835.html

相关文章:

  • 如何用WordPress建小说站wordpress怎么做301重定向
  • 建站技术服务新建的网站打不开
  • 网站解析后怎么做一般做个网站需要多少钱
  • 办个人网站租空间wordpress邮件发送不出去
  • 云南网站建设网站运营wordpress 总站模板
  • 带注册的网站需要多大空间信阳建设网站
  • 用jsp做电影网站的界面建设网站需要哪些
  • 中国建设银行投诉网站有关建设网站的英语文献
  • 南通网站群建设广州网站推广软件
  • 滨州网站建设公司刷网站建设
  • 网站建设与营销服务wordpress 下载类主题
  • 南京网站官网建设网络公司网站创建
  • 泰和县城乡建设局网站免费网站的app
  • 正规的邯郸网站建设网站推广分析
  • 好的免费网站建站平台wordpress 精简主题
  • 微信网站开发制作公司青云 wordpress
  • 做个人网站用什么程序网站设计技术
  • 如何做网站的悬浮窗口网站建设前台后台
  • 重庆做网站 熊掌号苏州网站设计公司排名
  • 宁波网站推广优化公司电话宁国网站开发
  • 可信网站认证不在有用吗个人电子邮箱怎么填写格式
  • 怎么做招聘网站的调研wordpress首页生成静态页面
  • 网站建设评分标准公司做的网站怎么维护
  • 一个人做运营网站食品包装设计分析
  • 网站运营问题宜春做网站的联系电话
  • 石家庄外贸网站推广wordpress企业主题下载
  • 邢台做网站邮箱用户登录入口
  • 网站关键词布局图大同本地做网站的
  • 音乐播放网站开发pc端外贸累网站
  • sns网站开发没有备案号的网站