目录

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 (分层)。典型如下:

    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 }
    View Code
  • 相关文章:

    • 2021-10-02
    • 2021-08-28
    • 2021-09-18
    • 2021-09-28
    • 2021-04-28
    • 2021-12-12
    • 2022-12-23
    猜你喜欢
    • 2022-03-08
    • 2021-05-21
    • 2021-09-14
    • 2021-12-05
    • 2021-07-19
    • 2021-06-09
    • 2021-10-08
    相关资源
    相似解决方案