Spring中AOP技术

AOP(Aspect Oriented Programing)面向切面编程

通过预编译方式或者运行时动态代理方式实现程序功能的统一维护,可以理解为扩展功能而不用修改源代码

Java程序的执行流

程序运行的过程就是方法调用的过程。我们按照方法执行的顺序,将方法调用排成一串,这样就构成了Java程序流。

将上述的线程栈里的方法调用按照执行流排列,会有如下类似的图

基于时间序列,我们可以将方法调用排成一条线。而每个方法调用则可以看成Java执行流中的一个节点。这个节点在AOP的术语中,被称为Join Point,即连接点。一个Java程序的运行的过程,就是若干个连接点连接起来依次执行的过程。

通常面向对象的程序,代码都是按照时间序列纵向展开的,而他们都有一个共性:即都是以方法调用作为基本执行单位展开的。将方法调用当做一个连接点,那么由连接点串起来的程序执行流就是整个程序的执行过程。

AOP(Aspect Oriented Programming)则是从另外一个角度来考虑整个程序的,AOP将每一个方法调用,即连接点作为编程的入口,针对方法调用进行编程。从执行的逻辑上来看,相当于在之前纵向的按照时间轴执行的程序横向切入。相当于将之前的程序横向切割成若干的面,即Aspect.每个面被称为切面。

所以,根据我的理解,AOP本质上是针对方法调用的编程思路。

AOP是针对切面进行的编程的,那么,你需要选择哪些切面(即 连接点Joint Point)作为你的编程对象呢?

因为切面本质上是每一个方法调用,选择切面的过程实际上就是选择方法的过程。那么,被选择的切面(Aspect)在AOP术语里被称为切入点(Point Cut). 切入点实际上也是从所有的连接点(Join point)挑选自己感兴趣的连接点的过程。

第三方

既然AOP是针对方法调用(连接点)的编程, 现在又选取了你自己感兴趣的链接点---切入点(Point Cut)了,那么,AOP能对它做什么类型的编程呢?AOP能做什么呢?

了解这个之前,我们先要知道一个非常重要的问题:既然AOP是对方法调用进行的编程,那么,AOP如何捕获方法调用的呢?弄清楚这个问题,下面我们先来了解一下引入了代理模式的Java程序执行流是什么样子的。

使用代理模式的Java程序执行流

我们假设在我们的Java代码里,都为实例对象通过代理模式创建了代理对象,访问这些实例对象必须要通过代理,那么,加入了proxy对象的Java程序执行流会变得稍微复杂起来。

我们来看下加入了proxy对象后,Java程序执行流的示意图:

由上图可以看出,只要想调用某一个实例对象的方法时,都会经过这个实例对象相对应的代理对象, 即执行的控制权先交给代理对象。

代理模式属于Java代码中经常用到的、也是比较重要的设计模式。代理模式可以为某些对象除了实现本身的功能外,提供一些额外的功能,大致作用如下图所示:

加入了代理模式的Java程序执行流,使得所有的方法调用都经过了代理对象。对于Spring AOP框架而言,它负责控制着整个容器内部的代理对象。当我们调用了某一个实例对象的任何一个非final的public方法时,整个Spring框架都会知晓。

此时的SpringAOP框架在某种程度上扮演着一个上帝的角色:它知道你在这个框架内所做的任何操作,你对每一个实例对象的非final的public方法调用都可以被框架察觉到!

既然Spring代理层可以察觉到你所做的每一次对实例对象的方法调用,那么,Spring就有机会在这个代理的过程中插入Spring的自己的业务代码

Spring AOP的工作原理

已经介绍了AOP编程首先要选择它感兴趣的连接点----即切入点(Point cut),那么,AOP能对切入点做什么样的编程呢?我们先将代理模式下的某个连接点细化,你会看到如下这个示意图所表示的过程:

为了降低我们对Spring的AOP的理解难度,我在这里将代理角色的职能进行了简化,方便大家理解。**(注意:真实的Spring AOP的proxy角色扮演的只能比这复杂的多,这里只是简化,方便大家理解,请不要先入为主)

代理模式的代理角色最起码要考虑三个阶段:

1.在****调用真正对象的方法之前,应该需要做什么?

在调****用真正对象的方法过程中,如果抛出了异常,需要做什么?

3.在调用真正对象的方法后,返回了结果了,需要做什么?

Spring AOP 根据proxy提供的类型名和方法签名,确定了在其感兴趣的切入点内,则返回AfterReturingAdivce处理建议,proxy得到这个处理建议,然后执行建议;

上述的示意图中已经明确表明了Spring AOP应该做什么样的工作:根据proxy提供的特定类的特定方法执行的特定时期阶段给出相应的处理建议。要完成该工作,Spring AOP应该实现:

1.确定自己对什么类的什么方法感兴趣?-----即确定 AOP的切入点(Point Cut),这个可以通过切入点(Point Cut)表达式来完成;

2. 对应的的类的方法的执行特定时期给出什么处理建议?------这个需要Spring AOP提供相应的建议 ,即我们常说的Advice。

AOP相关术语

连接点(joinpoint):连接点即指那些可以被拦截点,在spring,指的是方法,spring中支持对方法类型的连接点进行增强,这些方法称之为连接点。

切入点(pointcut):切入点指要对那些连接点进行拦截的定义,在类中有很对的方法可以被增强,实际被增强的方法称之为切入点

增强/通知(Advice):增强值的是拦截到的连接点之后要做的事情就是增强。

增强有5种类型:前置增强、后置增强、异常增强、最终增强、环绕增强

前置增强:在连接点前面执行,前置增强不影响连接点的执行

后置增强:在连接点正常执行完成后执行

异常增强:在连接点抛出异常后执行

最终增强:在连接点执行之后,无论连接点执行成功失败都会执行

环绕增强:环绕增强在连接点前后

切面(Aspect):是切入点和增强的结合,把增强应用到切入点的过程

  • Introduction(引介):引介是一种特殊的通知在不修改代码的前提下,Introduction可以在运行期为类动态的添加一些方法或Field
  • Traget(目标对象):代理的目标对象(要增强的类)
  • Weaving(织入):是吧增强应用到目标的过程,把advice应用到target的过程
  • Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类

AOP的使用

spring中整和AspectJ框架
AspectJ本身是一个单独的AOP框架,基于Java实现的,不是Spring的一部分,和SPring一块实现AOP操作


AOP的实现有两种形式:
基于XML配置形式实现
基于注解形式实现

引入核心依赖


除了AOP依赖,还需要将基础核心依赖引入 

在pom.xml中引入

 <!--Spring的AOP jar包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>4.3.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.7.4</version>
        </dependency>
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>


模拟租房业务
给定一个房东类:

/**
 * 房东
 */
public class Landlord {
    public void service() {
        System.out.println("签合同");
        System.out.println("收钱");
    }
}


创建中介类

/**
 * 中介
 */
public class Broker {

    public void  service1(){
        System.out.println("看房子");
    }

    public void  service2(){
        System.out.println("谈价钱");
    }

    public void  service3(){
        System.out.println("给钥匙");
    }

}


基于XML配置形式实现AOP

在XML引入AOP的约束(在资源文件夹下面新建一个配置文件,将下面代码放进去)

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/aop
   http://www.springframework.org/schema/aop/spring-aop.xsd">


</beans>


execution函数介绍


在通知中通过value属性定义切点,通过execution函数,可以定义切点的方法切入
1、切入点:实际增强的方法
2、常用的表达式
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
(1)execution(* com.tulun.bean.Book.show(..)) 表类里面的某一个方法
(2)execution(* com.tulun.bean.Book.*(..)) 表类某个包里类所有方法
(3)execution(* *.*(..)) 表示所有

例:
-匹配所有类public方法 execution(public *.*(..))
-匹配指定包下所有类方法 execution(* com.tulun.bean.*(..)) (不包含子包)
- execution(* com.tulun.bean..*(..)) (包含包、子包下所有类)
-匹配指定类所有方法 execution(* com.tulun.bean.Book.*(..))
-匹配实现特定接口所有类方法 execution(* com.tulun.bean.Book+.*(..))
-匹配所有com开头的方法 execution(* com*(..))

 

xml配置:

   <!--将类交给容器管理-->
    <bean id="landlord" class="com.tulun.Spring.AOP.Landlord"/>
    <bean id="broker" class="com.tulun.Spring.AOP.Broker"/>

    <!--配置AOP操作-->
    <aop:config >
        <!--
        aop:pointcut标签
        配置切入点
        对于要进行增强的连接点称之为切入点
        id属性:取名称
        expression:切入点表达式
        execution表达式
        -->
        <aop:pointcut id="pointcut1" expression="execution(* com.tulun.Spring.AOP.Landlord.service(..))"/>

        <!--
        aop:aspect标签
        配置切面
        把增强应用到切入点的过程
        ref属性:指定增强
         id属性:取名称
         order属性:给多个增强排序
        -->
        <aop:aspect  ref="broker">
            <!--配置增强类型:前置增强-->
            <aop:before method="service1" pointcut-ref="pointcut1"/>
        </aop:aspect>

    </aop:config>


增强类型:

aop:around

aop:before

aop:after-throwing

aop:after

aop:after-returning

image.png



测试使用:

        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("springcontext3.xml");
        Landlord landlord = (Landlord)applicationContext.getBean("landlord");
        landlord.service();

执行结果:

看房子

签合同

收钱

image.png




各种类型增强如下:

<!--配置增强类型:前置增强(aop:before)-->
            <aop:before method="service1" pointcut-ref="pointcut1"/>
            <!--后置增强(aop:after )-->
            <aop:after method="service2" pointcut-ref="pointcut1"/>
            <!--最终增强(aop:after-returning )-->
            <aop:after-returning method="service3" pointcut-ref="pointcut1"/>
            <!--异常增强(aop:after-throwing)-->
            <aop:after-throwing method="service4" pointcut-ref="pointcut1"/>

            <!--环绕增强-->
            <aop:around method="service5" pointcut-ref="pointcut1"/>

image.png



基于注解形式实现AOP操作


在xml配置文件中引入AOP操作

<?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:aop="http://www.springframework.org/schema/aop"
       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-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启注解扫描-->
    <context:component-scan base-package="com.tulun.Spring.AOP"/>

    <!--开启AOP操作-->
    <aop:aspectj-autoproxy/>
</beans>


在增强类上添加注解

/**
 * 中介
 */
@Component
@Aspect
//当前类开启AOP操作
public class Broker {

    //前置增强注解,@Before
    @Before(value = "execution(* com.tulun.Spring.AOP.Landlord.service(..))")
    public void  service1(){
        System.out.println("看房子");
    }
}

@Aspect 注解添加在类上,表示当前类是增强类
@Before注解添加在方法上,表示前置增强
@Before(value = "execution(* com.tulun.Spring.AOP.Landlord.service(..))")


关于增强注解:

//前置增强注解,@Before
    @Before(value = "execution(* com.tulun.Spring.AOP.Landlord.service(..))")
    //后置增强
    @After(value = "execution(* com.tulun.Spring.AOP.Landlord.service(..))")
//最终增强
    @AfterReturning(value = "execution(* com.tulun.Spring.AOP.Landlord.service(..))") 
    @AfterThrowing(value = "execution(* com.tulun.Spring.AOP.Landlord.service(..))") //异常增强
    @Around(value = "execution(* com.tulun.Spring.AOP.Landlord.service(..))")//环绕增强

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值