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容器来负责生成、管理的。其创建的方式有两种:

  1. 默认使用Java动态代理来创建AOP代理;
  2. 当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB。高版本的Spring会自动选择是使用动态代理还是CGLIB生成代理内容,当然我们也可以强制使用CGLIB生成代理,那就是里面有一个"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的方式:

  1. 经典的基于代理的AOP
  2. @AspectJ注解驱动的切面
  3. 纯POJO切面
  4. 注入式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=[[email protected], [email protected], [email protected], [email protected], [email protected]], version=1, hitCount=2, CreationTime = 1517736161430, LastAccessTime = 1517736166292 ]}

由结果可以看到,整个方法的执行耗时5ms,算是客观吧,如果太大则要对其进行优化。

主要的源码在这:

TimeInterceptor

也可以下载我的博客源码参考参考:
newblog

参考

  1. Spring学习总结——Spring实现AOP的多种方式
  2. Spring AOP基础入门总结一
  3. Spring AOP官方

原文地址:https://www.cnblogs.com/w1570631036/p/8312975.html

时间: 2025-01-07 17:14:17

Spring AOP小记的相关文章

SPring+Structs2实现的项目中进行Spring AOP时的相关小记

 SPring+Structs2实现的项目中进行Spring AOP时的相关小记 1.一般为了方便开发Structs2的项目中的action都会建立一个BaseAction如果继承了BaseAction中的子类进行AOP时,只能指定AOP中的PointCut为BaseAction 如果对应的BaseAction如果继承于ActionSupport的话,就只能定义AOP中的PointCut为ActionSupport了 因为Spring生成的代理类中,对同名的方法,只有一个,即子类重写父类的方

Spring框架之Spring AOP

一.基于注解管理的AOP 1.Spring配置文件 <!-- 配置自动扫描包,自动扫描Bean组件,切面类 --> <context:component-scan base-package="com.zhoujian.spring.anno,com.zhoujian.spring.test"> <!-- <context:include-filter type="annotation" expression="org.a

Spring AOP中pointcut expression表达式解析 及匹配多个条件

Pointcut 是指那些方法需要被执行"AOP",是由"Pointcut Expression"来描述的. Pointcut可以有下列方式来定义或者通过&& || 和!的方式进行组合. args() @args() execution() this() target() @target() within() @within() @annotation 其中 execution 是用的最多的,其格式为: execution(modifiers-pat

spring aop 原理

http://blog.csdn.net/moreevan/article/details/11977115 Spring AOP 实现原理 2013-09-24 15:23 79554人阅读 评论(11) 收藏 举报  分类: spring(2)  版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)

Spring AOP进行日志记录

在java开发中日志的管理有很多种.我一般会使用过滤器,或者是Spring的拦截器进行日志的处理.如果是用过滤器比较简单,只要对所有的.do提交进行拦截,然后获取action的提交路径就可以获取对每个方法的调用.然后进行日志记录.使用过滤器的好处是可以自己选择性的对某一些方法进行过滤,记录日志.但是实现起来有点麻烦. 另外一种就是使用Spring的AOP了.这种方式实现起来非常简单,只要配置一下配置文件就可以了.可是这种方式会拦截下所有的对action的每个操作.使得效率比较低.不过想做详细日志

spring AOP和通知

1.  spring的通知 1.1.  AOP的概念 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象.事务管理是J2EE应用中一个关于横切关注点的很好的例子.在Spring AOP中,切面可以使用基于模式或者基于注解的方式来实现. 连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候.在Spring AOP中,一个连接点总是表示一个方法的执行. 通知(Advice):在切面的某个特定的连接点上执行的动作.其中包括了"aroun

spring aop 整理

aop常见概念 1.切面 事务.日志.安全性框架.权限等都是切面(就是类,事务有事务类,日志有日志类,权限有权限类) 2.通知 切面中的方法就是通知(类中针对目标方法所要插入的方法,即事务类中执行事务的方法,日志类中执行日志操作的方法) 3.目标类 (你想要侵入修改的方法所在的类,诸如我们想在查询存款时加入一些其他操作,存款管理类就是目标类) 4.切入点 只有符合切入点,才能让通知和目标方法结合在一起 (就是你想要加强的方法,就是查看工资的方法showSalary()) 5.织入: 形成代理对象

JavaEE学习之Spring aop

一.基本概念 AOP——Aspect-Oriented Programming,面向切面编程,它是spring框架的一个重要组成部分.一般的业务逻辑都有先后关系,我们可以理解为纵向关系,而AOP关注的是横向关系,每一个关注点可以理解为一个横切面.例如我们的大部分代码都会涉及到日志记录,很多的数据库操作都会涉及到事务的创建和提交.那么从横向关注这些逻辑,他们都一个个的切面. AOP技术的具体实现,可以通过动态代理技术或者是在程序编译期间进行静态的"织入"方式.AOP经常使用的场景包括:日

Spring AOP详解(转载)

此前对于AOP的使用仅限于声明式事务,除此之外在实际开发中也没有遇到过与之相关的问题.最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP 来解决.一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容.本文是权当本人的自己AOP学习笔记,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况 监控部分重要函数,若抛出指定的异常,需要以短信或邮件方式通知相关人员