目录

1、介绍

1.1、AOP概念

1.2、Spring AOP 能力 和 目标

1.2.1、简介

1.2.2、@AspectJ 支持

1.2.3、声明一个aspect

例子

1.2.4、声明advice

1.2.5、Introductions 引入

1.6、Proxying mechanisms 代理机制

1.6.1、Understanding AOP proxies

匹配类型语法(补充)

-- 我的demo(码云)

1、介绍

Aspect-Oriented Programming (AOP),是对OOP的补充。OOP的关键是class,AOP的关键是aspect。

Spring 2.0 引入了更简单但更强大的自定义aspect的方式,可以使用 a schema-based approach 也可以使用 the @AspectJ annotation style 。注意,虽然是使用了AspectJ pointcut语言,但底层仍然是Spring来编织(weaving)。

Spring框架使用AOP来完成以下功能:

  • 提供声明式服务。其中最重要的是声明式事务管理。
  • 允许用户自定义aspect,进行AOP编程。

    1.1、AOP 概念

    先来定义一些关键概念和术语。这些说法不是Spring独有的,而且,不幸的是,AOP术语不是那么直观。

    Aspect:从多个类中切过去的一个概念。事务管理是个好例子。在Spring AOP中,aspect要么使用常规类实现--(the schema-based approach),要么使用@Aspect注解实现--(the @AspectJ style)。

    Join point:一个program执行过程的一个点,例如一个方法的执行或者一个异常的处理。注意,在Spring AOP中,一个join point 总是描述一个方法的执行。

    Advice:aspect在一个特定的join point执行的操作。advice有不同类型:around、before、after。许多AOP框架,包括Spring,利用advice做成拦截器(interceptor) ,围绕join point维护了一组拦截器。

    Pointcut:匹配join point的描述。advice关联了一个pointcut expression,会在任何匹配pointcut expression的join point执行。--例如执行一个具有特定名字的方法!注意:匹配pointcut expression的join point,这个概念是AOP的核心。默认,Spring使用AspectJ pointcut expression语言。

    Introduction:为一个类型声明额外的方法或字段。Spring AOP允许你给 任意advised object 引入新的接口(及其实现)。例如,你可以使用introduction来让一个bean实现 IsModified 接口来简化缓存(~~ 就是加一个标志位)。

    Target object:object being advised by one or more aspects. 也被叫做 advised object。因为Spring AOP是使用runtime proxies实现的,该object也是一个 proxied object。

    AOP proxy:AOP框架创建的一个object,用于实现aspect功能(advise method execution等)。在Spring框架中,AOP proxy是JDK dynamic proxy 或者 CGLIB proxy。

    Weaving:将aspects与其他类型或对象链接起来,以创建一个advised object。可以在编译时(例如,使用AspectJ编译器)、加载时、或者运行时完成。Spring AOP是在运行时 --和其他纯Java的AOP框架一样。

    Types of advice

  • Before advice:在join point之前执行的advice,不能阻止后续流程的继续执行--除非抛出异常。
  • After returning advice:在一个join point正常完成之后执行的advice。
  • After throwing advice:如果一个方法抛出了异常。
  • After (finally) advice:无论join point如何完成(正常还是异常),都执行的advice 。
  • Around advice:环绕。可以选择是否终止执行后续流程。

    以上,around advice是最通用的。虽然Spring AOP提供了这么多类型,但建议使用最贴近功能的类型。如无必要,不要使用around advice。这样可以减免错误发生的可能。

  • join point的概念,再匹配上pointcut,是AOP的关键,可以将其与仅提供拦截的旧技术区分开来。

    1.2、Spring AOP 能力 和 目标

    1.2.1、简介

    Spring AOP以纯Java实现。不需要控制类加载器层次,更适用于Servlet容器或application服务器。

    http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#aop-introduction-spring-defn

    Spring AOP 现在仅支持方法执行的 join point。字段拦截并没有实现--虽然可以添加该支持而不影响core Spring AOP APIs。如果你想advise字段,建议使用AspectJ之类的语言。

    Spring AOP是AOP的一个分支,目的不是提供最完善的AOP功能实现(但实际上Spring AOP是有这个能力的);而是提供AOP实现与Spring IoC之间的密切的集成,以解决企业应用中常见的问题。

    因此,Spring AOP通常配合Spring IoC容器使用。Aspect是使用常规的bean definition 语法来配置的,这是与其他AOP框架的天然区别。

    Spring AOP 与AspectJ竞争,互为补充而非竞争。Spring可以无缝的将AspectJ集成到Spring AOP 和 Spring IoC容器中。

    AOP Proxies (AOP 代理)

    Spring AOP默认使用标准JDK动态代理,这使得任何接口都可以被代理。

    Spring AOP也可以使用CGLIB代理。这样可以代理任何类,而不仅限于接口。

    Spring AOP是基于代理的!!!--理解这点很重要。

    1.2.2、@AspectJ 支持

    务必记住这点:只是使用了@AspectJ的风格(样式),本质上仍然是纯Spring AOP -- 没有依赖任何AspectJ编译器或编织器。就是说对注解的解释都是Spring AOP完成的,与AspectJ无关。

    启用@AspectJ 支持

    两点:启用Spring对配置基于@AspectJ aspect的Spring AOP的支持;启用自动代理。

    自动代理是指:如果Spring认为一个bean被一个或多个aspects advised了,它会自动生成该bean的一个代理,以拦截方法调用,从而保证advice按照需要被执行。

    @AspectJ 支持可以通过XML或Java-based Config来启用。无论哪种方式,都需要确保AspectJ的aspectjweaver.jar 存在于classpath。

    通过Java Configuration来启用@AspectJ支持

    @Configuration
    @EnableAspectJAutoProxy // this
    public class AppConfig {
    }

    通过XML Configuration来启用@AspectJ支持

    <aop:aspectj-autoproxy/>

    1.2.3、声明一个aspect

    启用了@AspectJ之后,在application context中定义的任何bean (其对应的类带有@AspectJ注解),都会被Spring自动探测到,并用于配置Spring AOP。

    下例示意了一个最小定义(几乎没有作用的aspect):

    <bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
    <!-- 正常配置 properties -->
    </bean>
    package org.xyz;
    import org.aspectj.lang.annotation.Aspect;
    @Aspect
    public class NotVeryUsefulAspect {
    }

    注意:@Aspect注解的类可以和其他类一样有方法和字段。可能包含pointcut、advice、以及introduction 声明。

    注意:仅有@Aspect注解是不够的,必须有bean definition,或者有@Component。

    注意:Spring AOP中,不能将aspect作为其他aspect的advice目标。@Aspect注解标记了该类是一个aspect,也因此,将其从自动代理中排除了。

    声明一个pointcut

    再次强调,Spring AOP仅支持方法调用作为Spring bean的join point,所以,就是匹配方法。

    pointcut声明包含两部分:由名字和参数组成的signature;能够决定具体方法执行的pointcut expression。

    在@AspectJ 注解风格中,pointcut签名是由常规方法定义提供的,pointcut expression则通过@Pointcut注解标明 -- 为pointcut signature服务的方法必须返回void。

    例子:

    @Pointcut("execution(* transfer(..))")// the pointcut expression
    private void anyOldTransfer() {}// the pointcut signature

    支持的pointcut指示器

    Spring AOP支持下列AspectJ pointcut designators (PCD),可以用于pointcut expression:

  • execution:匹配方法执行的join point,最基本的。
  • within:限制join point的匹配在特定类型之内。
  • this:限制join point的匹配,bean引用(Spring AOP 代理)必须是给定类型的实例。
  • target:限制被代理的对象是给定类型的实例。
  • args:限制参数是给定类型的实例。
  • @target:限制执行对象的类拥有给定注解。
  • @args:限制运行时实参的类型拥有给定注解。
  • @within:
  • @annotation:

    注意:由于基于代理,protected方法不会被拦截 -- 无论是JDK代理还是CGLIB代理(CGLIB技术上可以做到,但不推荐)。所以,任何pointcut都只会匹配public方法

    如果你的拦截需要包含protected/private方法甚至构造方法,可以考虑使用Spring驱动的native AspectJ weaving 来代替基于代理的Spring AOP框架。

    Spring AOP还支持一个特殊的PCD:bean,可以限制匹配特殊的Spring bean -- 单个或多个(使用通配符)。

    bean(idOrName)

    -- 该PCD仅限于Spring AOP框架。

    -- 该PCD在instance level操作,而非type level。

    combining pointcut expressions

    pointcut expressions可以通过 “&&”、“||”、“!”联合起来。还可以通过签名(signature)的名字来引用一个pointcut expression。

    示例:

    @Pointcut("execution(public * *(..))") // 注意,不要在容器环境下使用该pointcut,否则会导致问题!
    private void anyPublicOperation() {} @Pointcut("within(cn.larry.aop.trading..*)")
    private void inTrading() {} @Pointcut("anyPublicOperation() && inTrading()")
    private void tradingOperation() {}

    注意:签名(signature)的private/protected/public 不会影响pointcut匹配。

    Sharing common pointcut definitions

    开发企业应用时,我们推荐定义一个SystemArchitecture aspect (分层)。典型如下:

     1 package cn.larry.aop.aspect;
    2
    3 import org.aspectj.lang.annotation.Aspect;
    4 import org.aspectj.lang.annotation.Pointcut;
    5
    6 @Aspect
    7 @Component //
    8 public class SystemArchitecture {
    9
    10 /**
    11 * 在cn.larry.aop.web 包及其子包中! */
    12 @Pointcut("within(cn.larry.aop.web..*)")
    13 public void inWebLayer() {}
    14
    15 /**
    16 * 在cn.larry.aop.service 包及其子包中! */
    17 @Pointcut("within(cn.larry.aop.service..*)")
    18 public void inServiceLayer() {}
    19
    20 /**
    21 * 在cn.larry.aop.dao 包及其子包中! */
    22 @Pointcut("within(cn.larry.aop.dao..*)")
    23 public void inDataAccessLayer() {}
    24
    25 /**
    26 * business service是指定义在service接口中的任意方法的执行。该定义假定了接口放在service包中,其实现在子包中。 * 如果你是以功能来对service接口进行分组(例如cn.larry.aop.abc.service and cn.larry.aop.def.service), * 那可以使用"execution(* cn.larry.aop..service.*.*(..))"。 * * 或者,可以使用bean PCD,如"bean(*Service)"。 */
    27 @Pointcut("execution(* cn.larry.aop..service.*.*(..))")
    28 public void businessService() {}
    29
    30 /**
    31 * 数据访问操作是指定义在dao接口中的任意方法的执行。该定义假定了接口是放在dao包中,其实现在子包中。 */
    32 @Pointcut("execution(* cn.larry.aop.dao.*.*(..))")
    33 public void dataAccessOperation() {}
    34
    35 }

    例子

    Spring AOP用户大多数时候会使用 execution pointcut designator,其格式如下:

    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
    throws-pattern?)

    除了返回类型(ret-type-pattern)、name pattern、还有parameters pattern之外,都是可选的。

  • 返回类型:全路径,或者使用*通配符代表所有返回类型。
  • 方法名:可以使用*通配符代表全部或部分。
  • 参数部分:()表示没有参数;(..)表示任意数量参数;(*)表示接受一个任意类型的参数;(*,String)表示两个参数,第一个参数任意类型,第二个参数String类型。

    例子:

  • 匹配所有public方法:
    execution(public * *(..))
  • 匹配名字以set开头的方法:
    execution(* set*(..))
  • 匹配cn.larry.aop.service.AccountService接口中的任意方法:
    execution(* cn.larry.aop.service.AccountService.*(..))
  • 匹配cn.larry.aop.service包中的任意方法:
    execution(* cn.larry.aop.service.*.*(..))
  • 匹配cn.larry.aop.service包及其子包中的任意方法【service后面两个点】:
    execution(* cn.larry.aop.service..*.*(..))
  • 匹配cn.larry.aop.service包中的任意join point:
    within(cn.larry.aop.service.*)
  • 匹配cn.larry.aop.service包及其子包中的任意join point:
    within(cn.larry.aop.service..*)
  • 匹配代理实现了cn.larry.aop.service.AccountService接口的任意join point:
    this(cn.larry.aop.service.AccountService)
  • 匹配目标对象实现了cn.larry.aop.service.AccountService接口的任意join point:
    target(cn.larry.aop.service.AccountService)
  • 匹配所有只接收一个参数,且运行时传入的实参是Serializable的join point:
    args(java.io.Serializable)

    注意,上面的pointcut不同于 execution(* *(java.io.Serializable)):args版本匹配运行时,execution版本匹配方法签名声明了单独的Serializable类型参数。-- 后者的方法只能接收Serializable类型,前者则可能接收其他类型。

  • 匹配目标对象拥有@Transactional注解的任意join point:
    @target(org.springframework.transaction.annotation.Transactional)
  • 匹配目标对象的声明类型拥有@Transactional注解的任意join point:
    @within(org.springframework.transaction.annotation.Transactional)
  • 匹配执行方法拥有@Transactional注解的任意join point:
    @annotation(org.springframework.transaction.annotation.Transactional)
  • 匹配只接收一个参数,且运行时实参类型拥有@Classified注解的任意join point:
    @args(com.xyz.security.Classified)
  • 匹配名字为tradeService的Spring bean上的任意join point:
    bean(tradeService)
  • 匹配名字符合通配符表达式*Service的Spring bean上的任意join point:
    bean(*Service)

    编写良好的pointcut (略)

    http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#writing-good-pointcuts

    1.2.4、声明advice

    advice关联到一个pointcut expression,运行在pointcut匹配的方法之前、之后或环绕。

  • Before advice

    pointcut expression可以是一个已有的pointcut的简单引用,也可以是in place声明的。

    引用已有的pointcut:

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before; @Aspect
    public class BeforeExample { @Before("cn.larry.aop.aspect.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {
    // ...
    } }

    如果使用in-place pointcut expression,可以这样重写上面的代码:

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before; @Aspect
    public class BeforeExample { @Before("execution(* cn.larry.aop.dao.*.*(..))") // 将pointcut和advice写到一起,就是in-place,就地处斩的“就地”。
    public void doAccessCheck() {
    // 具体的工作
    } }
  • After returning advice

    匹配的方法正常返回后运行。

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.AfterReturning; @Aspect
    public class AfterReturningExample { @AfterReturning("cn.larry.aop.aspect.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {
    // 具体工作
    } }

    -- 注意,可以在同一个@Aspect拥有多个advice声明以及其他成员。

    有时候你需要在advice体内访问实际的返回值,如下:

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.AfterReturning; @Aspect
    public class AfterReturningExample { @AfterReturning(
    pointcut="cn.larry.aop.aspect.SystemArchitecture.dataAccessOperation()",
    returning="retVal") // returning
    public void doAccessCheck(Object retVal) {
    // ...
    } }

    注意上面,returning的值就是advice方法的参数名。另外,advice方法的参数类型反过来也会称为匹配pointcut要求的一部分,即方法必须返回该类型才能匹配 -- 这里用的是Object类型,可以匹配任何方法返回类型。

  • After throwing advice

    当方法抛出异常退出时执行。

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.AfterThrowing; @Aspect
    public class AfterThrowingExample { @AfterThrowing("cn.larry.aop.aspect.SystemArchitecture.dataAccessOperation()")
    public void doRecoveryActions() {
    // ...
    } }

    有时候你只想在抛出特定类型的异常时执行advice:

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.AfterThrowing; @Aspect
    public class AfterThrowingExample { @AfterThrowing(
    pointcut="cn.larry.aop.aspect.SystemArchitecture.dataAccessOperation()",
    throwing="ex") // throwing!!!
    public void doRecoveryActions(DataAccessException ex) { // ex
    // ...
    } }

    同after returning类似,这里的throwing的值也会反过来限制方法。

  • After (finally) advice

    只要结束就执行--无论正常还是异常。必须做好处理正常和异常的准备。通常用于释放资源。

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.After; @Aspect
    public class AfterFinallyExample { @After("cn.larry.aop.aspect.SystemArchitecture.dataAccessOperation()")
    public void doReleaseLock() {
    // ...
    } }
  • Around advice

    http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#aop-ataspectj-around-advice

    可以在方法执行之前、之后执行不同的工作。

    使用@Around注解,advice method的第一个形参必须是ProceedingJoinPoint类型。advice体内可以调用ProceedingJoinPoint的proceed()方法让底层的方法继续执行。proceed()也可以传入参数,Object[] -- 这些参数会被用作执行方法的实参!!!

    -- 稍微不同于AspectJ。AspectJ需要参数数量与advised method的参数数量一致。

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.ProceedingJoinPoint; @Aspect
    public class AroundExample { @Around("cn.larry.aop.aspect.SystemArchitecture.businessService()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed(); // 可以传入参数!!!Object[]
    // stop stopwatch
    return retVal;
    } }

    注意,around advice method 必须返回advised method返回的返回值(如果有),否则会出错。

    注意,proceed可以被调用任意次!!!甚至,不调用也可以!!!

    Advice parameters

    Spring提供了fully typed advice。就是说,可以在advice signature中声明需要的参数(例如上面提到的returning、throwing例子),而不仅仅是Object[]。下面来看看如何让这些参数在advice body中起作用。

    a> Accessing to the current JoinPoint

    任何advice method都可以声明第一个形参为org.aspectj.lang.JoinPoint类型(around advice必须是ProceedingJoinPoint,这是JoinPoint的子类)。

    JoinPoint接口提供了大量有用的方法,例如getArgs()(返回方法参数)、getThis()(返回代理对象)、getTarget()(返回目标对象)、getSignature()(返回advised method的描述)、toString()(打印advised method的有用描述)。 详见javadoc。

    b> Passing parameters to advice

    上面已经讲过如何绑定返回值或者异常。为了让argument values在advice body中可用,可以使用args绑定形式。详见下例:

    @Before("cn.larry.aop.aspect.SystemArchitecture.dataAccessOperation() && args(account,..)") // args(account,..)
    public void validateAccount(Account account) { // account
    // ...
    }

    pointcut expression的args(account,..)部分有两个作用:一,强制匹配至少一个参数的方法执行,且第一个参数必须是Account实例;二,让实际的Account对象在advice中可用!!!

    另一种方式如下:

    @Pointcut("cn.larry.aop.aspect.SystemArchitecture.dataAccessOperation() && args(account,..)")
    private void accountDataAccessOperation(Account account) {} @Before("accountDataAccessOperation(account)") // name signature ref
    public void validateAccount(Account account) {
    // ...
    }

    代理对象(this)、目标对象(target)、还有注解(@within, @target, @annotation, @args)可以用类似方式绑定。下例示意了如何在advice body中获取advised method的注解内容:

    先看看 @Auditable注解:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Auditable {
    AuditCode value();
    }

    再来看看advice:

    @Before("cn.larry.aop.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
    public void audit(Auditable auditable) {
    AuditCode code = auditable.value(); // 获取注解的值
    // ...
    }

    c> Advice parameters and generics

    Spring AOP可以处理用在类声明和方法参数中的泛型。假定你有如下泛型类:

    public interface Sample<T> {
    void sampleGenericMethod(T param);
    void sampleGenericCollectionMethod(Collection<T> param);
    }

    你可以限制方法拦截类型为特定参数类型,通过advice parameter:

    @Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
    public void beforeSampleMethod(MyType param) { // advice parameter:param
    // Advice implementation
    }

    上面是通过advice method的形参类型来限制pointcut,前面已有类似案例。

    但是,需要注意,不能用于泛型集合。所以下面这样是错误的:

    @Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
    public void beforeSampleMethod(Collection<MyType> param) { // 非法的!不能泛型集合
    // Advice implementation
    }

    要想完成上面的需求,只能手动遍历检查集合。

    d> Determining argument names

    绑定advice调用的parameter,依赖于匹配:pointcut expression中的name 到 (advice and pointcut) method signature中声明的name之间的匹配。parameter names在反射中不可用,所以Spring AOP使用以下策略来决定parameter names:

    ① 如果用户显式的指定了parameter names,那么该parameter names被用于:advice 和 pointcut 注解都有一个可选属性”argNames”,该属性可以用于指定被注解方法的argument names - 这些argument names在运行时可用。例如:

    @Before(value="cn.larry.aop.aspect.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
    argNames="bean,auditable") // 这里
    public void audit(Object bean, Auditable auditable) { // 对应这里
    AuditCode code = auditable.value();
    // ... use code and bean
    }

    如果第一个parameter是JoinPoint、ProceedingJoinPoint或者JoinPoint.StaticPart类型,可以不必在”argNames”中指出该parameter name!例如:

    @Before(value="cn.larry.aop.aspect.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
    argNames="bean,auditable") // 没有jp,因为默认可以省略
    public void audit(JoinPoint jp, Object bean, Auditable auditable) { // jp
    AuditCode code = auditable.value();
    // ... use code, bean, and jp
    }

    这种对于第一个parameter是JoinPoint、ProceedingJoinPoint或者JoinPoint.StaticPart类型的特别对待是非常有用的。例如:

    @Before("cn.larry.aop.aspect.Pointcuts.anyPublicMethod()") // no argNames
    public void audit(JoinPoint jp) {
    // ... use jp
    }

    ② 使用 argNames 属性略显臃肿,所以,如果没有指定该属性,Spring AOP会查看类的debug信息并试图从本地变量表中决定parameter names。该信息只有在类以debug information模式(-g:vars)编译时才会出现。该编译的结果:代码稍微易懂(逆向设计);class文件略大;不会进行移除无用本地变量的优化。

    如果@AspectJ aspect是用AspectJ编译器(ajc)编译的,哪怕没有使用debug information模式,也不需要添加argNames属性,因为编译器会保留需要的信息。

    ③ 如果code没有以debug information模式编译,Spring AOP会尝试推断要绑定的变量与parameter之间的配对(例如,如果只有一个变量,而advice method也只有一个参数,那它们就是一对)。如果变量的绑定是模糊的,会抛出一个AmbiguousBindingException。

    ④ 如果以上策略都失败了,抛出IllegalArgumentException。

    e> Proceeding with arguments

    如何在ProceedingJoinPoint.proceeding()时传入实参?

    @Around("execution(List<Account> find*(..)) && " +
    "cn.larry.aop.aspect.SystemArchitecture.inDataAccessLayer() && " +
    "args(accountHolderNamePattern)")
    public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
    String accountHolderNamePattern) throws Throwable {
    String newPattern = preProcess(accountHolderNamePattern);
    return pjp.proceed(new Object[] {newPattern}); // 这里
    }

    f> Advice ordering

    多个advice之间的顺序问题。Spring AOP遵守AspectJ的优先级。

    进入时最高优先级先执行,离开时,最高优先级最后执行。

    当两个advice定义在不同的aspects中,且都需要运行在相同的join point时,未定义!你可以通过指定优先级来控制顺序。@Order或实现Ordered接口。

    当两个advice定义在同一个aspect中,且都需要运行在相同的join point时,未定义!可以考虑将两个advice合并成一个!或者放到不同的aspect类中!

    1.2.5、Introductions 引入

    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-introductions

    引入就是可以让advised objects 实现 指定的接口,并提供该接口的一个实现!就是说,对象可以调用原本不存在的方法!

    通过@DeclaredParents注解实现 -- 顾名思义,就是给当前对象找一个爸爸!!!见代码:

    @Aspect
    public class UsageTracking { @DeclareParents(value="cn.larry.aop.service.*+", defaultImpl=DefaultUsageTracked.class) // 定义一个introduction
    public static UsageTracked mixin; // 这个类型就是要实现的接口! /**
    * 就是让特定目标有一个假的父类,然后针对该父类执行advice。--应该是,囧
    */
    @Before("cn.larry.aop.aspect.SystemArchitecture.businessService() && this(usageTracked)") // 引用introduction
    public void recordUsage(UsageTracked usageTracked) {
    usageTracked.incrementUseCount();
    } }

    上例中,指定了所有的service接口的实现同时实现了UsageTracked接口。所以,可以这样:

    UsageTracked usageTracked = (UsageTracked) context.getBean("myService");

    1.2.6、Aspect instantiation models

    提醒:这部分属于高级主题,如果刚开始接触AOP,建议略过,以后再来看。 -- 我也略过好了。

    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-instantiation-models

    1.2.7、案例 (略)

    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-ataspectj-example

    1.3、 Schema-based AOP support  (XML风格,略)

    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-schema

    1.4、 Choosing which AOP declaration style to use (略)

    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-choosing

    1.5、 Mixing aspect types

    一句,可以混合两种风格。

    1.6、 Proxying mechanisms 代理机制

    Spring AOP可以使用JDK动态代理或者CGLIB代理。如果可以,推荐JDK动态代理。

    如果要代理的目标对象实现了最少一个接口,那会使用JDK动态代理 -- 所有的接口都会被代理。如果没有实现任何接口,那会创建一个CGLIB代理。

    如果要强制使用CGLIB代理,需要考虑几个问题:

  • final methods不能被advised,因为不能被覆盖!
  • 自Spring 3.2起,不再需要添加CGLIB库了,因为Spring框架重新打包了它并将其包含在spring-core JAR中。

    自Spring 4.0起,被代理对象的构造方法不会被调用两次了!因为CGLIB代理实例会通过Objenesis(神马东西?)创建。只有你的JVM不允许构造方法bypassing时,你才可能会看到两次调用和相应debug日志。

    强制使用CGLIB代理(XML):

    <aop:config proxy-target-class="true">
    <!-- other beans defined here... -->
    </aop:config>

    或@AspectJ

    <aop:aspectj-autoproxy proxy-target-class="true"/>

    @EnableAspectJAutoProxy(proxyTargetClass=true)

    注意,还可以用于<tx:annotation-driven/>

    1.6.1、 Understanding AOP proxies

    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies

    AOP代理和普通调用的区别:

    先来个常规类:

    public class SimplePojo implements Pojo {
    
        public void foo() {
    // this next method invocation is a direct call on the 'this' reference
    this.bar();
    } public void bar() {
    // some logic...
    }
    }

    注意,foo()内部调用了bar()。如果是代理类,其调用过程是这样的:

    就是说,不会拦截方法内部的方法! 因为,实际上有两个对象,proxy object和target object,前者会拦截对后者的访问,但是,一旦进入了后者的方法,就已经在后者内部了。

    1.7、 Programmatic creation of @AspectJ Proxies 编码创建@AspectJ代理 (略)

    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-aspectj-programmatic

    1.8、 Using AspectJ with Spring applications (略)

    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-using-aspectj

    补充:

    类型匹配语法 

    AspectJ类型匹配的通配符:

    *:匹配任何数量字符;

    ..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。

             +:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。

    例如:

    java.lang.String    匹配String类型;
    java.*.String 匹配java包下的任何“一级子包”下的String类型; 如匹配java.lang.String,但不匹配java.lang.ss.String
    java..* 匹配java包及任何子包下的任何类型; 如匹配java.lang.String、java.lang.annotation.Annotation
    java.lang.*ing 匹配任何java.lang包下的以ing结尾的类型;
    java.lang.Number+ 匹配java.lang包下的任何Number的自类型;如匹配java.lang.Integer,也匹配java.math.BigInteger

    参考:

    Spring AspectJ切入点语法详解

  • Spring 4 官方文档学习(六)核心技术之Spring AOP的更多相关文章

    1. Spring Boot 官方文档学习(一)入门及使用

      个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...

    2. Spring boot官方文档学习(一)

      个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...

    3. Spring 4 官方文档学习(十二)View技术

      关键词:view technology.template.template engine.markup.内容较多,按需查用即可. 介绍 Thymeleaf Groovy Markup Template ...

    4. Spring 4 官方文档学习(十一)Web MVC 框架之配置Spring MVC

      内容列表: 启用MVC Java config 或 MVC XML namespace 修改已提供的配置 类型转换和格式化 校验 拦截器 内容协商 View Controllers View Reso ...

    5. Spring 4 官方文档学习(十一)Web MVC 框架之resolving views 解析视图

      接前面的Spring 4 官方文档学习(十一)Web MVC 框架,那篇太长,故另起一篇. 针对web应用的所有的MVC框架,都会提供一种呈现views的方式.Spring提供了view resolv ...

    6. Spring 4 官方文档学习(十一)Web MVC 框架

      介绍Spring Web MVC 框架 Spring Web MVC的特性 其他MVC实现的可插拔性 DispatcherServlet 在WebApplicationContext中的特殊的bean ...

    7. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(一)

      题外话:本篇是对之前那篇的重排版.并拆分成两篇,免得没了看的兴趣. 前言 在Spring Framework官方文档中,这三者是放到一起讲的,但没有解释为什么放到一起.大概是默认了读者都是有相关经验的 ...

    8. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(二)

      接前一篇 Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 本篇主要内容:Spring Type Conver ...

    9. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion

      本篇太乱,请移步: Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 写了删删了写,反复几次,对自己的描述很不 ...

    10. Spring 4 官方文档学习 Spring与Java EE技术的集成

      本部分覆盖了以下内容: Chapter 28, Remoting and web services using Spring -- 使用Spring进行远程和web服务 Chapter 29, Ent ...

    随机推荐

    1. Accelerated C++学习笔记7—&lt;使用库算法&gt;

      第6章  使用库算法 本章中主要教我们怎样使用几个库算法来解决与处理字符串和学生成绩相关的问题. 1.分析字符串 使用一个循环来连接两幅字符图案 <span style="font-f ...

    2. 控件不响应OnMouseMove

      原文链接: http://bbs.csdn.net/topics/370017205 问:我继承CStatic写了个CStaticEx,设为Rectangle类型,用来画图.把这个控件添加到了一个Vi ...

    3. 本地PC安装Centos 6.5 操作手册及遇到的问题

      我采取的是使用U盘安装 一.准备工作 1.下载Centos6.5 ISO文件 我在官网上下的6.5版本CentOS-6.5-x86_64-bin-DVD1.iso, 由于CentOS-6.5-x86_ ...

    4. spring boot实战读书笔记1

      1 覆盖起步依赖引入的传递依赖. 以Spring Boot的Web起步依赖为例,它传递依赖了Jackson JSON库.如果不想使用,可以使用 <exclusions>元素去除Jackso ...

    5. 2. 解题报告~买卖股票的最佳时机 II

      原题地址:https://leetcode-cn.com/explore/interview/card/top-interview-questions-easy/1/array/22/ 给定一个数组, ...

    6. Solr 数字字符不能搜索的一个问题

      问题一: 测试人员告诉我数字不能被搜索.于是开始找原因: <fields> ***<field name="productName" type="tex ...

    7. jquery ajax中success与complete的执行顺序 (转)

      http://blog.sina.com.cn/s/blog_4adc4b090101dhnh.html https://q.cnblogs.com/q/21810/ **************** ...

    8. Python2 获取两日期之间的每一天

      import datetime def getEveryDay(begin_date,end_date): date_list = [] begin_date = datetime.datetime. ...

    9. Logstash5.3借助临时字段修改@timestamp为北京时间,方便按天生成output文件

      $more config/first-pipeline.conf input { beats { port => " } } filter { if [type] == "s ...

    10. rzsz安装【转】

      环境:CentOS 发生情况:需要安装工具:szrz 工具进行 windows 和linux传文件 安装方式:从网上其他教程找的所以就按照如下方式操作 1. 下载软件 rzsz-3.34.tar.gz ...