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

5 篇文章 0 订阅
4 篇文章 0 订阅

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吧,而且我们还需要在类中加点方法
@Bean
data class User(var name: String, var age: Int)
{
    fun add()
    {
        println("user add")
    }
}

@Bean
data 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方法中了
下面我就直接演示下其他几种的用法了
@Bean
data class User(var name: String, var age: Int)
{
    fun add(): String
    {
        println("user add")
        return "你好"
    }
}

@Bean
data 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的注解扫描

这里写图片描述

结果自然毫无疑问
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值