Spring容器蕴含两个重要的个性:面向切面编程(AOP)和管制反转(IOC)。面向切面编程是面向对象(OOP)的一种补充,在面向对象编程的过程中编程针对的指标是一个个对象,而面向切面编程中编程针对的指标是一个个切面。切面反对跨类型跨对象(如事务的切面能够加在任何中央)进行模块化。

<!--more-->

前言

AOP是Spring的要害个性之一,尽管Spring的IOC个性并不依赖于AOP(意味着你能够只应用Spring的IOC个性而不应用AOP个性),然而二者联合起来能够灵便的实现很多中间件解决方案。比方咱们常常应用的事务(@Transaction),就是通过AOP计划实现的。本文重点介绍AOP编程中的一些术语,这些术语不仅仅局限于Spring,它实用于所有的AOP编程。

  1. 切面(Aspect):面向切面编程能够跨类跨对象进行切面编程,一个切面就是对一类横切关注点的模块化。
  2. 切入点(JoinPoint):程序执行过程中的一个点,如办法调用、字段拜访和异样抛出等。
  3. 加强(Advice):用于对切面加强,蕴含前加强、后加强和盘绕加强。大多数AOP框架会对切入点进行拦挡,并在切入点保护一个拦截器链。
  4. 指标对象(TargetObject):蕴含一个或者多个切面的对象。
  5. AOP代理(AOPProxy):通过Java动静代理或者CGLib加强失去的代理对象。
  6. 织入(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@EnableAspectJAutoProxypublic 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;@Aspectpublic 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 expressionprivate 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;@Aspectpublic class BeforeExample {    @Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")    public void doAccessCheck() {        // ...    }}
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class BeforeExample {    @Before("execution(* com.xyz.myapp.dao.*.*(..))")    public void doAccessCheck() {        // ...    }}

后加强

import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterReturning;@Aspectpublic 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;@Aspectpublic 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;@Aspectpublic 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 objectAspectJProxyFactory 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 aspectsfactory.addAspect(SecurityManager.class);// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspectfactory.addAspect(usageTracker);// now get the proxy object...MyInterfaceType proxy = factory.getProxy()
本文最先公布至微信公众号,版权所有,禁止转载!