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(切面):是切入点和通知(引介)的结合web
看不懂吧<( ̄︶ ̄)>,由于我以前也没看懂,没事,咱们写几个例子看看就能懂了。若是你没学过还能看懂,那我也只能膜拜大佬了
概念说完,下面开始进入正式环节
第一步 添加依赖
想使用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")
}
}spring
@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>express
<!--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")app
val user = context.getBean("user") as User
user.add()
}
}
webapp
结果能够看到,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("前置通知")
}oop
//后置通知须要传入一个参数,这个参数就是须要加强方法的返回值,没有能够不写
fun afterResult(result: Any)
{
println("后置通知 "+result)
}测试
//最终通知不管该方法有没有出异常有没有返回值,最终都会被执行
fun after()
{
println("最终通知")
}.net
/* 环绕通知须要一个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>
1
2
而后在各方法上写上注解,别忘了类上面的注解
@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的注解扫描
结果天然毫无疑问