Spring AOP小记
一、概述
在通常的开发过程中,我们调用的顺序通常是controller->service-dao,其中,service中包含着太多的业务逻辑,并且还要不断调用dao来实现自身的业务逻辑,经常会导致业务耗时过久,在aop出现之前,方式一般是在函数中开始写一个startTime,结尾再写一个endTime来查看执行该函数的耗时,过多的使用此类方式会导致代码的耦合性太高,不利于管理,于是,AOP(面向切面)出现了。AOP关注的是横向的,而OOP的是纵向。
Spring自2.0版本开始采用@AspectJ注解非常容易的定义一个切面。@AspectJ注解使用AspectJ切点表达式语法进行切点定义,可以通过切点函数、运算符、通配符等高级功能进行切点定义,拥有强大的连接点描述能力。
1.1 特点
AOP(Aspect Oriented Programming)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的横向多模块统一控制的一种技术。AOP是OOP的补充,是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP可以分为静态织入与动态织入,静态织入即在编译前将需织入内容写入目标模块中,这样成本非常高。动态织入则不需要改变目标模块。Spring框架实现了AOP,使用注解配置完成AOP比使用XML配置要更加方便与直观。
1.2 AOP概述
Aspect:一个模块用来关注多个类的切面。在JAVA EE的应用中,事务是AOP的典型例子。
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Target(目标对象):代理的目标对象
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入.
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类Aspect(切面): 是切入点和通知(引介)的结合
二、Spring中的AOP
Spring实现AOP主要是由IOC容器来负责生成、管理的。其创建的方式有两种:
- 默认使用Java动态代理来创建AOP代理;
- 当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB。高版本的Spring会自动选择是使用动态代理还是CGLIB生成代理内容,当然我们也可以强制使用CGLIB生成代理,那就是aop:config里面有一个"proxy-target-class"属性,这个属性值如果被设置为true,那么基于类的代理将起作用。
2.1 AspectJ支持5种类型的通知注解:
[1] Before:前置通知,在方法执行之前执行
[2] After:后置通知,在方法执行之后执行
[3] AfterRunning:返回通知,在方法返回结果之后执行
[4] AfterThrowing:异常通知,在方法抛出异常之后执行
[5] Around:环绕通知,围绕着方法执行
其中,环绕通知是最常见的一种通知注解,特别是在缓存的使用中,例如:Spring-Cache中的使用,在service的方法中添加一个cache的注解,通过AOP来拦截,如果缓存中已经存在,则直接返回结果,如果没有,再进行service的访问。
2.2 Spring提供了4种实现AOP的方式:
- 经典的基于代理的AOP
- @AspectJ注解驱动的切面
- 纯POJO切面
- 注入式AspectJ切面
三、原理概述
Spring AOP的实现原理是基于动态织入的动态代理技术,而AspectJ则是静态织入,而动态代理技术又分为Java JDK动态代理和CGLIB动态代理,前者是基于反射技术的实现,后者是基于继承的机制实现。Spring AOP 在使用时机上也进行自动化调整,当有接口时会自动选择JDK动态代理技术,如果没有则选择CGLIB技术,当然Spring AOP的底层实现并没有这么简单,为更简便生成代理对象,Spring AOP 内部实现了一个专注于生成代理对象的工厂类,这样就避免了大量的手动编码,这点也是十分人性化的,但最核心的还是动态代理技术。从性能上来说,Spring AOP 虽然无需特殊编译器协助,但性能上并不优于AspectJ的静态织入,这点了解一下即可。
具体的原理请看Spring AOP
四、使用
网上看别人写了很多入门的例子,自己就不再阐述了,毕竟自己还是菜,下面是关于AOP入门的资料:
我们为什么要使用AOP?
Spring中AOP的实现
关于AOP
下面是自己在个人网站中的使用,主要是用来统计一个方法的执行消耗了多少时间,需要引入aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar的包。
4.1 在Spring MVC中开启AOP
<!--自动扫描自定义切面-->
<aop:aspectj-autoproxy/>
4.2 定义一个切面
/**
* 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
*/
@Order(2)
@Aspect
@Component
public class TimeInterceptor {
}
4.3 声明一个切入点
@Pointcut("execution(* com.myblog.service.impl.BlogServiceImpl.*(..))")
public void pointcut() {
}
4.4 声明一个前置切点
@Before("pointcut()")
public void before(JoinPoint jp) {
logger.info(jp.getSignature().getName());
logger.info("----------前置通知----------");
}
4.5 声明一个后置切点
@After("pointcut()")
public void after(JoinPoint jp) {
logger.info("----------最终通知----------");
}
4.6 环绕通知
这里,特别要注意的是要抛出Throwable异常,否则方法执行报错的时候无法处理也无法查看
@Around("execution(* (com.myblog.service.impl.*+&&!com.myblog.service.impl.AsyncServiceImpl).*(..))")
public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object obj = null;
Object[] args = joinPoint.getArgs();
long startTime = System.currentTimeMillis();
obj = joinPoint.proceed(args);
// 获取执行的方法名
long endTime = System.currentTimeMillis();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
// 打印耗时的信息
this.printExecTime(methodName, startTime, endTime);
return obj;
}
4.7 返回结果通知
@AfterReturning(pointcut = "execution(* com.myblog.service.impl.BlogServiceImpl.*(..))", returning = "result")
public void afterReturning(JoinPoint jp, Object result) {
logger.info(jp.getSignature().getName());
logger.info("结果是:" + result);
logger.info("----------返回结果----------");
}
4.8 异常后通知
@AfterThrowing(pointcut = "execution(* com.myblog.service.impl.BlogServiceImpl.*(..))", throwing = "exp")
public void afterThrowing(JoinPoint jp, Exception exp) {
logger.info(jp.getSignature().getName());
logger.info("异常消息:" + exp.getMessage());
logger.info("----------异常通知----------");
}
4.9 结果
2018-02-04 17:22:46.287 [http-nio-9090-exec-3] INFO com.myblog.aspect.TimeInterceptor - getAllBlog
2018-02-04 17:22:46.288 [http-nio-9090-exec-3] INFO com.myblog.aspect.TimeInterceptor - ----------前置通知----------
2018-02-04 17:22:46.288 [http-nio-9090-exec-3] DEBUG com.myblog.dao.BlogMapper - Cache Hit Ratio [com.myblog.dao.BlogMapper]: 0.6
2018-02-04 17:22:46.288 [http-nio-9090-exec-3] DEBUG com.myblog.dao.BlogMapper - Cache Hit Ratio [com.myblog.dao.BlogMapper]: 0.6666666666666666
2018-02-04 17:22:46.289 [http-nio-9090-exec-3] INFO com.myblog.cache.EhRedisCache - ===========================Cache L1 (ehcache)
2018-02-04 17:22:46.292 [http-nio-9090-exec-3] INFO com.myblog.aspect.TimeInterceptor - com.myblog.service.IBlogService.getAllBlog method take time: **5 ms**
2018-02-04 17:22:46.292 [http-nio-9090-exec-3] INFO com.myblog.aspect.TimeInterceptor - ----------最终通知----------
2018-02-04 17:22:46.292 [http-nio-9090-exec-3] INFO com.myblog.aspect.TimeInterceptor - getAllBlog
2018-02-04 17:22:46.292 [http-nio-9090-exec-3] INFO com.myblog.aspect.TimeInterceptor - 结果是:Page{count=true, pageNum=1, pageSize=15, startRow=0, endRow=15, total=462, pages=31, countSignal=false, orderBy='null', orderByOnly=false, reasonable=true, pageSizeZero=true}
2018-02-04 17:22:46.292 [http-nio-9090-exec-3] INFO com.myblog.aspect.TimeInterceptor - ----------返回结果----------
2018-02-04 17:22:46.292 [http-nio-9090-exec-3] INFO com.myblog.cache.EhRedisCache - ===========================Cache L1 (ehcache) :{myCache}{com.myblog.service.impl.BlogServiceImpl.getBanner}={[ key = com.myblog.service.impl.BlogServiceImpl.getBanner, value=[com.myblog.model.Blog@2a5de6bc, com.myblog.model.Blog@544159b3, com.myblog.model.Blog@1de1421c, com.myblog.model.Blog@6dbb79bb, com.myblog.model.Blog@28160ab6], version=1, hitCount=2, CreationTime = 1517736161430, LastAccessTime = 1517736166292 ]}
由结果可以看到,整个方法的执行耗时5ms,算是客观吧,如果太大则要对其进行优化。
主要的源码在这:
也可以下载我的博客源码参考参考:
newblog
参考
Spring AOP小记的更多相关文章
- TinyFrame再续篇:整合Spring AOP实现日志拦截
上一篇中主要讲解了如何使用Spring IOC实现依赖注入的.但是操作的时候,有个很明显的问题没有解决,就是日志记录问题.如果手动添加,上百个上千个操作,每个操作都要写一遍WriteLog方法,工作量 ...
- 学习AOP之深入一点Spring Aop
上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...
- 学习AOP之认识一下Spring AOP
心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...
- spring aop
什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将 ...
- spring aop注解方式与xml方式配置
注解方式 applicationContext.xml 加入下面配置 <!--Spring Aop 启用自动代理注解 --> <aop:aspectj-autoproxy proxy ...
- 基于Spring AOP的JDK动态代理和CGLIB代理
一.AOP的概念 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...
- Spring AOP详解
一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...
- Spring AOP实例——异常处理和记录程序执行时间
实例简介: 这个实例主要用于在一个系统的所有方法执行过程中出线异常时,把异常信息都记录下来,另外记录每个方法的执行时间. 用两个业务逻辑来说明上述功能,这两个业务逻辑首先使用Spring AOP的自动 ...
- 从零开始学 Java - Spring AOP 实现用户权限验证
每个项目都会有权限管理系统 无论你是一个简单的企业站,还是一个复杂到爆的平台级项目,都会涉及到用户登录.权限管理这些必不可少的业务逻辑.有人说,企业站需要什么权限管理阿?那行吧,你那可能叫静态页面,就 ...
随机推荐
- 【ASP.NET Core分布式项目实战】(二)oauth2 + oidc 实现 server部分
本博客根据http://video.jessetalk.cn/my/course/5视频整理(内容可能会有部分,推荐看源视频学习) 资料 我们基于之前的MvcCookieAuthSample来做开发 ...
- 从交叉熵损失到Facal Loss
1交叉熵损失函数的由来1.1关于熵,交叉熵,相对熵(KL散度) 熵:香农信息量的期望.变量的不确定性越大,熵也就越大,把它搞清楚所需要的信息量也就越大.其计算公式如下: 其是一个期望的计算,也是记录随 ...
- 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](三)
前言 上一篇<一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](二)>我们通过如下操作: 创建实体及工具类 创建Re ...
- VueI18n插件的简单应用于国际化
作为一个前端小白,刚刚接触学习Vue.js框架结合Element-ui组件开发项目.由于最近需要实现国际化功能,在看element-ui的开发文档时,只有简单的引入没有应用实例,对于我这种小白不能ge ...
- Machine Learning - week 3
Classification 使用线性回归来分类,会很不准确.并且,它的范围也会超出 {0, 1}.所以使用下面的逻辑回归模型. Hypothesis representation 线性回归中 hθ( ...
- Oracle实战笔记(第二天)
导读 今日主要内容:表管理.表操作(增删改查).表查询(简单查询&复杂查询).创建数据库. 一.表管理 1.表命名规范 必须以字母开头: 长度不能超过30个字符: 不能使用Oracle保留字: ...
- [bzoj1999]树网的核
从下午坑到网上..noip的数据太弱,若干的地方写挂结果还随便过= = 最坑的就是网上有些题解没考虑周全... 第一步是找直径,用两次bfs(或者dfs,Linux下系统栈挺大的..)解决.找出其中一 ...
- BZOJ2001: [Hnoi2010]City 城市建设
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2001 cdq分治+重建图. 可以保留当前一定会被选的非修改边然后把点缩起来.这样的话每次点数至 ...
- MongoDB基本命令操作
在上一篇随笔中记录了如何在Centos7上安装MongoDB数据库,这一篇我们就一起来学学基本的操作命令. 安装完成后,shell交互式下输入mongo就可以直接无密码登录到数据库. show dbs ...
- Git服务搭建及github使用教程
.pos { position: fixed; top: 35%; left: 90% } .pos a { border: 2px solid white; background: #99CCFF; ...