SpringAOP

AOP:Aspect Oriented Programming  面向切面编程。
     面向切面编程是一种编程范式,它作为OOP面向对象编程的一种补充,用于处理系统中分布于各个模块的横切关注点,
     比如事务管理、权限控制、缓存控制、日志打印等等。AOP采取横向抽取机制,取代了传统纵向继承体系的重复性代码
     AOP把软件的功能模块分为两个部分:核心关注点和横切关注点。
         业务处理的主要功能为核心关注点,而非核心、需要拓展的功能为横切关注点。
         AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点进行分离

AOP的术语
1.Join point(连接点)
Spring 官方文档的描述:
A point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.
程序执行过程中的一个点,如方法的执行或异常的处理。在Spring AOP中,连接点总是表示方法的执行。通俗的讲,连接点即表示类里面可以被增强的方法
2.Pointcut(切入点)
Pointcut are expressions that is matched with join points to determine whether advice needs to be executed or not. Pointcut uses different kinds of expressions that are matched with the join points and Spring framework uses the AspectJ pointcut expression language
切入点是与连接点匹配的表达式,用于确定是否需要执行通知。切入点使用与连接点匹配的不同类型的表达式
Spring框架使用AspectJ切入点表达式语言。我们可以将切入点理解为需要被拦截的Join point
3.Advice(增强/通知)
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知
通知分为前置通知、后置通知、异常通知、最终通知和环绕通知(切面要完成的功能)
4.Aspect(切面)
Aspect切面表示Pointcut(切入点)和Advice(增强/通知)的结合

spring_config.xml

  <!--启用aspectj支持-->
    <aop:aspectj-autoproxy />
    
    <!--将方面类也注册为spring管理的bean-->
    <bean class="com.qdu.aop.LogAspect" />
    
    <bean id="mathService" class="com.qdu.service.impl.MathServiceImpl" />
    <bean id="studentService" class="com.qdu.service.impl.StudentServiceImpl" />
    <bean id="teacherService" class="com.qdu.service.impl.TeacherServiceImpl" />
View Code
package com.qdu.aop;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * 创建一个类,包含次要业务逻辑,也就是日志功能
 * <br>一个方面可以包含一个类,也可包含多个类
 */
@Aspect //该注解说明该方法是个方面类
public class LogAspect {

    //定义两个方法,分别包含要在方法调用前执行的操作和方法调用后执行的操作
    //方面包含的操作叫做通知,在java中,操作通常是封装成一个方法
    
    //通知对应的方法名可以随便起
    //每个通知要指定切入的切入点Pointcut
    //如果使用AspectJ的风格实现AOP,就可以使用AspectJ的语法写切入点
    //其中execution表示选中的切入点是方法的执行,spring只支持方法执行作为切入点
    //切入点中第一个星号表示方法的返回类型可以是任何类型
    //切入点中第二个星号表示切入点对应的类是com.qdu.service.impl包下的所有类
    //切入点中第三个星号表示切入点包含的连接点(也就是方法)的名称可以是任何名称
    //切入点最后的..说明方法的参数可以是任何参数,包括没有参数
    //总的来说,这里切入点包含的连接点是com.qdu.service.impl包下的所有类的所有方法的执行
    @Before("execution(* com.qdu.service.impl.*.*(..))") //@Before说明该方法是一个前置通知
    public void beforeAdvice(){
        System.out.println("-----------------------------方法调用前");
    }
    
    @AfterReturning("execution(* com.qdu.service.impl.*.*(..))") //@AfterReturning说明该方法是一个返回后通知
    public void afterReturningAdvice(){
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~方法调用后");
    }
}
View Code
package com.qdu.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * LogAspect类作为一个方面类,包含日志功能的次要业务逻辑
 * <br>一个方面可以包含一个类或多个类
 *
 * @author NIIT
 */
@Aspect
public class LogAspect { //方面包含通知和切入点等的定义

    //定义一个操作,用于执行目标方法执行前要执行的操作,方法名可以随便起
    //execution
    //如果想获取连接点的信息,添加一个JoinPoint类型的参数即可,参数名随便起
    //那么哪个目标方法执行,该对象携带的就是那个连接点的信息
    @Before("execution(* com.qdu.service.impl.StudentServiceImpl.*(..))")
    public void beforeAdvice1(JoinPoint point) {
        System.out.println("........................................"+point.getSignature().getName()+"方法调用前");
    }

    //切入点中也可限定为某个接口的方法,这样只要调用该接口的实现类的方法,就会应用通知
    //在这里解释一下通过连接点可以获取的信息
    @Before("execution(* com.qdu.service.MathService.*(..))")
    public void beforeAdvice2(JoinPoint point){
        System.out.println("---------------beforeAdvice2---------------");
        System.out.println("目标方法的名称:"+point.getSignature().getName());
        //可以获取目标对象并转换成对应类型进行操作,比如这里根据切入点的写法
        //目标对象是MathService的对象,可以转换成对应类型进行操作
        System.out.println("目标对象:"+point.getTarget());
        //argument
        //可以调用getArgs()方法获取目标方法的所有参数值
        //参数以Object数组的形式返回,如果需要转换成具体类型,可以手动转换
        System.out.print("目标方法的参数:");
        Object[] args=point.getArgs();
        for(Object arg:args)
            System.out.print(arg+";");
        System.out.println();
    }
    
    
    @AfterReturning("execution(* com.qdu.service.impl.*.*(..))")
    //定义一个方法,作为一个操作,包含目标方法执行成功返回后要执行的操作
    public void afterReturningAdvice(JoinPoint p) {
        System.out.println("----------------------------------------"+p.getSignature().getName()+"方法调用返回后");
    }
}
View Code

Advice注解
Advice注解一共有五种,分别是:
   1.@Before前置通知
       前置通知在切入点运行前执行,不会影响切入点的逻辑
    2.@After后置通知
       后置通知在切入点正常运行结束后执行,如果切入点抛出异常,则在抛出异常前执行
    3.@AfterThrowing异常通知
       异常通知在切入点抛出异常前执行,如果切入点正常运行(未抛出异常),则不执行
   4.@AfterReturning返回通知
       返回通知在切入点正常运行结束后执行,如果切入点抛出异常,则不执行
    5.@Around环绕通知
       环绕通知是功能最强大的通知,可以在切入点执行前后自定义一些操作。
       环绕通知需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行

 

package com.qdu.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * LogAspect类作为一个方面类,包含日志功能的次要业务逻辑
 * <br>一个方面可以包含一个类或多个类
 *
 * @author NIIT
 */
@Aspect
@Component
public class LogAspect {

    //第一个切入点覆盖StudentService接口的所有方法
    @Pointcut("execution(* com.qdu.service.StudentService.*(..))")
    public void logPointcut1() {
    }

    //第二个切入点覆盖所有业务接口的所有方法
    @Pointcut("execution(* com.qdu.service.*.*(..))")
    public void logPointcut2() {
    }

    //定义一个操作,用于执行目标方法执行前要执行的操作,方法名可以随便起
    @Before("logPointcut1()")
    public void beforeAdvice(JoinPoint point) {
        System.out.println("........................................" + point.getSignature().getName() + "方法调用前");
    }

    //定义一个方法,作为一个操作,包含目标方法执行成功返回后要执行的操作
    @AfterReturning("logPointcut2()")
    public void afterReturningAdvice(JoinPoint point) {
        System.out.println("----------------------------------------" + point.getSignature().getName() + "方法调用返回后");
    }
}
View Code

 

 

 

参考:https://www.cnblogs.com/liantdev/p/10125284.html

posted @ 2019-10-16 21:56  七忆鱼  阅读(132)  评论(0编辑  收藏  举报