Spring 容器蕴含两个重要的个性:面向切面编程(AOP)和管制反转(IOC)。面向切面编程是面向对象(OOP)的一种补充,在面向对象编程的过程中编程针对的指标是一个个对象,而面向切面编程中编程针对的指标是一个个切面。切面反对跨类型跨对象(如事务的切面能够加在任何中央)进行模块化。
<!–more–>
前言
AOP 是 Spring 的要害个性之一,尽管 Spring 的 IOC 个性并不依赖于 AOP(意味着你能够只应用 Spring 的 IOC 个性而不应用 AOP 个性),然而二者联合起来能够灵便的实现很多中间件解决方案。比方咱们常常应用的事务 (@Transaction),就是通过 AOP 计划实现的。本文重点介绍 AOP 编程中的一些术语,这些术语不仅仅局限于 Spring,它实用于所有的 AOP 编程。
- 切面 (Aspect):面向切面编程能够跨类跨对象进行切面编程,一个切面就是对一类横切关注点的模块化。
- 切入点 (JoinPoint):程序执行过程中的一个点,如办法调用、字段拜访和异样抛出等。
- 加强(Advice):用于对切面加强,蕴含前加强、后加强和盘绕加强。大多数 AOP 框架会对切入点进行拦挡,并在切入点保护一个拦截器链。
- 指标对象(TargetObject):蕴含一个或者多个切面的对象。
- AOP 代理(AOPProxy):通过 Java 动静代理或者 CGLib 加强失去的代理对象。
- 织入(Weaving):将切面整合到残缺的流执行流程。
Spring 的 AOP 的性能和指标
Spring 的 AOP 应用纯 Java 语言实现(如 AspectJ 就不是 Java 语言),不须要任何额定的编译流程,不须要批改类加载器,实用于任何 Servlet 容器和应用服务。Spring 的 AOP 只反对办法拦挡,不反对字段拦挡,如果用户须要应用字段拦挡,能够思考引入 AspectJ 等相似的框架。
Spring 的 AOP 框架和其它的框架有些不同,Spring 的 Aop 框架不仅仅是为了提供一个 AOP 性能,它更重要的性能是和 Spring 的 IOC 容器联合,提供一些企业应用服务的解决方案(如事务等),咱们能够和定义一个一般 Bean 一样的形式去定以一个切面。Spring 的 AOP 不反对十分细粒度的 AOP,只反对对容器中的 Bean 进行 AOP,如果须要更细粒度的 AOP,能够思考应用 AspectJ。Spring 容器的一个优良的个性就是非侵入性,所以你能够灵便的抉择本人的 AOP 计划,不肯定非要应用 Spring 的计划。
代理形式
Spring 对实现接口的办法默认应用 Java 动静代理实现 AOP 拦挡,对于非接口办法则会应用 CGLIB 字节码工具实现代理。
@AspectJ 的反对
@AspectJ 注解能够把一个一般的 Java 类申明为切面。@AspectJ 注解是 AspectJ5 引入的注解,Spring 尽管能够读取 AspectJ5 的注解用于切面元数据的配置,然而在运行的时候应用的依然是 Spring AOP 进行代理,而没有应用 AspectJ 的编译器或者织入逻辑。咱们会在后续探讨如何在 Spring 中应用 AspectJ 的编译器和织入逻辑。
启用 @AspectJ
Spring 默认没有启用 AspectJ,如果你须要 Spring 反对 @AspectJ 注解对应的切面,能够通过在配置类上增加注解或者应用 XML 启用配置(AspectJ 所在的包是:aspectjweaver.jar)。
通过 Java 注解启用 AspectJ 注解反对 :
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}
通过 XML 配置启用 AspectJ 注解
<aop:aspectj-autoproxy/>
定义一个切面
当启用 AspectJ 反对之后,开发者定义的任何 Aspect 切面会主动地被检测到,而后 Spring AOP 会对切面进行拦挡。上面两个例子展现了如何配置 AspectJ 切面,你能够通过增加 @Component 注解把切面 Bean 注册到 Spring 容器中。
<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
<!-- configure properties of the aspect here -->
</bean>
package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect {}
申明一个切入点
切入点程序运行过程中咱们感兴趣的一个点,Spring 的 AOP 框架只反对发现对 Spring Bean 办法上的切入点,因而你能够简略的把切入点了解为 SpringBean 的办法。
切入点确定感兴趣的连接点,从而使咱们可能管制何时运行告诉。springaop 只反对 springbean 的办法执行连接点,因而能够将切入点看作与 springbean 上办法的执行相匹配。切入点申明由两局部组成:一部分是由名称和任何参数组成的签名,另一部分是确定咱们感兴趣的办法执行的切入点表达式。在 AOP 的 @AspectJ 正文款式中,切入点签名由惯例办法定义提供,切入点表达式由 @pointcut 正文批示(用作切入点签名的办法必须具备 void 返回类型)。切入点由两局部组成,一部分是用于区别不同切入点的标识(上面例子中的 private void anyOldTransfer() {}
)),另外一部分是确定咱们感兴趣的 Bean 办法的表达式 (上面例子中的 @Pointcut("execution(* transfer(..))")
),上面的例子展现了一个切入点的定义:
@Pointcut("execution(* transfer(..))") // the pointcut expression
private void anyOldTransfer() {} // the pointcut signature
Spring 匹配切入点的语法应用了 AspectJ5 中的表达式语法,咱们能够参考 AspectJ 文档相干的语法.
常见的切入点匹配表白
Spring 反对上面常见的 AspectJ 切面定义语法:
- execution:用于匹配办法的连接点。
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
- within:用于匹配类型内的办法。
@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {}
- this:匹配以后 AOP 代理对象的执行办法
@target(org.springframework.transaction.annotation.Transactional)
- target:target 匹配指标对象的类型,即被代理对象的类型,例如 A 继承了 B 接口,则应用 target(”B”),target(”A”)均能够匹配到 A
// 以后 AOP 对象实现了 IPointcutService 接口的任何办法
@Pointcut("target(cn.javass.spring.chapter6.service.IPointcutService)")
private void anyPublicOperation() {}
- args:用于限定切点办法的参数类型。
args(java.io.Serializable)
- @target:被代理对象应该蕴含指定的注解。
@target(org.springframework.transaction.annotation.Transactional)
- @args: 被代理对象的参数蕴含指定的注解。
@args(com.xyz.security.Classified)
- @within: 被代理的对象应蕴含指定注解
@within(org.springframework.transaction.annotation.Transactional)
- @annotation:切入点蕴含指定的注解。
@annotation(org.springframework.transaction.annotation.Transactional)
咱们能够通过“&&”和“||”对多个条件进行组合,AspectJ 还有很多其它的表达式,然而 Spring 不反对除上述表达式以外的其它表达式。AspectJ 其它表达式蕴含:call, get, set, preinitialization, staticinitialization, initialization, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this, @withincode 等。
咱们在应用 Spring 的代理办法之前,应该晓得其代理原理。Java 动静代理只能拦挡 public 接口办法上的调用,CGLib 只能拦挡 public、protected 和 defult 办法。如果你须要更深层次的拦挡,能够思考应用底层的 Aspectj。
切面的加强
咱们在下面的步骤定义好了一个切入点,咱们当初就能够对这个切入点进行额定操作,这些额定操作被称为加强,Spring 反对四种加强形式:前加强、后加强、异样加强和盘绕加强。Spring 反对在加强办法的定义上间接定义切入点。
前加强 BeforeAdvice
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {@Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
public void doAccessCheck() {// ...}
}
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {@Before("execution(* com.xyz.myapp.dao.*.*(..))")
public void doAccessCheck() {// ...}
}
后加强
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning(pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
returning="retVal")
public void doAccessCheck(Object retVal) {// ...}
}
异样加强
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing(pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
throwing="ex")
public void doRecoveryActions(DataAccessException ex) {// ...}
}
盘绕加强
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
public class AroundExample {@Around("com.xyz.myapp.CommonPointcuts.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
}
代理机制
咱们后面说过,Spring AOP 通过动静代理和 CGLIB 实现 AOP 对象的代理。咱们能够通过如下配置设置动静代理全副走 CGLIB。
<aop:config proxy-target-class="true">
<!-- other beans defined here... -->
</aop:config>
代理工厂的应用
Spring AOP 实现代理的外围类是 AspectJProxyFactory
,咱们能够应用这个类编程式生成代理对象:
// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
// add an aspect, the class must be an @AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);
// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect
factory.addAspect(usageTracker);
// now get the proxy object...
MyInterfaceType proxy = factory.getProxy()
本文最先公布至微信公众号,版权所有,禁止转载!