• spring——Spring AOP(转载)


    Spring AOP 是 Spring 框架的核心模块之一,它使用纯 Java 实现,因此不需要专门的编译过程和类加载器,可以在程序运行期通过代理方式向目标类织入增强代码。

    Spring AOP 的代理机制

    Spring 在运行期会为目标对象生成一个动态代理对象,并在代理对象中实现对目标对象的增强。

    Spring AOP 的底层是通过以下 2 种动态代理机制,为目标对象(Target Bean)执行横向织入的。

    代理技术描述
    JDK 动态代理 Spring AOP 默认的动态代理方式,若目标对象实现了若干接口,Spring 使用 JDK 的 java.lang.reflect.Proxy 类进行代理。
    CGLIB 动态代理 若目标对象没有实现任何接口,Spring 则使用 CGLIB 库生成目标对象的子类,以实现对目标对象的代理。

    注意:由于被标记为 final 的方法是无法进行覆盖的,因此这类方法不管是通过 JDK 动态代理机制还是 CGLIB 动态代理机制都是无法完成代理的。

    Spring AOP 连接点

    Spring AOP 并没有像其他 AOP 框架(例如 AspectJ)一样提供了完成的 AOP 功能,它是 Spring 提供的一种简化版的 AOP 组件。其中最明显的简化就是,Spring AOP 只支持一种连接点类型:方法调用。您可能会认为这是一个严重的限制,但实际上 Spring AOP 这样设计的原因是为了让  Spring 更易于访问。

    方法调用连接点是迄今为止最有用的连接点,通过它可以实现日常编程中绝大多数与 AOP 相关的有用的功能。如果需要使用其他类型的连接点(例如成员变量连接点),我们可以将 Spring AOP 与其他的 AOP 实现一起使用,最常见的组合就是 Spring AOP + ApectJ。 

    Spring AOP 通知类型

    AOP 联盟为通知(Advice)定义了一个 org.aopalliance.aop.Interface.Advice 接口。

    Spring AOP 按照通知(Advice)织入到目标类方法的连接点位置,为 Advice 接口提供了 6 个子接口,如下表。

    通知类型接口描述
    前置通知  org.springframework.aop.MethodBeforeAdvice 在目标方法执行前实施增强。
    后置通知 org.springframework.aop.AfterReturningAdvice 在目标方法执行后实施增强。
    后置返回通知 org.springframework.aop.AfterReturningAdvice 在目标方法执行完成,并返回一个返回值后实施增强。
    环绕通知 org.aopalliance.intercept.MethodInterceptor 在目标方法执行前后实施增强。
    异常通知 org.springframework.aop.ThrowsAdvice 在方法抛出异常后实施增强。
    引入通知 org.springframework.aop.IntroductionInterceptor 在目标类中添加一些新的方法和属性。

    Spring AOP 切面类型

    Spring 使用 org.springframework.aop.Advisor 接口表示切面的概念,实现对通知(Adivce)和连接点(Joinpoint)的管理。

    在 Spring AOP 中,切面可以分为三类:一般切面、切点切面和引介切面。

    切面类型接口描述
    一般切面 org.springframework.aop.Advisor Spring AOP 默认的切面类型。

    由于 Advisor 接口仅包含一个 Advice(通知)类型的属性,而没有定义 PointCut(切入点),因此它表示一个不带切点的简单切面。

    这样的切面会对目标对象(Target)中的所有方法进行拦截并织入增强代码。由于这个切面太过宽泛,因此我们一般不会直接使用。
    切点切面 org.springframework.aop.PointcutAdvisor Advisor 的子接口,用来表示带切点的切面,该接口在 Advisor 的基础上还维护了一个 PointCut(切点)类型的属性。

    使用它,我们可以通过包名、类名、方法名等信息更加灵活的定义切面中的切入点,提供更具有适用性的切面。
    引介切面 org.springframework.aop.IntroductionAdvisor Advisor 的子接口,用来代表引介切面,引介切面是对应引介增强的特殊的切面,它应用于类层面上,所以引介切面适用 ClassFilter 进行定义。

    本节我们着重对 Advisor 和 PointcutAdvisor 进行讲解,至于 IntroductionAdvisor,我们会在后面的教程中进行介绍。

    一般切面的 AOP 开发

    当我们在使用 Spring AOP 开发时,若没有对切面进行具体定义,Spring AOP 会通过 Advisor 为我们定义一个一般切面(不带切点的切面),然后对目标对象(Target)中的所有方法连接点进行拦截,并织入增强代码。

    示例 1

    下面我们就通过一个简单的实例演示下一般切面的 AOP 开发流程。

    1. 创建一个名为 my-spring-aop-demo 的 Java 工程,并将以下依赖引入到工程中。

    • org.springframework.core-5.3.13.jar
    • org.springframework.beans-5.3.13.jar
    • spring-context-5.3.13.jar
    • spring-expression-5.3.13.jar
    • commons.logging-1.2.jar
    • spring-aop-5.3.13.jar

    Spring 能够基于 org.springframework.aop.framework.ProxyFactoryBean 类,根据目标对象的类型(是否实现了接口)自动选择使用 JDK 动态代理或 CGLIB 动态代理机制,为目标对象(Target Bean)生成对应的代理对象(Proxy Bean)。

    ProxyFactoryBean 的常用属性如下表所示。

    属性描述
    target  需要代理的目标对象(Bean)
    proxyInterfaces 代理需要实现的接口,如果需要实现多个接口,可以通过 <list> 元素进行赋值。
    proxyTargetClass 针对类的代理,该属性默认取值为 false(可省略), 表示使用 JDK 动态代理;取值为 true,表示使用 CGlib 动态代理
    interceptorNames 拦截器的名字,该属性的取值既可以是拦截器、也可以是 Advice(通知)类型的 Bean,还可以是切面(Advisor)的 Bean。
    singleton 返回的代理对象是否为单例模式,默认值为 true。
    optimize  是否对创建的代理进行优化(只适用于CGLIB)。


    6. 在 net.biancheng.c 包下,创建一个名为 MainApp 的类,代码如下。

    基于 PointcutAdvisor 的 AOP 开发

    PointCutAdvisor 是 Adivsor 接口的子接口,用来表示带切点的切面。使用它,我们可以通过包名、类名、方法名等信息更加灵活的定义切面中的切入点,提供更具有适用性的切面。

    Spring 提供了多个 PointCutAdvisor 的实现,其中常用实现类如如下。

    • NameMatchMethodPointcutAdvisor:指定 Advice 所要应用到的目标方法名称,例如 hello* 代表所有以 hello 开头的所有方法。
    • RegExpMethodPointcutAdvisor:使用正则表达式来定义切点(PointCut),RegExpMethodPointcutAdvisor 包含一个 pattern 属性,该属性使用正则表达式描述需要拦截的方法。

    示例 2

    下面我们就通过一个简单的实例,演示下切点切面的 AOP 开发。

    1. 在 my-spring-aop-demo 的 net.biacheng.c.dao 包下,创建一个名为 OrderDao,代码如下。

     

     

     

    自动代理

    在前面的案例中,所有目标对象(Target Bean)的代理对象(Proxy Bean)都是在 XML 配置中通过 ProxyFactoryBean 创建的。但在实际开发中,一个项目中往往包含非常多的 Bean, 如果每个 Bean 都通过 ProxyFactoryBean 创建,那么开发和维护成本会十分巨大。为了解决这个问题,Spring 为我们提供了自动代理机制。

    Spring 提供的自动代理方案,都是基于后处理 Bean 实现的,即在 Bean 创建的过程中完成增强,并将目标对象替换为自动生成的代理对象。通过 Spring 的自动代理,我们在程序中直接拿到的 Bean 就已经是 Spring 自动生成的代理对象了。

    Spring 为我们提供了 3 种自动代理方案:

    • BeanNameAutoProxyCreator:根据 Bean 名称创建代理对象。
    • DefaultAdvisorAutoProxyCreator:根据 Advisor 本身包含信息创建代理对象。
    • AnnotationAwareAspectJAutoProxyCreator:基于 Bean 中的 AspectJ 注解进行自动代理对象。


    本节我们就通过两个简单的实例,对 BeanNameAutoProxyCreator 和 DefaultAdvisorAutoProxyCreator 进行演示,至于 AnnotationAwareAspectJAutoProxyCreator,我们会在后续的教程中进行讲解。

    根据 Bean 名称创建代理对象

    1. 在 my-spring-aop-demo 工程的 src 目录下,创建一个名为 Beans2.xml 的配置文件,配置内容如下。

     

    3. 执行 MainApp 中的 main() 方法,控制台输出如下。

    正在执行前置增强操作…………
    环绕增强前********
    正在执行 UserDao 的 add() 方法……
    环绕增强后********
    正在执行前置增强操作…………
    环绕增强前********
    正在执行 UserDao 的 delete() 方法……
    环绕增强后********
    正在执行前置增强操作…………
    环绕增强前********
    正在执行 UserDao 的 modify() 方法……
    环绕增强后********
    正在执行前置增强操作…………
    环绕增强前********
    正在执行 UserDao 的 get() 方法……
    环绕增强后********
    正在执行前置增强操作…………
    环绕增强前********
    正在执行 OrderDao 的 add() 方法……
    环绕增强后********
    正在执行前置增强操作…………
    环绕增强前********
    正在执行 OrderDao 的 adds() 方法……
    环绕增强后********
    正在执行前置增强操作…………
    环绕增强前********
    正在执行 OrderDao 的 delete() 方法……
    环绕增强后********
    正在执行前置增强操作…………
    环绕增强前********
    正在执行 OrderDao 的 get() 方法……
    环绕增强后********
    正在执行前置增强操作…………
    环绕增强前********
    正在执行 OrderDao 的 modify() 方法……
    环绕增强后********
    

      

    根据切面中信息创建代理对象

    1. 修改 Beans2.xml 中的配置,配置内容如下。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 定义目标(target)对象 -->
        <bean id="userDao" class="net.biancheng.c.dao.impl.UserDaoImpl"></bean>
        <bean id="orderDao" class="net.biancheng.c.dao.OrderDao"></bean>
    
        <!-- 定义增强 -->
        <bean id="beforeAdvice" class="net.biancheng.c.advice.UserDaoBeforeAdvice"></bean>
        <bean id="aroundAdvice" class="net.biancheng.c.advice.OrderDaoAroundAdvice"></bean>
    
        <!--定义切面-->
        <bean id="myPointCutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <!--定义表达式,规定哪些方法进行拦截 .* 表示所有方法-->
            <!--<property name="pattern" value=".*"></property>-->
            <property name="patterns"
                      value="net.biancheng.c.dao.OrderDao.add.*,net.biancheng.c.dao.OrderDao.delete.*"></property>
            <property name="advice" ref="aroundAdvice"></property>
        </bean>
    
        <!--Spring 自动代理:根据切面 myPointCutAdvisor 中信息创建代理对象-->
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
    
    </beans>
    

      

  • 相关阅读:
    jQuery实现图片前进后退
    jQuery写日历
    python列表模拟栈
    python 列表去重
    Linux的文件系统
    新建vss数据库
    关于业务用例和系统用例
    从零开始使用Linux命令
    svn的安装与配置
    数塔 动态规划
  • 原文地址:https://www.cnblogs.com/xiaobaibailongma/p/16182124.html
Copyright © 2020-2023  润新知