Kotlin的Spring之旅(二):AOP(面向切面编程)

来源:互联网 发布:大二申请国外大学知乎 编辑:程序博客网 时间:2024/06/11 15:55

AOP(面向切面编程)

AOP是OOP(面向对象编程)的延续,但是它和面向对象的纵向编程不同,它是一个横向的切面式的编程。可以理解为oop就是一根柱子,如果需要就继续往上加长,而aop则是在需要的地方把柱子切开,在中间加上一层,再把柱子完美的粘合起来。

用物理上的话来说,aop就是给这个编程世界加上了一个维度,二维到三维的差别。很明显aop要灵活得多

AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

AOP核心概念

  • Joinpoint(连接点):指那些被拦截到的点(spring中这些点指的是方法)
  • Pointcut(切入点):指我们要对哪些joinpoint进行拦截的定义
  • Advice(通知/增强):拦截到joinpoint之后多要做的事情就是通知(通知分为前置通知,后置通知,异常通知,最终通知,环绕通知)
  • Introduction(引介):引介是一种特殊的通知。在不改变代码的前提下,introduction可以在运行期间为类动态日案件一些方法或Field
  • Target(目标对象):代理的目标对象(需要增强的类)
  • Weaving(织入):把增强应用到目标的过程(advice应用到target的过程)
  • Proxy(代理):一个类被AOP织入增强后,就会产生一个结果代理类
  • Aspect(切面):是切入点和通知(引介)的结合

    看不懂吧<( ̄︶ ̄)>,因为我之前也没看懂,没事,我们写几个例子看看就能懂了。如果你没学过还能看懂,那我也只能膜拜大佬了

概念说完,下面开始进入正式环节

第一步 添加依赖

想使用AOP光凭之前的那些还是不够的,所以我们还需要添加一些依赖

compile "org.springframework:spring-aspects:4.3.9.RELEASE"compile "org.springframework:spring-aop:4.3.9.RELEASE"compile "aspectj:aspectjweaver:1.5.4"compile "aopalliance:aopalliance:1.0"

这里面的aspectj可以看到,并不是spring的一部分,但是自从spring2之后,官方就添加了对aspectj的支持,而且现在spring官方是推荐使用aspectj来开发aop,我们自然要跟随官方的大旗走了

aspectj实现aop有两种方式,而且还是老两种

  • xml配置
  • 注解实现

我们来看这两种,和之前一样,我们常用的肯定还是能少写代码的注解方式,xml配置的方式看看就可以了,但是至少也要能看懂,不然别人写了你看不懂就尴尬了

1. xml配置

首先在我们的xml文件中加入约束

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

可以看到我们又加上了aop的约束

我们还是继续用我们的User类不过这次一个类不够用,我们需要再来一个类就叫Advice吧,而且我们还需要在类中加点方法

@Beandata class User(var name: String, var age: Int){    fun add()    {        println("user add")    }}@Beandata class Advice(var content: String){    fun before()    {        println("前置通知")    }}

然后就是配置xml了,我们通过xml来配置切入点(pointcut),这里我们需要用到一个函数

execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)

来点例子看一下:

  • 匹配所有public的方法:execution(public * *(..))
  • 匹配包下所有类的方法:execution(* com.kotlin.*(..)) (一个点表示不包含子包)
  • execution(* com.kotlin.. *(..)) (两个点表示包含子包)
  • 匹配实现特定接口的所有类的方法:execution(* com.kotlin.xxinterface+.*(..))
  • 匹配所有add开头的方法:execution(* add*(..))
  • 匹配所有方法:execution(* *.*(..))

这样大概清楚了吧,下面我们我们来写一个前置通知增强User中的add方法

   <!--1.配置对象-->    <bean id="user" class="com.kotlin.Bean.User"></bean>    <bean id="advice" class="com.kotlin.Bean.Advice"></bean>    <!--2.配置aop操作-->    <aop:config>        <!--2.1配置切入点 因为User中只有一个方法,就直接增强User中的所有方法了-->        <aop:pointcut id="pointcut" expression="execution(* com.kotlin.Bean.User.*(..))"/>        <!--2.2配置切面 将增强用到方法上-->        <aop:aspect ref="advice">            <!--选择用来增强的方法和需要增强的切入点-->            <aop:before method="before" pointcut-ref="pointcut"/>        </aop:aspect>    </aop:config>

然后来到我们的测试类,运行下看看

class main{    @Test    fun test()    {        //加载Spring配置文件,创建对象        val context = FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml")        val user = context.getBean("user") as User        user.add()    }}

这里写图片描述

结果可以看到,before方法已经添加到add方法中了

下面我就直接演示下其他几种的用法了

@Beandata class User(var name: String, var age: Int){    fun add(): String    {        println("user add")        return "你好"    }}@Beandata class Advice(var content: String){    fun before()    {        println("前置通知")    }    //后置通知需要传入一个参数,这个参数就是需要增强方法的返回值,没有可以不写    fun afterResult(result: Any)    {        println("后置通知  "+result)    }    //最终通知无论该方法有没有出异常有没有返回值,最终都会被执行    fun after()    {        println("最终通知")    }    /* 环绕通知需要一个ProceedingJoinPoint参数,这相当于需要增强的函数的方法体,需要的调用它的proceed方法执行,如果该函数有返回值,那么环绕通知也需要返回一个proceed方法的返回值 */    fun around(pro: ProceedingJoinPoint): Any    {        //方法之前        println("环绕通知 方法之前")        //被增强的方法        val any = pro.proceed()        //方法之后        println("环绕通知 方法之后")        return any    }    //异常通知需要一个异常参数,当出现异常时该方法将会被调用    fun exception(ex : Exception)    {        println("异常通知 "+ex)    }}
<!--1.配置对象-->    <bean id="user" class="com.kotlin.Bean.User"></bean>    <bean id="advice" class="com.kotlin.Bean.Advice"></bean>    <!--2.配置aop操作-->    <aop:config>        <!--2.1配置切入点-->        <aop:pointcut id="pointcut" expression="execution(* com.kotlin.Bean.User.*(..))"/>        <!--2.2配置切面 将增强用到方法上-->        <aop:aspect ref="advice">            <!--选择用来增强的方法和需要增强的切入点-->            <aop:before method="before" pointcut-ref="pointcut"/>            <!--后置通知需要配置它的参数-->            <aop:after-returning method="afterResult" pointcut-ref="pointcut" returning="result"/>            <aop:after method="after" pointcut-ref="pointcut" />            <aop:around method="around" pointcut-ref="pointcut" />            <!--异常通知也要配置它的异常参数-->            <aop:after-throwing method="exception" pointcut-ref="pointcut" throwing="ex"/>        </aop:aspect>    </aop:config>

然后我们来看下结果

这里写图片描述

接着,我们手动给他制造一个异常,就用4/0吧

这里写图片描述

可以看到,后置通知和后环绕通知没有了,取而代之的是除零异常,这时异常通知出现了,这也说明了后置通知只有在没有异常时候才会执行,异常通知只会在有异常时候执行

这也得出了这样的结论

try{    //  前置通知    //  环绕通知(前)    //  目标方法    //  环绕通知(后)    //  后置通知(也有人称为返回通知)}catche(Exception e){    //  异常通知}finally{    //  最终通知}

2.注解配置

注解配置很简单,直接把内容写在方法的头上就可以了,我把代码给出,大家一看就知道了

首先在xml中开启自动扫描

<!--开启aop自动扫描代理-->    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

然后在各方法上写上注解,别忘了类上面的注解

@Bean@Component(value = "user")data class User(var name: String, var age: Int){    fun add(): String    {        println("user add")//        var s = 4 / 0        return "你好"    }}@Aspect@Bean@Component(value = "advice")data class Advice(var content: String){    @Before(value = "execution(* com.kotlin.Bean.User.*(..))")    fun before()    {        println("前置通知")    }    @AfterReturning(value = "execution(* com.kotlin.Bean.User.*(..))", returning = "result")    fun afterResult(result: Any)    {        println("后置通知  " + result)    }    @After(value = "execution(* com.kotlin.Bean.User.*(..))")    fun after()    {        println("最终通知")    }    @Around(value = "execution(* com.kotlin.Bean.User.*(..))")    fun around(pro: ProceedingJoinPoint): Any    {        //方法之前        println("环绕通知 方法之前")        //被增强的方法        val any = pro.proceed()        //方法之后        println("环绕通知 方法之后")        return any    }    @AfterThrowing(value = "execution(* com.kotlin.Bean.User.*(..))", throwing = "ex")    fun exception(ex: Exception)    {        println("异常通知 " + ex)    }}

别忘了开启IOC的注解扫描

这里写图片描述

结果自然毫无疑问

原创粉丝点击