Spring AOP编程官方文档解读之切点

Spring AOP编程官方文档解读目录


前言

AOP(Aspect Oriented Programming)面向切面编程与OOP (Object-Oriented Programming)面向对象编程在java中都占有非常重要的地位,Java是一个面向对象编程的语言,面向切面编程通过提供对程序结构不同的思维方式对OOP进行补充。对于OOP来说,最主要的模块单元是类,对于AOP来说是切面(aspect).这些切面使关注点模块化,例如跨多个类或对象、方法的事务管理。此类关注点通常被称为横切关注点。

Spring的关键组件之一是AOP框架。 尽管Spring IOC容器不依赖于AOP,这意味着在不需要时就不需要使用AOP,但AOP是对Spring IOC的补充,可以提供功能强大的中间件解决方案。。


简单项目

创建一个maven项目,并引入jar包

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
	<version>4.3.27.RELEASE</version>
</dependency>

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aop</artifactId>
	<version>4.3.27.RELEASE</version>
</dependency>

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
	<version>4.3.27.RELEASE</version>
</dependency>

创建以下的服务接口

package com.example.aop.anno.service;

import com.example.aop.anno.entity.Order;

public interface OrderService {

    Order add(Order order);

    Order findById(Long ordId);

    boolean delete(Long ordId);
}

其实现如下

package com.example.aop.anno.service.impl;

import com.example.aop.anno.entity.Order;
import com.example.aop.anno.service.OrderService;
import org.springframework.stereotype.Service;

@Service
public class SimpleOrderService implements OrderService {
    public Order add(Order order) {
        System.out.println("-----------SimpleOrderService------add------------");
        return new Order();
    }

    public Order findById(Long ordId) {
        System.out.println("-----------SimpleOrderService------findById------------");
        return new Order();
    }

    public boolean delete(Long ordId) {
        System.out.println("-----------SimpleOrderService------delete------------");
        return true;
    }
}

如果此时执行add方法,只会执行add实现方法体内的逻辑,如果需要进行增加额外的逻辑,可以通过修改方法的实现。比如首先输出当前方法的参数。修改之后如下:

public Order add(Order order) {
    System.out.println(order);
    System.out.println("-----------SimpleOrderService------add------------");
    return new Order();
}

如果该类中其他的方法也要输出方法输入参数呢?更进一步,其他类中的其他方法也要输出方法输入参数呢?如果不介意成本或者代码的优雅,当然可以一个方法一个方法像上面那样的添加代码。但是AOP给我们另外一个更优雅的解决方案,在每个方法执行之前切入相同的逻辑。而这个切面所执行相同的逻辑,以下都会用增强这个词来替代,英文advice。进行增强的目标有类、对象或者方法,其实最终还是执行方法之上(Spring AOP并不支持属性的增强),所以这些类、对象或者方法都可以称之为连接点(joint point),对应Java类org.aspectj.lang.JoinPoint。另外,究竟是哪些类、对象或者方法需要增强呢?这就需要通过切入点来匹配了。切入点(point cut)简称切点,对应类org.aspectj.lang.reflect.Pointcut。切点用于匹配连接点(二者不是一个概念)。

Join point: a point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.

Pointcut: a predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.

Advice: action taken by an aspect at a particular join point. Different types of advice include “around”, “before” and “after” advice. (Advice types are discussed below.) Many AOP frameworks, including Spring, model an advice as an interceptor, maintaining a chain of interceptors around the join point.

首先开启Aspect(通过加入注解@EnableAspectJAutoProxy开启)

@ComponentScan("com.example.aop.anno")
@Configuration
@EnableAspectJAutoProxy
public class RootConfig {
}

定义一个自定义的切面类

package com.example.aop.anno.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
@Aspect
public class AnCustomAspect {

    @Pointcut("execution(* com.example.aop.anno.service.OrderService.*(..))")// the pointcut expression
    private void anPointCut() {}// the pointcut signature


    @Before("anPointCut()")
    public void anBeforeAdvice(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args.length < 1){
            System.out.println("no args");
            return;
        }
        if (args.length == 1){
            System.out.println(args[0]);
            return;
        }
        StringBuilder builder = new StringBuilder();
        for (Object arg : args) {
            builder.append(arg).append(",");
        }
        builder.delete(builder.length()-1,builder.length());
        System.out.println(builder.toString());
    }
}

首先类com.example.aop.anno.AnCustomAspect上面必须添加org.aspectj.lang.annotation.Aspect注解,标识当前类作为一个切面。同时要注意加上org.springframework.stereotype.Component注解保证能被Spring容器扫描并注册、管理,因为Spring中切面编程是针对于容器中的bean的。Aspect只能标识这个类作为一个切面,而没有作为Spring容器扫描的标识。

在这个类中包含两个主要部分,一个通过@Pointcut标识的方法,也就是切点,通过切点表达式匹配需要增强的那些类、对象或方法。切点表达式是在org.aspectj.lang.annotation.Pointcut注解属性中来定义的。另一个就是
包含@Before注解的方法,被称为Advice(增强),方法内定义了需要执行的代码,而@Before指代执行的时机。在连接点的前面还是后面(此处是连接点的前面)。

以下针对切点的定义进行详细的探讨。

pointcuts determine join points of interest, and thus enable us to control when advice executes. Spring AOP only supports method execution join points for Spring beans, so you can think of a pointcut as matching the execution of methods on Spring beans. A pointcut declaration has two parts: a signature comprising a name and any parameters, and a pointcut expression that determines exactly which method executions we are interested in. In the @AspectJ annotation-style of AOP, a pointcut signature is provided by a regular method definition, and the pointcut expression is indicated using the @Pointcut annotation (the method serving as the pointcut signature must have a void return type).

In Spring AOP, it is not possible to have aspects themselves be the target of advice from other aspects. The @Aspect annotation on a class marks it as an aspect, and hence excludes it from auto-proxying.

execution

for matching method execution join points, this is the primary pointcut designator you will use when working with Spring AOP

比如上面实例中的@Pointcut("execution(* com.example.aop.anno.service.OrderService.*(..))")就是其中一种方式。完整定义如下所示:

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

modifiers-pattern:匹配方法修饰符 publicor protected
ret-type-pattern:匹配方法返回类型 *代表所有类型
declaring-type-pattern:匹配类名称 可以包含....前者代表单层目录,后者代表任意层级目录。
name-pattern:匹配方法名称 *匹配任意方法名称
param-pattern:匹配参数名称 如果是()代表没有方法参数,(..)匹配任意个数或类型参数,(*)匹配一个任意类型的参数,(*,String)匹配两个参数,第一个任意类型,第二个参数必须为String类型。
throws-pattern:匹配方法异常类型
其中returning type pattern、name pattern, param-pattern是必须的,其他的可以不需要。比如execution(* set*(..))就是一个最简单的,ret-type-pattern为*(匹配任意返回类型的方法),name-pattern为set*(匹配方法名称以set开头的方法),param-pattern为…(匹配任意类型或者数量的方法参数),其他的没有定义,完整意思就是:匹配所有方法名以set开头的方法

以上表达式所代表的含义就是com.example.aop.anno.service.OrderService接口中任意方法都满足当前切点要求。returning type pattern*代表方法返回类型可以为任意类型,declaring-type-patterncom.example.aop.anno.service.OrderService,代表类或接口名称与此相同才会增强,其后的.算入name-pattern中,那么name-pattern.*代表任意方法名称都可以,param-pattern..代表可以是任意个数或类型的参数。很明显SimpleOrderService中的三个方法都满足以上要求,因此在这里这三个方法就是连接点,切点与连接点的关系就是一对多的关系(这里是一对多,其他地方可能一对一,甚至一对无)。

编写启动类进行测试

package com.example.aop.anno;

import com.example.aop.anno.config.RootConfig;
import com.example.aop.anno.entity.Order;
import com.example.aop.anno.service.OrderService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.math.BigDecimal;

public class SpringStartMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        OrderService orderService = applicationContext.getBean(OrderService.class);
        long ordId = System.currentTimeMillis();
        orderService.add(new Order(ordId,"HuaWei",new BigDecimal("5998.00")));
        orderService.findById(ordId);
        orderService.delete(ordId);
        applicationContext.close();
    }
}

在这里插入图片描述
可以看到这三个方法都得到了增强(对应方法执行之前输出了参数)。

修改切点定义为@Pointcut("execution(* com.example.aop.anno.service.OrderService.add(..))")限定匹配的方法名称为add
在这里插入图片描述
可以看到此时只有add方法进行了增强。

常见的切点定义如下所示:
execution(public * *(..)) :所有的公有方法
execution(* set*(..)) :所有名称以set开头的方法
execution(* com.xyz.service.AccountService.*(..)):AccountService接口中定义的所有方法
execution(* com.xyz.service.*.*(..)):com.xyz.service包下面定义的所有方法(不包含子包)
execution(* com.xyz.service..*.*(..)):com.xyz.service包下面定义的所有方法(包含子包)

within

limits matching to join points within certain types (simply the execution of a method declared within a matching type when using Spring AOP)

除了execution关键字,还有如下其中形式
within(com.example.aop.anno.service.*):com.example.aop.anno.service包内所有类,不包含子包
在这里插入图片描述
within(com.example.aop.anno.service..*):com.example.aop.anno.service包内所有类,包含子包
在这里插入图片描述
通过测试可以看出对于within作用的主要是对应的类而不是接口,由于真实类为com.example.aop.anno.service.impl.SimpleOrderService如下图所示,所以within(com.example.aop.anno.service.*)并不能匹配到(不包含子包)
在这里插入图片描述

this

limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type

this(com.example.aop.anno.service.OrderService):代理之后的类实现了OrderService接口
在这里插入图片描述
可以看一下当前orderService的类型
在这里插入图片描述
那么这个类是不是实现了OrderService接口接口呢?
在这里插入图片描述
反过来理解一下,通过当前切面增强之后的类型是不是实现了指定类型的接口。

target

limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type

target(com.example.aop.anno.service.OrderService)代理的目标类是否实现了指定的接口
在这里插入图片描述
从结果来看答案是的。

对于this和target,第一次接触的人可能比较懵逼,此处来详细解释一下。理解Spring Aop的重点其实就是理解一个类如何被增强,所谓的被增强其实一个类被proxied(代理)。比如我们需要对com.example.aop.anno.service.impl.SimpleOrderService进行增强,其实就是对该类进行代理。代理之后的类与SimpleOrderService有一定的关系。而此处SimpleOrderService对象称为target(目标),而增强之后的对象称为this。target通过proxy,成为this。在默认情况下,Spring Aop通过JDK的动态代理对目标类进行增强,目标类对象肯定是实现OrderService接口,所以target(com.example.aop.anno.service.OrderService)匹配成功,而通过JDK代理是针对接口的,因为首先会找到SimpleOrderService这个类实现的接口OrderService,然后根据这个接口实现类来代理SimpleOrderService这个类的逻辑,大致可以理解为实现了如下这样一个类,而构造参数就是目标类对象。

public class Proxy123 implements OrderService {

    OrderService orderService;
    
    public Proxy123(OrderService orderService){
        this.orderService = orderService;
    }

    @Override
    public Order add(Order order) {
        // before advive 
        return orderService.add(order);
    }

    @Override
    public Order findById(Long ordId) {
        // .. before advive 
        return orderService.findById(ordId);
    }

    @Override
    public boolean delete(Long ordId) {
        // .. before advive 
        return orderService.delete(ordId);
    }
}

这个增强类也实现了OrderService接口。因此this(com.example.aop.anno.service.OrderService)也是可以匹配成功的。

对代理深入的理解请参考:一个类被代理或多次代理之后,如何获取原来的类型?

It is important to grasp the fact that Spring AOP is proxy-based.

在上面案例中,SimpleOrderService对象(target)也称为Target object,而增强后的那个Proxy类对象(this)也称为AOP proxy。在Spring Aop中,一个AOP proxy可能是JDK动态代理对象(默认方式)或者CGLIB代理对象。

Target object: object being advised by one or more aspects. Also referred to as the advised object. Since Spring AOP is implemented using runtime proxies, this object will always be a proxied object.

AOP proxy: an object created by the AOP framework in order to implement the aspect contracts (advise method executions and so on). In the Spring Framework, an AOP proxy will be a JDK dynamic proxy or a CGLIB proxy.

args

limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types

@Pointcut("args(com.example.aop.anno.entity.Order)"):运行时参数属于Order类型(或接口)
在这里插入图片描述
此时只有第一个方法满足,其他方法参数都是Long类型的。修改为@Pointcut("args(java.lang.Long)")
在这里插入图片描述
此时只有另外两个方法能匹配了。

Note that the pointcut given in this example is different to execution(* *(java.io.Serializable)): the args version matches if the argument passed at runtime is Serializable, the execution version matches if the method signature declares a single parameter of type Serializable.

@target

limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type

@target(com.example.aop.anno.annotation.AnCustomAnnotation):目标对象包含有AnCustomAnnotation注解。
自定义一个注解

package com.example.aop.anno.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface AnCustomAnnotation {

}

然后在接口上、方法上和实现类上面添加注解,只有在实现类上添加注解是可以的。因为目标对象是实现类对象,另外这个是作用于类级别的,而不是方法级别,因为无论是接口方法还是实现方法上添加相应注解并不会起作用。

  1. 作用于目标类接口,不能增强
    在这里插入图片描述
  2. 作用于目标类方法,不能增强
    在这里插入图片描述
  3. 作用于目标类,可以增强
    在这里插入图片描述

@within

limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP)

@within(com.example.aop.anno.annotation.AnCustomAnnotation):目标类上面包含有AnCustomAnnotation注解。

  1. 在目标类接口和方法上添加注解都不能匹配
    在这里插入图片描述
  2. 在目标类上添加相应注解,所有(公有)方法都可以匹配

在这里插入图片描述
3. 在目标类方法上添加相应注解,没有效果
在这里插入图片描述

无论是@target还是@within,都只作用于类上面,而不是方法,前者是在运行时检查目标对象是否包含有相应注解,而后者是在目标类上面。

@target@within区别:

修改注解的保留策略(如果不熟悉注解的可以参考博客:关于Annotation的那些事儿
在这里插入图片描述
修改完之后,查看@target@within的结果有啥不同:
在这里插入图片描述
而对于@target注解会报错:

Caused by: java.lang.IllegalArgumentException: error Annotation type com.example.aop.anno.annotation.AnCustomAnnotation does not have runtime retention
	at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:301)
	at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:217)
	at org.springframework.aop.aspectj.AspectJExpressionPointcut.checkReadyToMatch(AspectJExpressionPointcut.java:190)
	at org.springframework.aop.aspectj.AspectJExpressionPointcut.getClassFilter(AspectJExpressionPointcut.java:169)
	at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:220)
	at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:279)
	at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:311)
	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:119)
	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:89)
	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:70)
	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:343)
	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:295)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:421)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1623)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
	... 10 more

报这个错的原因就在于AnCustomAnnotation注解的保留策略为CLASS级别,而@target里面使用的注解必须是RUNTIME级别的。因为CLASS级别的注解并不会保留到运行时(RUNTIME)。我们再尝试降低注解的保留级别为SOURCE.此时within也不能匹配成功了(不会有异常)。
在这里插入图片描述
从以上的结果不难看出,
@target@within的区别在于:前者是在运行时查找目标对象的注解,而后者是在编译时查找目标类上面的注解。

@annotation

limits matching to join points where the subject of the join point (method being executed in Spring AOP) has the given annotation

执行方法上包含有特定注解,首先修改注解的保留策略为RUNTIME级别
在这里插入图片描述
@annotation(com.example.aop.anno.annotation.AnCustomAnnotation):执行方法上面包含有AnCustomAnnotation注解。

  1. 目标接口和目标类上添加注解都不能匹配
  2. 对应方法添加注解,可以匹配目标方法
    在这里插入图片描述
  3. 接口方法包含注解,不能匹配
    在这里插入图片描述

@args

limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given type(s)

@args(com.example.aop.anno.annotation.AnCustomAnnotation):方法类中包含一个参数,而且参数上面包含有注解
在这里插入图片描述
此时只有方法add才会匹配
在这里插入图片描述

bean

通过bean指定目标在Spring容器中的bean名称,并且支持统配符(*
bean(simpleOrderService):匹配名称为simpleOrderService的bean
在这里插入图片描述
bean(*Service):匹配所有bean名称为以Service结尾的bean
在这里插入图片描述

组合使用切点表达式

如下所示,Spring AOP支持通过连接符将多个切点表达式合并为一个复杂的切点表达式。支持的连接符有&&||!

@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}

@Pointcut("within(com.example.aop.anno.service..*)")
public void inServiceLayer() {}

@Pointcut("anyPublicOperation() && inServiceLayer()")// the pointcut expression
private void anPointCut() {}// the pointcut signature

以上案例中第一个切点anyPublicOperation用于匹配所有的公共方法,而inServiceLayer用于匹配所有service包下面的类(包括子包),通过&&连接符匹配同时满足以上两个条件的连接点。
在这里插入图片描述

共享公共的切点表达式

单独定义一个切面来保存公共的切点

package com.example.aop.anno.aspects;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SystemArchitecture {

    @Pointcut("execution(public * *(..))")
    public void anyPublicOperation() {}

    @Pointcut("within(com.example.aop.anno.service..*)")
    public void inServiceLayer() {}
}

按如下模式引用

@Pointcut("SystemArchitecture.anyPublicOperation() && SystemArchitecture.inServiceLayer()")
private void anPointCut() {}

在这里插入图片描述

xml方式配置

<aop:aspectj-autoproxy/>

<bean id="aBean" class="com.example.aop.anno.aspects.AnCustomAspect"/>

<bean id="orderService" class="com.example.aop.anno.service.impl.SimpleOrderService"/>

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        <aop:pointcut id="anPointCut"
                      expression="execution(* com.example.aop.anno.service.*.*(..))
                      and com.example.aop.anno.aspects.SystemArchitecture.anyPublicOperation()"/>
        <aop:before pointcut-ref="anPointCut" method="anBeforeAdvice"/>
    </aop:aspect>
</aop:config>

此时的切面类定义如下(只需保留对应的方法即可)

package com.example.aop.anno.aspects;

import org.aspectj.lang.JoinPoint;

public class AnCustomAspect {

    public void anBeforeAdvice(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args.length < 1){
            System.out.println("no args");
            return;
        }
        if (args.length == 1){
            System.out.println(args[0]);
            return;
        }
        StringBuilder builder = new StringBuilder();
        for (Object arg : args) {
            builder.append(arg).append(",");
        }
        builder.delete(builder.length()-1,builder.length());
        System.out.println(builder.toString());
    }
}

公共配置类

package com.example.aop.anno.aspects;

import org.aspectj.lang.annotation.Pointcut;

public class SystemArchitecture {

    @Pointcut("execution(public * *(..))")
    public void anyPublicOperation() {}

    @Pointcut("within(com.example.aop.anno.service..*)")
    public void inServiceLayer() {}

}

在这里插入图片描述

总结

上面我们对Spring AOP中的切点使用的几个方式进行了详细的探讨。

首先对于Spring AOP的能力与目标我们必须要有认识。Spring AOP是用纯Java实现的。 不需要特殊的编译过程。 Spring AOP不需要控制类加载器的层次结构,因此适合在Servlet容器或应用程序服务器中使用。

Spring AOP当前仅支持方法执行连接点(建议在Spring Bean上执行方法)。 尽管可以在不破坏核心Spring AOP API的情况下添加对字段拦截的支持,但并未实现字段拦截。 如果需要建议字段访问和更新连接点,请考虑使用诸如AspectJ之类的语言。

Spring AOP的AOP方法不同于大多数其他AOP框架。 目的不是提供最完整的AOP实现(尽管Spring AOP相当强大); 而是在AOP实现和Spring IoC之间提供紧密的集成,以帮助解决企业应用程序中的常见问题。

定义切点的目的就是为了匹配出需要进行增强的类、对象或者方法,而增强的手段就是通过代理,目前代理的手段主要有JDK动态代理和CGLIB代理,二者区别在于针对接口实现一个代理类(接口实现)还是针对目标类实现了代理类(继承实现)。
切点的定义以及各种标识符的使用并不是难点,其实难点在于对性能的影响分析。在上面我们尝试比较了@target@within注解的区别,一个(@within)是在编译期就决定了目标是否已经匹配,另一个是在运行期进行匹配,在编译期中决定的也被称为静态匹配(statically match),而运行时完成匹配的称为动态匹配(dynamically match)。

During compilation, AspectJ processes pointcuts in order to try and optimize matching performance. Examining code and determining if each join point matches (statically or dynamically) a given pointcut is a costly process. (A dynamic match means the match cannot be fully determined from static analysis and a test will be placed in the code to determine if there is an actual match when the code is running).

首次遇到切入点声明时,AspectJ将其重写为匹配过程的最佳形式。 这是什么意思? 基本上,切入点是用DNF(析取范式)重写的,并且对切入点的组件进行了排序,以便首先检查那些简单的组件。 这意味着您不必担心理解各种切入点指示符的性能,并且可以在切入点声明中以任何顺序提供它们。

但是,AspectJ只能使用所告知的内容,为了获得最佳的匹配性能,您应该考虑他们正在尝试实现的目标,并在定义中尽可能缩小匹配的搜索空间。 现有的匹配标识符自然分为以下三类之一:特定类型(kinded·),范围界定(scoping)和上下文(context),且性能依次降低:

  • 特定类型标识符(Kinded designators)是那些只选择特定类型连接点的切点标识符,比如: execution, get, set, call, handler
  • 范围节点标识符(Scoping designators)是那些选择某个(或多个)范围内感兴趣的目标进行匹配的切点标识符。比如: within(一般可以限定为某个包下面)
  • 上下文标识符(Contextual designators)是那些根据Spring容器上下文匹配或绑定的切点标识符。比如this, target, @annotation。

编写良好的切入点应尝试并至少包括前两种类型(Kinded designatorsScoping designators),而如果希望基于上下文进行匹配或将上下文绑定以用于增强时,则可以包括Contextual designators。 使用Kinded designatorsContextual designators都可以达到目标,但是由于所有额外的处理和分析,可能会影响编织性能(使用的时间和内存)。 Scoping designators的匹配非常快,它们的使用意味着AspectJ可以非常快地消除不应进一步处理的连接点组-这就是为什么一个好的切入点定义应始终包括一个切入点的原因。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值