1.前言

  向spring boot转型,所有的配置基本上是用注解完成 ,以前使用spring MVC 需要写一大堆xml文件来配置。

基本上没什么变化,但是有些地方需要注意:

  环绕通知不要使用异常捕获,否则出现异常后,异常通知不会执行,而返回通知仍然会执行,
同时返回结果为null,
可以使用 throws Throwable 配合pjp.proceed(); 即可,这样就不会出现红色下划线!

  如果在同一个方法引入多个切面,那么需要定义使用顺序,先开始执行的切面将最后结束,呈包含关系,
在切面类使用注解@Order(整数) 来定义顺序 ,数字越小,执行的越早。

2.AOP术语解释

  • 通知、增强处理(Advice) 就是你想要的功能,也就是上说的安全、事物、日子等。

      你给先定义好,然后再想用的地方用一下。包含Aspect的一段处理代码

  • 连接点(JoinPoint) 这个就更好解释了,就是spring允许你是通知(Advice)的地方,

      那可就真多了,基本每个方法的钱、后(两者都有也行),或抛出异常是时都可以是连接点,

      spring只支持方法连接点。其他如AspectJ还可以让你在构造器或属性注入时都行,不过那

      不是咱们关注的,只要记住,和方法有关的前前后后都是连接点。

  • 切入点(Pointcut) 上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,

      那就有十几个连接点了对吧,但是你并不想在所有方法附件都使用通知(使用叫织入,下面再说),

      你只是想让其中几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定

      义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。

  • 切面(Aspect) 切面是通知和切入点的结合。现在发现了吧,没连接点什么事,链接点就是为了

      让你好理解切点搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过

      方法名中的befor,after,around等就能知道),二切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。

  • 引入(introduction) 允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗
  • 目标(target) 引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,

      他可以在毫不知情的情况下,被咋们织入切面。二自己专注于业务本身的逻辑。

  • 代理(proxy) 怎么实现整套AOP机制的,都是通过代理,这个一会儿给细说。
  • 织入(weaving) 把切面应用到目标对象来创建新的代理对象的过程,有三种方式,但spring采用的是运行时
  • 目标对象 – 项目原始的Java组件。
  • AOP代理 – 由AOP框架生成java对象。
  • AOP代理方法 = advice + 目标对象的方法。

3.操作

(1)目录结构

 

(2)随意做一个业务层

package com.example.javabaisc.service;

public interface FoodService {
public String food();
}

接口

package com.example.javabaisc.service.serviceImpl;

import com.example.javabaisc.service.FoodService;
import org.springframework.stereotype.Service; @Service
public class FoodServiceImpl implements FoodService {
@Override
public String food() {
System.out.println("=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy");
System.out.println("吃什么");
System.out.println("=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy");
return "apple and tea";
}
}

实现类

(3)做一个controller层接口

package com.example.javabaisc.controller;

import com.example.javabaisc.service.FoodService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
public class VVController { @Autowired
private FoodService foodService; @RequestMapping("/")
public String gg(int id) { if (id == 1) {
// 强制抛出异常
throw new RuntimeException();
} else {
return foodService.food();
} } }

这已经完整了一个功能业务了,

AOP面向切面编程 不会影响业务程序的执行编码,但是可以影响输入参数与返回结果,因此controller和service该干嘛就干嘛,不会因为

有了AOP就需要其他特别的改动,当然,一些辅助操作可以放在切面 完成,如日志、发短信、发邮件等。【如果是分布式可以使用消息中间件啦】

(4)现在需要配置 aspect 【面向切面编程】类

package com.example.javabaisc.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; //aop切面注解,作用是把当前类标识为一个切面,供容器读取
@Aspect
@Component
//定义切面执行顺序,如果在同一个方法有多个切面,那么需要定义使用顺序,先开始直送的切面将最后结束,呈包含关系
@Order(1)
public class myAspect { //定义一个公用方法
// 切入点,表示切入的点,即程序中通用的逻辑业务,这里是请求的路径
//
//参数意思是 公共的 任意返回类型 该类里的所有方法 任意输入参数
//参数public可要可不要
@Pointcut("execution(public * com.example.javabaisc.controller.VVController.*(..))")
public void log() {
} //前置通知 ,表示当前方法是在具体的请求方法之前执行 【权限控制(权限不足抛出异常)、记录方法调用信息日志 】
@Before("log()")
public void doBefore(JoinPoint joinPoint) {
System.out.println("====================");
System.out.println("前置通知---before");
System.out.println("====================");
//如果是controller层 可使用这两句获取 参数 ,当然如果是restful 参数得自己解析路径
// ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// HttpServletRequest request = attributes.getRequest();
//
//获取请求的url
// String url = request.getRequestURI();
//
//获取请求的方法
// String method =request.getMethod();
//
//获取请求的ip
// String ip = request.getRemoteAddr();
//
//获取请求参数,返回来的是个object 数组
// Object[] args = joinPoint.getArgs();
//也可以转成 list
// List<Object> args = Arrays.asList(joinPoint.getArgs());
//
//获取该切入点所在方法的路径,getDeclaringTypeName()获取类名,joinPoint.getSignature().getName()获取方法名
// String ss = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
// System.out.println(ss);
//打印com.example.javabaisc.controller.VVController.gg
//
//可以获取目标class
// Class clazz = joinPoint.getSignature().getDeclaringType(); } //异常通知 :在目标方法出现异常时才会进行执行的代码。【处理异常(一般不可预知),记录日志 】
//方法体Exception参数:用来接收连接点抛出的异常。Exception类匹配所有的异常,可以指定为特定的异常 例如NullPointerException类等
@AfterThrowing(pointcut = "log()", throwing = "ex")
public void throwss(JoinPoint joinPoint, Exception ex) {
System.out.println("====================");
System.out.println("异常通知.....afterthrowing");
System.out.println("====================");
} //返回通知: 在目标方法正常结束时,才执行的通知 【 如银行在存取款结束后的发送短信消息 】
@AfterReturning(returning = "obj", pointcut = "log()")
public void doAfterReturnig(JoinPoint joinPoint, Object obj) {
//处理完请求,返回内容
System.out.println("====================");
System.out.println("返回通知,reponse参数:");
System.out.println(obj);
System.out.println("返回通知---afterreturening");
System.out.println("====================");
} //后置通知,不管是抛出异常或者正常退出都会执行 ,都会执行(类似于finally代码功能) 【释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 】
@After("log()")
public void doAfter(JoinPoint joinPoint) {
System.out.println("====================");
System.out.println("后置通知---adfer");
System.out.println("====================");
} //环绕通知,环绕增强 【日志、缓存、权限、性能监控、事务管理 】
@Around("log()")
public Object arround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("====================");
System.out.println("环绕通知--开始--在前置通知之前");
Object result = null;
//获取参数
// String methodName = pjp.getSignature().getName();
// List<Object> args = Arrays.asList(pjp.getArgs());
// try {
//获取结果
result = pjp.proceed();
System.out.println("环绕通知around,结果是 :" + result);
System.out.println("环绕通知------在方法结束之后");
System.out.println("===================="); // } catch (Throwable e) {
// System.out.println("环绕通知---在环绕通知的方法里的 异常捕获 导致服务降级,不再触发异常通知方法,也就是说如果需要使用环绕通知,不允许在这里使用 catch ,可使用throws Throwable即可");
// e.printStackTrace();
// }
System.out.println("环绕通知---在后置通知之前 ");
return result;
}
}

关于切入点公共方法 注解里面的参数 说明:

public可要可不要

==============================================================================================

==============================================================================================

  想象不到吧 ,spring boot搭建 aspect 竟然一个配置类就足够了,

回想一下 spring MVC ,不仅需要配置xml,还要配置相应的通知方法 ,

简直就是暗无天日的样子。

==============================================================================================

==============================================================================================

3.测试

(1)启动spring boot ,测试正常执行业务,查看控制台打印结果

网址访问 http://localhost:8080/?id=22

(2)测试抛出异常,,查看控制台打印结果

网址访问 http://localhost:8080/?id=1

(3)注意,千万不要在环绕通知使用异常捕获 catch ,否则会导致服务降级,异常通知不会执行,

而返回通知仍会执行,返回结果会是null。

否则会出现下图的打印顺序

spring boot 使用 AOP 的正确姿势 --- 心得的更多相关文章

  1. Spring Boot使用AOP的正确姿势

    一.为什么需要面向切面编程? 面向对象编程(OOP)的好处是显而易见的,缺点也同样明显.当需要为多个不具有继承关系的对象添加一个公共的方法的时候,例如日志记录.性能监控等,如果采用面向对象编程的方法, ...

  2. spring boot 中AOP的使用

    一.AOP统一处理请求日志 也谈AOP 1.AOP是一种编程范式 2.与语言无关,是一种程序设计思想 面向切面(AOP)Aspect Oriented Programming 面向对象(OOP)Obj ...

  3. Spring Boot学习——AOP编程的简单实现

    首先应该明白一点,AOP是一种编程范式,是一种程序设计思想,与具体的计算机编程语言无关,所以不止是Java,像.Net等其他编程语言也有AOP的实现方式.AOP的思想理念就是将通用逻辑从业务逻辑中分离 ...

  4. spring boot使用AOP切面编程

    spring boot使用AOP 1.在pom文件中添加依赖: <!--spring boot aop切面--> <dependency> <groupId>org ...

  5. Spring Boot 使用 Aop 实现日志全局拦截

    前面的章节我们学习到 Spring Boot Log 日志使用教程 和 Spring Boot 异常处理与全局异常处理,本章我们结合 Aop 面向切面编程来实现全局拦截异常并记录日志. 在 Sprin ...

  6. spring boot+自定义 AOP 实现全局校验

    最近公司重构项目,重构为最热的微服务框架 spring boot, 重构的时候遇到几个可以统一处理的问题,也是项目中经常遇到,列如:统一校验参数,统一捕获异常... 仅凭代码 去控制参数的校验,有时候 ...

  7. Spring Boot 使用 AOP 实现页面自适应

    鉴于复杂页面自适应的难度,一般会做几套模板分别适应手机.平板.电脑等设备.使用 Spring Boot 开发单体应用时,一般会使用 Thymeleaf 模板,那么可以使用 AOP 技术来实现页面自适应 ...

  8. Spring Boot使用AOP在控制台打印请求、响应信息

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等. AOP简介 AOP全称Aspect Oriented Programming,面向切面,AOP主要实现的 ...

  9. Spring Boot系列——AOP配自定义注解的最佳实践

    AOP(Aspect Oriented Programming),即面向切面编程,是Spring框架的大杀器之一. 首先,我声明下,我不是来系统介绍什么是AOP,更不是照本宣科讲解什么是连接点.切面. ...

随机推荐

  1. 二进制转换为ip地址

    #include <stdio.h> #include<math.h> int power(int b)//定义幂函数 { int i = 2, j = 1; if (b == ...

  2. 【C/C++】链表

    #include <bits/stdc++.h> using namespace std; struct node { int data; // 数据 node* next; // 指针 ...

  3. 【划重点】Python matplotlib绘图设置坐标轴的刻度

    一.语法简介 plt.xticks(ticks,labels,rotation=30,fontsize=10,color='red',fontweight='bold',backgroundcolor ...

  4. sctf_2019_easy_heap(off-by-null在2.27的利用)

    题目的例行检查我就不放了 将程序放入ida中 漏洞也较为明显 可以看到 if这里多一个null ,明显的off by null 漏洞 程序在最开始的地方给了我们一个很大的空间,并且权限是rwx,所以我 ...

  5. [BUUCTF]REVERSE——[WUSTCTF2020]level3

    [WUSTCTF2020]level3 附件 步骤: 例行检查,64位程序,无壳 64位ida载入,找到关键函数 看样子是个base64加密,但又感觉没那么简单,再翻翻左边的函数,找到了base64加 ...

  6. [BUUCTF]REVERSE——[SUCTF2019]SignIn

    [SUCTF2019]SignIn 附件 步骤: 无壳,64位ida载入 程序调用了 __gmpz_init_set_str 函数,这是一个 GNU 高精度算法库,在RSA加密中见过几次,加上6553 ...

  7. M函数目录(Power Query 之 M 语言)

    2021-12-11更新 主页(选项卡) 管理列(组) 选择列 选择列Table.SelectColumns 删除列 删除列Table.RemoveColumns 删除其他列Table.SelectC ...

  8. 四、Uniapp+vue+腾讯IM+腾讯音视频开发仿微信的IM聊天APP,支持各类消息收发,音视频通话,附vue实现源码(已开源)-会话好友列表的实现

    会话好友列表的实现 1.项目引言 2.腾讯云后台配置TXIM 3.配置项目并实现IM登录 4.会话好友列表的实现 5.聊天输入框的实现 6.聊天界面容器的实现 7.聊天消息项的实现 8.聊天输入框扩展 ...

  9. 使用 juqery.media.js 实现 pdf 预览

    作用:可以实现在指定的位置预览PDF 缺点: (1)在iPad上只能预览一页PDF.(问题是iPad会将PDF转为img呈现,试了将img宽度设置为100%方法但并不好使) (2)在安卓上不能预览,依 ...

  10. centos7使用docker安装es(elasticsearch)

    1.安装docker依赖(已安装可以不用安装) yum install -y docker 2.搜索镜像 docker search elasticsearch 如果出现以下报错 Cannot con ...