详解Spring框架AOP(面向切面编程)

最近在学习AOP,以前一直很不明白,什么是AOP?为何要使用AOP,它有什么做用?学完以后有一点小小的感触和本身的理解,因此在这里呢就跟你们一块儿分享一下html

AOP(Aspect-Oriented Programming)实际上是OOP(Object-Oriented Programing) 思想的补充和完善。咱们知道,OOP引进"抽象"、"封装"、"继承"、"多态"等概念,对万事万物进行抽象和封装,来创建一种对象的层次结构,它强调了java

一种完整事物的自上而下的关系。可是具体细粒度到每一个事物内部的状况,OOP就显得无能为力了。好比日志功能。日志代码每每水平地散布在全部对象层次当
中,却与它所散布到的对象的核心功能毫无关系。对于其余不少相似功能,如事务管理、权限控制等也是如此。这致使了大量代码的重复,而不利于各个模块的重
用。   而AOP技
术则偏偏相反,它利用一种称为"横切"的技术,可以剖解开封装的对象内部,并将那些影响了多个类而且与具体业务无关的公共行为 封装成一个独立的模块(称
为切面)。更重要的是,它又能以巧夺天功的妙手将这些剖开的切面复原,不留痕迹的融入核心业务逻辑中。这样,对于往后横切功能的编辑和重用都可以带来极大
的方便。    AOP技术的具体实现,无非也就是经过动态代理技术或者是在程序编译期间进行静态的"织入"方式。下面是这方面技术的几个基本术语:    算法

一、join point(链接点):是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不须要去定义一个join point。    spring

二、point cut(切入点):本质上是一个捕获链接点的结构。在AOP中,能够定义一个point cut,来捕获相关方法的调用。    app

三、advice(通知):是point cut的执行代码,是执行“方面”的具体逻辑。 学习

四、aspect(切面):point cut和advice结合起来就是aspect,它相似于OOP中定义的一个类,但它表明的更可能是对象间横向的关系。   测试

 

说了这么多,可能咱们仍是对AOP有点不知所措,不知道是干什么的,那么咱们就以一个例子做为讲解,来理解这个抽象的概念spa

咱们有一个简易的计算器,进行加减乘除的操做,有一个需求,1.须要在进行算法以前和以后进行输出一句话代理

那么对于以上操做咱们可能最容易想到的就是用一个实现类实现这个接口。而后在接口调用方法先后输出一句话日志

这样确实能实现这个需求,可是可能有的同窗就想到了,是否是重复代码了呢?若是我有上千个方法呢?是否是还得在每一个方法里增长几行代码?咱们能不能在不改变原来方法的结构上

也能实现相同的需求呢?这个时候AOP就能帮咱们实现了。下面咱们详细的讲解下如何使用注解的方式来实现AOP

仍是一样的接口和实现类,只是这时候实现类中没有了输出语句,如图

上图就是最原始的方法了,也就是说咱们在这个方法里面只须要关注咱们方法执行的内容,并不须要关注一些方法以外的东西,好比说记录日志,方法前输出语句等等。。

那么,既然这个方法什么都不关注的话,那咱们的输出语句又在哪儿写呢?这个时候咱们就定义一个专门的类,用它来做为切面,代码以下所示

复制代码
package advice;

import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;


@Aspect    //声明注解
public class CalculationAnnotation {
    
    /**
     * 定义前置通知
     * execution(* biz.UserBiz.*(..)) 表示  全部修饰符的全部返回值类型  biz.UserBiz 包下的全部方法
     * 在方法执行以前执行
     * */
    @Before("execution(* biz.CalculationImpl.*(..))")
    public void before(JoinPoint join){
        //获取方法名
        String mathName=join.getSignature().getName();
        //获取参数列表
        List<Object> args = Arrays.asList(join.getArgs());
        
        System.out.println("前置通知---->before   方法名是:"+mathName+"\t参数列表是:"+args);
    }
    
    /**
     * 后置通知
     * 在方法返回后执行,不管是否发生异常
     * 不能访问到返回值
     * 
     * */
    @After("execution(* biz.CalculationImpl.*(..))")
    public void after(){
        System.out.println("后置通知---->after....");
    }
}
复制代码

 

@Aspect ----->表示声明这个类是一个切面,

这样呢,我们这个切面就声明完毕了,那么,咱们能够想到,这个时候咱们只是声明了一个切面而已,并无在那个地方用到这个切面对不对?也就是说咱们配置的切面还跟咱们程序尚未任何的关联关系

这样的话呢,就引出了咱们的配置文件了也就是咱们Spring的配置文件applicationContext.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: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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        ">
        
     
     
      <!-- 配置Bean -->
     <bean id ="CalculationImpl" class="biz.CalculationImpl"></bean>
     
     
     <!-- 将切面类交与Spring容器管理 -->
     <bean class="advice.CalculationAnnotation"></bean>


      <!-- 使用注解自动生成代理对象 -->
      <aop:aspectj-autoproxy/>
       
       
</beans>  
复制代码

 

这个时候咱们能够看看配置文件里到底写了什么,写这些是干啥的,有什么用。

这个你们确定都懂是吧,这没话说,也就是将CalculationImpl类交给Spring容器来管理,若是有不懂的童鞋能够看看个人另外一篇关于Spring  IOC 的文章

那么这行代码呢?这行代码的意思就是将咱们的CalculationAnnotation类交给Spring容器管理,由于咱们在CalculationAnnotation类中不是声明了一个@Aspect切面注解吗对不对

当Spring容器初始化的时候它会找有没有这个节点,若是有的话呢,容器就会根据你的Bean配置,找看那个类中配置了@Aspect切面注解

若是找到了的话那么就根据你的注解来执行相应的代码,什么意思呢?好比说如图所示

好,那么咱们就来看看执行以后结果会是怎样的呢?

这样咱们是否是就完成了以前的需求呢?在执行代码前输出一行语句,若是咱们想要作到日志的记录的话,是否是只须要把输出语句修改成记录日志的代码就能够了呢。并且我尚未影响任何的功能性代码

也就是对源代码并无作任何的修改,那么既然有前置加强的话确定也有后置加强和其余加强操做下面我就讲讲后置加强,

其实对于其余的加强类型的话呢,既然知道前置加强是怎么一回事了,那么其余四种就垂手可得了

后置加强,其实咱们只须要在切面类也就是咱们写前置加强的类中直接添加后置 加强代码便可,如图

只是将注解标签给进行了一道修改,其余的任何操做咱们都不须要在进行修改,示例结果如图所示

这样是否是就完成了在方法先后执行与方法无关的代码呢?可能有些童鞋有疑问,为何输出语句是在最后输出的,不该该是夹在中间吗?可是咱们看测试代码,我是执行了add方法,接收了一个返回值,而后在方法的外面输出的我接收的返回值变量,那么这样的话,可不就是咱们看到的结果嘛。

因为时间的关系呢,我今天就先给你们分享下前置加强和后置加强。至于返回,异常和环绕的话呢,我就下次在跟你们分享吧!但愿你们可以学到点东西吧!

 

转载:https://www.cnblogs.com/liujiayun/p/5912628.html

相关文章
相关标签/搜索