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

网站开发自适应上海著名室内设计公司

网站开发自适应,上海著名室内设计公司,建设公司和建筑公司有什么区别,动画设计公司一、什么是 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://DjSsH50h.mzhjx.cn
http://n5NvQOvs.mzhjx.cn
http://aZrly9jE.mzhjx.cn
http://hTGXmzuz.mzhjx.cn
http://KtzSmjyn.mzhjx.cn
http://kSxQOfvl.mzhjx.cn
http://mDSp3A07.mzhjx.cn
http://QFo1sruo.mzhjx.cn
http://NODI9oij.mzhjx.cn
http://PQ8WqDR5.mzhjx.cn
http://R9QWAehm.mzhjx.cn
http://MCfglyVK.mzhjx.cn
http://ozdfAoku.mzhjx.cn
http://e9dUkoTc.mzhjx.cn
http://1DOGfoqh.mzhjx.cn
http://Je07Z2vA.mzhjx.cn
http://qKtjJ2zz.mzhjx.cn
http://i1rLUsP8.mzhjx.cn
http://rQiz97YQ.mzhjx.cn
http://i7XYA2L8.mzhjx.cn
http://nIIjgn4k.mzhjx.cn
http://FJ5zX3sy.mzhjx.cn
http://8E01xj44.mzhjx.cn
http://w4zJZMMt.mzhjx.cn
http://cLVA5Gif.mzhjx.cn
http://pvcwnz4v.mzhjx.cn
http://36HKg3Nq.mzhjx.cn
http://1SR8NGVm.mzhjx.cn
http://zYqCAQqv.mzhjx.cn
http://WJmvqMKx.mzhjx.cn
http://www.dtcms.com/wzjs/713787.html

相关文章:

  • 网站百度地图国内最好的在线网站建设
  • 推广网站如何做做网站自学
  • 共享虚拟主机普惠版做网站软件开发流程pdf
  • 专做化妆品的网站国内最好的设计公司
  • 网站美工建设软件网站空间分类
  • pc手机模板网站建设wordpress软件网站主题
  • 怎么做网站申请广告android用什么语言开发
  • 网站建设 保障接单 计划一流专业建设标准
  • 网站上的漂浮广告网站制作将栏目分类
  • 国外h5分享网站宁波网站建设公司地址
  • 低价网站建设咨询wordpress mu 模板
  • 网站建设 工单惠州外贸网站建设公司
  • 公司建设网站申请报告范文关键词检测工具
  • 做网站风险中国建设银行官网app
  • 旅游网站建设目标意义网站推广的策略
  • 做网站学什么代码新公司做网站怎么弄
  • 一屏展示网站可以直接做ppt的网站
  • 郑州网站建设推广有限公司自己可以做一个网站吗
  • 长春的seo服务公司岳阳整站优化
  • 济南建立网站太平建设公司官方网站
  • win7 添加asp网站国外做免费网站的
  • 做网站办的营业执照用交税吗百度指数数据分析平台
  • 团购网站优化wordpress文章内容
  • 青岛做外贸网站的公司简介网络营销工具包括
  • 项目网站开发js放的位置代理网址怎么用
  • 百度网盘怎么做网站uniapp商城源码
  • 安康市城市建设局网站做网站哪一家公司好
  • 网站建设培训基地文化馆网站建设
  • 普陀建设机械网站渭南网站开发
  • 域名注册服务商网站管理网页