前言

容器和AOP是Spring的两大核心。本文将来学习Spring AOP。

AOP是什么?

AOP在计算机科学领域还是相对年轻的概念,由Xerox PARC公司发明。Gregor Kiczales 在1997年领导一队研究人员首次介绍了AOP。当时他们关心的问题是如何在大型面向对象的代码库中重复使用那些必要且代价高的样板,那些样板的通用例子具有日志,缓存和事务功能。在最终的研究报告中,Kiczales和他的团队描述了OOP技术不能捕获和解决的问题,他们发现横切关注点最终分散在整个代码中,这种交错的代码会变得越来越难开发和维护。他们分析了所有技术原因,包括为何这种纠缠模式会出现,为什么避免起来这么困难,甚至涉及了设计模式的正确使用。该报告描述了一种解决方案作为OOP的补充,即使用“切面aspects”封装横切关注点以及允许重复使用。最终实现了AspectJ,就是今天Java开发者仍然使用的一流AOP工具。

也就是说,AOP可不是Spring发明的,Spring只是对AOP做了支持而已。既然如此,AOP里面的几个概念就是通用的了。

《Spring in Action》这本书给出了明确的解释:

在软件开发中,散布于应用中多处的功能被称为横切关注点(cross-cutting concern)。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的。比如:日志、声明式事物、安全和缓存。这些东西都不是我们平时写代码的核心功能,但许多地方都要用到。
把这些横切关注点与业务相分离正是面向切面编程(AOP)索要解决的问题。
简单的说就是把这些许多地方都要用到,但又不是核心业务的功能,单独剥离出来封装,通过配置指定要切入到指定的方法中去。

如上图所示,这就是横切关注点的概念,水平的是核心业务,这些切入的箭头就是我们的横切关注点。

横切关注点可以被模块化为特殊的类,这些类被称为切面(aspect)。这样做有两个好处:

首先,现在每个关注点都集中于一个地方,而不是分割到多处代码中
其次,服务模块更简洁,因为它们只包含主要关注点(或核心功能)的代码,而次要关注点的代码被转移到切面中了。

AOP术语

通知(Advice):
在AOP中,切面的工作被称为通知。通知定义了切面“是什么”以及“何时”使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。
Spring切面可以应用5种类型的通知:

  • 前置通知(Before):在目标方法被调用之前调用通知功能
  • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
  • 返回通知(After-returning):在目标方法成功执行之后调用通知
  • 异常通知(After-throwing):在目标方法抛出异常后调用通知
  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为

连接点(Join point):
连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加行为。

切点(Pointcut):
如果说通知定义了切面“是什么”和“何时”的话,那么切点就定义了“何处”。比如我想把日志引入到某个具体的方法中,这个方法就是所谓的切点。

切面(Aspect):
切面是通知和切点的结合。通知和切点共同定义了切面的全部内容———他是什么,在何时和何处完成其功能。

引入(Introduction):
引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)。

织入(Weaving):
把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:

  • 编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器。
  • 类加载时:使用特殊的 ClassLoader 在目标类被加载到程序之前增强类的字节代码。
  • 运行时:切面在运行的某个时刻被织入, 方式是容器为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面的。

Spring对AOP的支持

同依赖注入一样,Spring AOP也提供两种配置:

  • 基于Java Annotation;
  • 基于Xml配置;

Spring AOP是基于动态代理实现的,代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。至于什么是动态代理,后文将会解释。
动态代理只能处理方法,因此Spring AOP只支持方法连接点。但是通常方法拦截可以满足大部分需求。如果需要字段或构造器拦截,别忘了Spring是一个高度可扩展的框架,可以使用AspectJ 等第三方AOP框架。

下面就举两个例子分别来介绍下Spring AOP的两种配置方式,我们就拿简单的日志来说明。

使用Java注解配置

@Component //声明bean
@Aspect //该注解标示该类为切面类
public class LogAspect {@Pointcut("execution(* com.springdemo.service.impl.UserServiceImpl.*(..))")public void logAop(){}@Before("logAop() && args(name)")public void logBefore(String name){System.out.println(name+"前置通知Before");}@AfterReturning("logAop()")public void logAfterReturning(){System.out.println("返回通知AfterReturning");}@After("logAop() && args(name)")public void logAfter(String name){System.out.println(name+"后置通知After");}@AfterThrowing("logAop()")public void logAfterThrow(){System.out.println("异常通知AfterThrowing");}
}

该代码片段就声明了一个bean,而这个bean是一个切面,其中的方法将会应用于com.springdemo.service.impl.UserServiceImpl类的任何方法。

UserServiceImpl类很简单,只是个普通的bean:

package com.springdemo.service.impl;
@Service("userService")
public class UserServiceImpl implements UserService{@Overridepublic void sayHello(String name) {System.out.println("hello, "+name);}
}

最后,还要启用Spring AOP的注解配置。同样采用Java配置:

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class Config{
...
}

注意不要被注解里的“AspectJ”字样吓到了,这个注解确实是AspectJ定义的,但Spring只是重用了这个注解,实现还是Spring AOP自己的基于动态代理的实现。

sayHello被执行后的结果:

aop前置通知Before
hello, aop
aop后置通知After
返回通知AfterReturning

本段代码源自嘟嘟的博客,里面有具体的解释。

基于XML的配置

同理,不再列出详细代码。

  • <aop:aspectj-autoproxy/> 启用AOP注解。
  • 如不采用注解,则用aop名称空间的标记描述切面。如下是一个等效的配置:
<bean id="logAspect" class="com.springdemo.aspect.LogAspect" />
<aop:config><aop:aspect id="log"  ref="logAspect"><aop:pointcut id="logAop" expression="execution(* com.springdemo.service.impl.UserServiceImpl.sayHello(..)) and args(name)"/><aop:before method="logBefore" pointcut-ref="logAop"/><aop:after method="logAfter"  pointcut-ref="logAop"/><aop:after-returning method="logAfterReturning"  pointcut-ref="logAop"/><aop:after-throwing method="logAfterThrow" pointcut-ref="logAop"/><!--<aop:around method="logAfterThrow"  pointcut-ref="logAop"/>--></aop:aspect>
</aop:config>

Spring AOP原理 —— 动态代理

参考这篇知乎问答。
首先,静态代理应该是普遍了解的概念了。它是OOP的一种设计模式,依赖于接口。
动态代理则是为了解决目标类方法越来越多时,代理类也要跟着膨胀的问题。因为动态代理类只要在invoke()方法中有选择地实现接口的方法。这对那些普遍适用的功能来说特别适合,比如缓存、认证、log等等。

public class ProxyHandler implements InvocationHandler
{private Object tar;//绑定委托对象,并返回代理类public Object bind(Object tar){this.tar = tar;//绑定该类实现的所有接口,取得代理类 return Proxy.newProxyInstance(tar.getClass().getClassLoader(),tar.getClass().getInterfaces(),this);}// invoke中的逻辑可以对接口中的所有方法生效。public Object invoke(Object proxy , Method method , Object[] args)throws Throwable{Object result = null;//在调用具体函数方法前,执行功能处理result = method.invoke(tar,args);//在调用具体函数方法后,执行功能处理return result;}
}

至于InvocationHandler,它是JDK的java.lang.reflect名称空间里提供的一个类,用来实现动态代理。具体可见Java Dynamic Proxy API。

可以看出,动态代理的功能十分强大,因此得到了广泛的应用。比如单元测试中的mock框架,MyBatis的sql注解等等。

最后,动态代理仍是需要接口的。而借助另一个第三方类库CGLib,则可以动态生成字节码,不依赖于接口。Spring AOP对这两种技术都有使用。参见CSDN。

Spring 学习笔记(二)Spring AOP相关推荐

  1. JavaEE——Spring学习笔记03【AOP开发】

    JavaEE--Spring学习笔记01[Ioc开发的模式] JavaEE--Spring学习笔记02[Spring和Mybatis的整合] JavaEE--Spring学习笔记03[AOP开发] J ...

  2. Spring学习笔记 之 Spring<全>

    开始学习Spring全家桶 文章目录 1. IoC 定义 为什么叫控制反转? 实现 IoC 容器创建 bean 的两种⽅式 IoC DI 特殊字符的处理 Spring 中的bean创建类型 -- sc ...

  3. Spring 学习之 二----Spring创建对象的三种方式

    最近在系统的学习Spring,现在就Spring的一些知识进行总结. 我们知道Spring是一个开放源代码的设计层面的框架,他主要解决的是业务逻辑层与其他各层之间松耦合的问题. Spring 有三个核 ...

  4. spring学习笔记(spring概述和IOC)

    spring5 1.spring的概述 1.1.spring是什么 Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发的复杂性而创建的. Spring 的 ...

  5. Spring学习笔记之Spring Web Flow

    Spring Web Flow 是Spring MVC 的扩展,它支持开发基于流程的应用程序.它将流程的定义与实现流程行为的类和视图分离开来. 1.配置Web Flow 在Spring MVC上下文定 ...

  6. Spring学习笔记:Spring整合Mybatis(mybatis-spring.jar)(二:mybatis整合spring)

    http://blog.csdn.net/qq598535550/article/details/51703190 二.Spring整合mybatis其实是在mybatis的基础上实现Spring框架 ...

  7. spring学习笔记 -- day02 spring基础、IOC控制反转

    一:srping的介绍 1.spring的体系结构 2.spring资源 a.spring-framework-4.2.4.RELEASE:Spring Framework的官方发行包 b.sprin ...

  8. JavaEE——Spring学习笔记01【Ioc开发的模式】

    JavaEE--Spring学习笔记01[Ioc开发的模式] JavaEE--Spring学习笔记02[Spring和Mybatis的整合] JavaEE--Spring学习笔记03[AOP开发] J ...

  9. Java学习笔记-Day64 Spring 框架(二)

    Java学习笔记-Day64 Spring 框架(二) 一.控制反转IOC和依赖注入DI 1.控制反转IOC 2.依赖注入DI 3.Spring IOC容器 3.1.简介 3.2.实现容器 3.2.获 ...

  10. Spring 学习笔记----->AOP

    Spring 学习笔记----->AOP 代理模式 为什么学代理模式? 因为这就是Spring Aop的底层 代理模式的分类: 静态代理 动态代理 静态代理 生活用的例子: 房东 public ...

最新文章

  1. 利用反射搭建orm框架
  2. 洛谷 [P1198] 最大数
  3. 领域驱动设计(2)怎么使用沟通
  4. 分布式文件系统—HDFS—入门简介
  5. 使用freemarker模板生成word文档
  6. K8s稳居容器榜首,Docker冲顶技术热词,微服务应用热度不减,2021云原生开发者现状
  7. ARM:供给中国 160 亿颗芯片,继续授权华为!
  8. 《并行计算的编程模型》一3.1 引言
  9. Python垃圾回收(gc)拖累了程序执行性能?
  10. 利用纯CSS3实现超立体的3D图片侧翻倾斜效果
  11. 主管好当:一不指点工作,二不检查工作,三不改正错误
  12. 解决THINKCMF后台文章的相册图集只能上传一个图片的问题
  13. PS 在PS中如何等比例放大缩小图片
  14. nmds与mds的区别_数量生态学笔记||非约束排序|NMDS
  15. excel图片根据表格内容动态变化
  16. 15个国外便宜主机介绍
  17. 淼淼Kruskal算法
  18. 云大计算机专业基础综合,2020云南大学计算机专业课改考408
  19. 程序员因违反竟业协议,赔腾讯97.6万...
  20. Ubantu18.04 安装qq

热门文章

  1. Android Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE解决办法
  2. JNI Java本地接口(双向接口)
  3. BetterFE 前端技术周刊 - 2019/04/15
  4. 修改python的pip下载源
  5. SDNU 1048.石子合并2(区间dp)
  6. js node.js 编写命令工具demo
  7. SQL SERVER 中如何用脚本管理作业
  8. Sublime Text 3 快捷键汇总
  9. 笔谈OpenGL ES(一)
  10. [zz]c++ list sort方法