在开始Spring AOP之前,先要对AOP有一个直观的认识。AOP按我的理解:AOP时一种面向特定种类事件的编程。这种思想与前端页面中事件机制是类似的,这是一种不同分支下的相互借鉴。
在前端页面中,事件机制如下:
1、事件类型,如点击事件、双击事件、焦点丢失事件
2、事件处理函数,在事件触发时需要运行的一段面向过程的程序
3、将事件触发点、事件类型、事件处理函数做一次连结(可静态或运行时连接)
4、运行时检查事件是否得到正常处理
AOP的过程如下:
1、切面(Aspect),主要是定义是什么,在何时和何处完成其功能,类比于前端页面的事件类型
2、通知(Advice),主要是处理切面要完成的任务,模式有Before、After、After-returning,After-throwing和Around
3、目标类(Target),程序中触发切面处理的节点,主要是类对象
4、切点(Pointcut),目标类上可以触发通知的具体处理方法或操作
5、织入(Weaving),将目标类、切面、通知做一次编译处理,将它们做有效的绑定结合,可以在编译期、类加载期、运行期做这种处理
这两者不同在于表现的形式,前端是页面事件机制,AOP是服务器端面向对象中的自定义事件机制,但是核心的思想机制是一样的。在编写面向切面编程的时候,要有一种前端事件机制在面向对象程序中实现的思考。
Spring AOP 的底层是通过以下 2 种动态代理机制,为目标对象(Target Bean)执行横向织入的。
1、JDK 动态代理,Spring AOP 默认的动态代理方式,若目标对象实现了若干接口,Spring 使用 JDK 的 java.lang.reflect.Proxy 类进行代理。
2、CGLIB 动态代理,若目标对象没有实现任何接口,Spring 则使用 CGLIB 库生成目标对象的子类,以实现对目标对象的代理。
Note: final修饰的类和方法是无法被代理的。
Spring AOP 并没有像其他 AOP 框架(例如 AspectJ)一样提供了完成的 AOP 功能,它是 Spring 提供的一种简化版的 AOP 组件。其中最明显的简化就是,Spring AOP 只支持一种连接点类型:方法调用。您可能会认为这是一个严重的限制,但实际上 Spring AOP 这样设计的原因是为了让 Spring 更易于访问。
方法调用连接点是迄今为止最有用的连接点,通过它可以实现日常编程中绝大多数与 AOP 相关的有用的功能。如果需要使用其他类型的连接点(例如成员变量连接点),我们可以将 Spring AOP 与其他的 AOP 实现一起使用,最常见的组合就是 Spring AOP + ApectJ。
Spring 使用 org.springframework.aop.Advisor 接口表示切面的概念,实现对通知(Adivce)和连接点(Joinpoint)的管理。
在 Spring AOP 中,切面可以分为三类:一般切面、切点切面和引介切面。
示例配置信息:
<!--******Advisor : 代表一般切面,Advice 本身就是一个切面,对目标类所有方法进行拦截(* 不带有切点的切面.针对所有方法进行拦截)*******-->
<!-- 定义目标(target)对象 -->
<bean id="userDao" class="net.biancheng.c.dao.impl.UserDaoImpl"></bean>
<!-- 定义增强 -->
<bean id="beforeAdvice" class="net.biancheng.c.advice.UserDaoBeforeAdvice"></bean>
<!--通过配置生成代理 UserDao 的代理对象 -->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 设置目标对象 -->
<property name="target" ref="userDao"/>
<!-- 设置实现的接口 ,value 中写接口的全路径 -->
<property name="proxyInterfaces" value="net.biancheng.c.dao.UserDao"/>
<!-- 需要使用value:增强 Bean 的名称 -->
<property name="interceptorNames" value="beforeAdvice"/>
</bean>
Spring 能够基于 org.springframework.aop.framework.ProxyFactoryBean 类,根据目标对象的类型(是否实现了接口)自动选择使用 JDK 动态代理或 CGLIB 动态代理机制,为目标对象(Target Bean)生成对应的代理对象(Proxy Bean)。
PointCutAdvisor 是 Adivsor 接口的子接口,用来表示带切点的切面。使用它,我们可以通过包名、类名、方法名等信息更加灵活的定义切面中的切入点,提供更具有适用性的切面。
Spring 提供了多个 PointCutAdvisor 的实现,其中常用实现类如如下。
- NameMatchMethodPointcutAdvisor:指定 Advice 所要应用到的目标方法名称,例如 hello* 代表所有以 hello 开头的所有方法。
- RegExpMethodPointcutAdvisor:使用正则表达式来定义切点(PointCut),RegExpMethodPointcutAdvisor 包含一个 pattern 属性,该属性使用正则表达式描述需要拦截的方法
所有目标对象(Target Bean)的代理对象(Proxy Bean)都是在 XML 配置中通过 ProxyFactoryBean 创建的。但在实际开发中,一个项目中往往包含非常多的 Bean, 如果每个 Bean 都通过 ProxyFactoryBean 创建,那么开发和维护成本会十分巨大。为了解决这个问题,Spring 为我们提供了自动代理机制。
Spring 提供的自动代理方案,都是基于后处理 Bean 实现的,即在 Bean 创建的过程中完成增强,并将目标对象替换为自动生成的代理对象。通过 Spring 的自动代理,我们在程序中直接拿到的 Bean 就已经是 Spring 自动生成的代理对象了。
Spring 为我们提供了 3 种自动代理方案:
- BeanNameAutoProxyCreator:根据 Bean 名称创建代理对象。
- DefaultAdvisorAutoProxyCreator:根据 Advisor 本身包含信息创建代理对象。
- AnnotationAwareAspectJAutoProxyCreator:基于 Bean 中的 AspectJ 注解进行自动代理对象。