spring aop详解
1.前言
spring aop是一个面向切面的编程,在自己第一遍学习的时候,感觉aop没有什么作用,但是真实接触下来,感觉spring aop还是很有用途的,感觉自己之前的想法太年轻了。
2.概念
Spring 提供了两种AOP 的实现:基于注解式配置和基于XML配置,我这里主要就是介绍一下,基于注解式配置。
2.1 AOP 即 Aspect Oriented Program 面向切面编程
首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。
所谓的核心业务,工作中做的最多的就是增删改查,增删改查都叫核心业务。
所谓的周边功能,比如性能统计,日志记录,事务管理等等
周边功能在 Spring 的面向切面编程AOP思想里,即被定义为切面
在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能 "编织" 在一起,这就叫AOP
2.2 AOP 的目的
AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
2.3 AOP 当中的概念:
切面 (Aspect)可以理解成,就是一个特殊的类(包含的都是增强核心业务的代码),切面 = 切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强!
连接点(join point)通俗理解就是整个系统的所有方法都可以称为连接点
切入点(Pointcut) 就是被选中的连接点,可以通过execution来确定选中的连接点有哪些
通知(Advice)在方法执行的什么实际(when:方法前/方法后/方法前后)做什么(what:增强的功能),就是切面这个类中的代码块
织入(Weaving) 把切面加入到对象,并创建出代理对象的过程。(由 Spring 来完成)
2.4 Spring Aop中的通知类型:
前置通知(Before Advice): 在目标方法被调用前调用通知功能;相关的类
org.springframework.aop.MethodBeforeAdvice
后置通知(After Advice): 在目标方法被调用之后调用通知功能;相关的类
org.springframework.aop.AfterReturningAdvice
返回通知(After-returning): 在目标方法成功执行之后调用通知功能;
异常通知(After-throwing): 在目标方法抛出异常之后调用通知功能;相关的类
org.springframework.aop.ThrowsAdvice
环绕通知(Around): 把整个目标方法包裹起来,在被调用前和调用之后分别调用通知功能相关的类
org.aopalliance.intercept.MethodInterceptor
2.5 spring Aop实现的基础
spring aop实现是通过动态代理的方式实现的,动态代理避免了静态代理需要定义冗余的代理类,实现类,动态代理分为两种,第一种就是jdk 动态代理,第二种就是cglib 动态代理,aop 实现同时采用两种代理模式。
两种动态代理的区别:
jdk动态代理模式 :采用反射的方式,只能对实现接口的类生成代理,具有加载速度快,执行效率低的特点。
cglib动态代理模式:采用的asm,通过字节码形式实现,是针对类实现代理,具有加载速度慢,执行效率高的特点。
2.6 基于AspectJ实现基础上需要连接的几个内置注解
execution函数用于匹配方法执行的连接点,语法为:
execution(方法修饰符(可选) 返回类型 方法名(参数) 异常模式(可选))
参数部分允许使用通配符:
* 匹配任意字符,但只能匹配一个元素
.. 匹配任意字符,可以匹配任意多个元素(零到若干个都可以),必须和*联合使用
"execution(public * com.qli.controller.TestController.*(..))"
@Pointcut(value = "@annotation(com.qli.config.RequestLog)")任何方法使用RequestLog注解都会触发该切面,进入切点
3.实现
具体的案例,在spring boot中实现spring aop,包含的内容有通过aop实现自定义注解,听起来就很高大上,其实懂了之后,感觉就那样,不过在新入门的程序员面前还是可以装起来的,还有就是多个切面的时候的执行顺序
3.1导入依赖
<!--引入父依赖当前工程 继承 父类工程 spring-boot-starter-parent pom--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version><relativePath/></parent> <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties> <dependencies> <!-- web相关依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency></dependencies>
3.2创建切面
package com.qli.config; 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; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; @Aspect @Component @Order(2) public class LogAspect {@Pointcut("execution(public * com.qli.controller.TestController.*(..))")public void webLog(){} @Before("webLog()")public void deBefore(JoinPoint joinPoint) throws Throwable {// 接收到请求,记录请求内容ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();// 记录下请求内容System.out.println("URL : " + request.getRequestURL().toString());System.out.println("HTTP_METHOD : " + request.getMethod());System.out.println("IP : " + request.getRemoteAddr());System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(returning = "ret", pointcut = "webLog()")public void doAfterReturning(Object ret) throws Throwable {// 处理完请求,返回内容System.out.println("方法的返回值 : " + ret);} //后置异常通知@AfterThrowing("webLog()")public void throwss(JoinPoint jp){System.out.println("方法异常时执行.....");} //后置最终通知,final增强,不管是抛出异常或者正常退出都会执行@After("webLog()")public void after(JoinPoint jp){System.out.println("方法最后执行.....");} //环绕通知,环绕增强,相当于MethodInterceptor@Around("webLog()")public Object arround(ProceedingJoinPoint pjp) {System.out.println("方法环绕start.....");try {Object o = pjp.proceed();System.out.println("方法环绕proceed,结果是 :" + o);return o;} catch (Throwable e) {e.printStackTrace();return null;}} }
3.3 创建控制类
package com.qli.controller; import com.qli.config.RequestLog; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @RequestMapping("/requestLog")public String requestLog(){return "first controller";} }
执行结果:
方法环绕start..... URL : http://localhost:8088/requestLog HTTP_METHOD : GET IP : 0:0:0:0:0:0:0:1 CLASS_METHOD : com.qli.controller.TestController.requestLog ARGS : [] 方法环绕proceed,结果是 :first controller 方法最后执行..... 方法的返回值 : first controller
上边是创建一个切面通过 @Pointcut("execution(public * com.qli.controller.TestController.*(..))")注解来限制切入点是controller包下TestController控制类下所有的方法都是切入点,那么congtroller包下其他控制类的方法就是连接点。
3.4创建自定义注解
3.4.1自定义注解之前需要了解的概念
@Target注解,是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的。它使用一个枚举类型定义如下:
public enum ElementType {/** 类,接口(包括注解类型)或枚举的声明 */TYPE, /** 属性的声明 */FIELD, /** 方法的声明 */METHOD, /** 方法形式参数声明 */PARAMETER, /** 构造方法的声明 */CONSTRUCTOR, /** 局部变量声明 */LOCAL_VARIABLE, /** 注解类型声明 */ANNOTATION_TYPE, /** 包的声明 */PACKAGE }
@Retention注解,翻译为持久力、保持力。即用来修饰自定义注解的生命力。
public enum RetentionPolicy {/*** Annotations are to be discarded by the compiler.* (注解将被编译器忽略掉)*/SOURCE, /*** Annotations are to be recorded in the class file by the compiler* but need not be retained by the VM at run time. This is the default* behavior.* (注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,这是一个默认的行为)*/CLASS, /*** Annotations are to be recorded in the class file by the compiler and* retained by the VM at run time, so they may be read reflectively.* (注解将被编译器记录在class文件中,而且在运行时会被虚拟机保留,因此它们能通过反射被读取到)* @see java.lang.reflect.AnnotatedElement*/RUNTIME }
@Documented注解,是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。
3.5 实现
3.5.1 创建自定义注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface RequestLog {String desc() default "无信息"; }
3.5.2 创建自定义注解时候后将会触发的切面
package com.qli.config;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component;@Component @Aspect @Order(1) public class RequestAspect {@Pointcut(value = "@annotation(com.qli.config.RequestLog)")public void access() {}@Before("access()")public void deBefore(JoinPoint joinPoint) throws Throwable {System.out.println("second before");}@Around("@annotation(requestLog)")public Object around(ProceedingJoinPoint pjp, RequestLog requestLog) {//获取注解里的值System.out.println("second around:" + requestLog.desc());try {return pjp.proceed();} catch (Throwable throwable) {throwable.printStackTrace();return null;}} }
3.5.3 在控制层添加
@RequestLog(desc = "second")@RequestMapping("/second")public Object second(){return "second controller";}
执行结果
second around:second second before
如果将两个切面的切入点都包含有second这个方法的时候,浏览器访问http://localhost:8088/second的执行结果如下
second around:second second before 方法环绕start..... URL : http://localhost:8088/second HTTP_METHOD : GET IP : 0:0:0:0:0:0:0:1 CLASS_METHOD : com.qli.controller.TestController.second ARGS : [] 方法环绕proceed,结果是 :second controller 方法最后执行..... 方法的返回值 : second controller
执行顺序:
spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfter、doAfterReturn方法。也就是说对多个AOP来说,先before的,一定后after。
spring aop详解相关推荐
- Spring AOP详解(转载)所需要的包
上一篇文章中,<Spring Aop详解(转载)>里的代码都可以运行,只是包比较多,中间缺少了几个相应的包,根据报错,几经百度搜索,终于补全了所有包. 截图如下: 在主测试类里面,有人怀疑 ...
- Spring AOP详解(http://sishuok.com/forum/posts/list/281.html)
三6.5 AspectJ切入点语法详解 6.5.1 Spring AOP支持的AspectJ切入点指示符 切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接 ...
- 细说Spring——AOP详解(AOP概览)
一.对AOP的初印象 首先先给出一段比较专业的术语(来自百度): 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方 式和运行期动态代理实 ...
- Spring AOP详解一文搞懂@Aspect、@Pointcut、@Before、@Around、@After、@AfterReturning、@AfterThrowing
文章目录 1.AOP是什么 2.AOP中注解的含义 3.Pointcut切入点的语法 4.AOP代码实现 1.AOP是什么 AOP:Aspect Oriented Programming,翻译过来就是 ...
- Spring 体系结构详解
Spring 体系结构详解 核心容器(Core Container) Core和Beans模块提供了Spring最基础的功能,提供IOC和依赖注入特性.这里的基础概念是BeanFactory,它提供对 ...
- struts2+hibernate+spring配置详解
#struts2+hibernate+spring配置详解 struts2+hibernate+spring配置详解 哎 ,当初一个人做好难,现在终于弄好了,希望自学这个的能少走些弯路. 以下是自己配 ...
- Spring入门详解
typora-copy-images-to: upload Spring入门详解 Spring框架是Java开发中最常用的框架,功能非常强大 源码下载:Spring Framework jar包.文档 ...
- Spring IoC详解
Spring IoC详解 原文地址:Spring IoC详解 写在最前 本文将主要写Spring最核心的部分,为什么写这篇的原因也是因为在刚开始学习Spring的时候,学得太粗糙了.感觉学了个皮毛,从 ...
- Spring源码(八):Spring事务详解
Spring事务详解 一.事务执行流程 二.Spring事务切面 三.事务切面的Pointcut和Advice 四.注解事务的源码分析 五.Sping事务的传播属性 六.Sping事务的异常校验 七. ...
最新文章
- WinCE项目应用之车载导航
- PHP环境配置文件php.ini详解注释 --转贴
- HTTP隧道工具HTTPTunnel
- Bitmap类getPixels()方法中参数stride理解
- 老板和用户你听谁的——手机网站改版踩坑记
- 预编译头文件来自编译器的早期版本_Debug
- 使用 Kafka 和 Spark Streaming 构建实时数据处理系统
- 积分上下限无穷_数学方法的思考和存在无穷多个孪生素数证明的思路
- 篇三:访问JSON静态文件
- 谷歌浏览器外贸版_针对谷歌SEO,你有哪些值得推荐的工具、插件、网站、app,或者技巧分享?...
- java基础—统计一个字符串中各个字符出现的次数
- php递归面包屑,php实现面包屑导航例子分享,_PHP教程
- 腾讯QQ认证空间4月27日已全面开放申请,欲进军自媒体
- 拓端tecdat|R语言GJR-GARCH和GARCH波动率预测普尔指数时间序列和Mincer Zarnowitz回归、DM检验、JB检验
- 2018年阿里巴巴关于java重要开源项目汇总
- 戏说中国互联网5大巨头
- 苹果为M1 MacBook Air/Pro提供自助维修服务 维修成本更低
- (Hadoop、HBase、Kafka)中,Zookeeper都作为核心组件使用
- 有些jpg图在IE浏览器中打不开
- 浅析微信小程序生命周期之应用生命周期
热门文章
- java-net-php-python-jsp员工时间管理系统查重PPT计算机毕业设计程序
- [资料整理]魔法师传奇 MagicMayhem
- PostgreSQL批量插入/批量更新时间一致问题
- 用MARP输出PPTX文件,不能用powerpoint软件修改,这是正常现象吗?如何才可以修改?
- Idea 方法模板注释
- 解决虚拟机VMware黑屏、无法关机
- 实现一个红绿灯效果,每次红灯亮 10 秒,绿灯亮 8 秒,黄灯亮 3 秒,并在红绿灯右侧显示倒计时
- (转)26种搞笑的醉酒行为
- micropython应用触摸屏_尝鲜MicroPython,写一个GT911触摸屏的demo
- python语言与c语言java的区别_c语言和java有什么区别