Spring框架中AOP原理及通知类型

这篇文章讲述的是Spring框架中的AOP原理以及相关的一些例子,如有错误或不当之处还望各位大神批评指正

什么是AOP?

基本概念

  • AOP是一种编程思想,即面向切面编程,是对传统的面向对象编程思想(OOP)的补充

AOP术语

  • 切面Aspect:一个模块具有一组提供横切需求的 APIs
  • 通知Advice:这是实际行动之前或之后执行的方法
  • 目标对象Target object:被通知的对象
  • 代理proxy:向目标对象应用通知后生成的对象
  • 连接点Join point:程序执行的某个特定位置
  • 切点Pointcut: 这是一组一个或多个连接点,通知应该被执行
  • Introduction:引用允许你添加新方法或属性到现有的类中
  • Weaving: Weaving 把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成

AOP编程的好处

  • AOP的作用在与分离系统中各个关注点,将核心关注点和横切关注点分离开来

AOP的简单例子

业务要求

假如有一个Student接口及其实现类如下

/*Student接口*/
package com.cn.cmc.aop;
/** * @author 叶清逸 * @date 2018年7月17日下午9:25:59 * @version 1.0 * @project com.cn.cmc.aop */
public interface Student {
    //添加学生
    public void addStudent() ;

    //删除学生
    public void delStudent();   
}


/*Student实现类*/
package com.cn.cmc.aop;
/** * @author 叶清逸 * @date 2018年7月17日下午9:28:21 * @version 1.0 * @project com.cn.cmc.aop */
public class StudentImpl implements Student{

    @Override
    public void addStudent() {
        System.out.println("添加学生");
    }

    @Override
    public void delStudent() {
        System.out.println("删除学生");
    }

}

现有业务要求:在执行Student的方法前后打印通知

传统的实现方法

传统的解决办法是直接在实现的业务代码中添加通知

package com.cn.cmc.aop;
/** * @author 叶清逸 * @date 2018年7月17日下午9:28:21 * @version 1.0 * @project com.cn.cmc.aop */
public class StudentImpl implements Student{

    @Override
    public void addStudent() {
        //执行前通知
        System.out.println("方法addStudent开始执行");
        //业务代码
        System.out.println("添加学生");
        //执行后通知
        System.out.println("方法addStudent执行完毕");
    }

    @Override
    public void delStudent() {
        //执行前通知
        System.out.println("方法delStudent开始执行");
        //业务代码
        System.out.println("删除学生");
        //执行后通知
        System.out.println("方法delStudent执行完毕");
    }
}

问题:添加删除和改动时需要改动大量代码,且不便于维护

动态代理的实现方法

可以使用动态代理来实现相关的业务逻辑

  • 编写代理类
package com.cn.cmc.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/** * @author 叶清逸 * @date 2018年7月17日下午9:31:16 * @version 1.0 * @project com.cn.cmc.aop */
public class StudentProxy {

    private Student student ;

    public StudentProxy(Student student) {
        this.student = student;
    }

    @SuppressWarnings("unchecked")
    public Proxy getProxy(){
        //类加载器用于加载Class
        ClassLoader loader = ClassLoader.getSystemClassLoader() ;
        //代理类中实现的方法
        Class<Student> [] interfaces = new Class[]{Student.class} ;
        //当调用代理类时,实际执行的方法
        InvocationHandler handler = new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("方法"+method.getName()+"开始执行");

                if(method.getName().equals("addStudent")){
                    student.addStudent();
                }else if(method.getName().equals("delStudent")){
                    student.delStudent();
                }

                System.out.println("方法"+method.getName()+"执行结束");
                return proxy;
            }
        };
        //初始化代理类
        Proxy proxy = (Proxy) Proxy.newProxyInstance(loader, interfaces, handler) ;

        return proxy ;
    }   
}
  • 使用代理
//要代理类的接口
Student student = new StudentImpl() ;
//获取代理类
Student proxy = (Student) new StudentProxy(student).getProxy();
//执行方法
proxy.addStudent();
proxy.delStudent();

SpringAOP的实现方法

  • 通知的切面类
package com.cn.cmc.aop;
/** * * @author 叶清逸 * @date 2018年7月18日下午1:59:04 * @version 1.0 * @project com.cn.cmc.aop */
public class LoggingAspect {
    /** * 前置通知Advice */
    public void beforeMethod(){
        System.out.println("前置通知");
    }
    /** * 前置通知Advice */
    public void afterMethod(){
        System.out.println("后置通知");
    }
}
  • xml文件中的AOP配置
<?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-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
    <!-- 配置切面的bean -->
    <bean id="logging" class="com.cn.cmc.aop.LoggingAspect"></bean>
    <!-- 配置bean -->
    <bean id="student" class="com.cn.cmc.aop.StudentImpl"></bean>
    <!-- 配置aop aop:config标签:表示aop的配置 aop:pointcut标签:配置切点表达式 expression属性:切点的aspectJ表达式 id属性:切点的唯一标识 aop:aspect标签:配置切面及通知 id属性:该切面的唯一标识 ref属性:该切面关联的通知类 order属性:切面的优先级,值越小优先级越大 aop:before标签:表示前置通知 method属性:表示前置通知对应的方法 pointcut-ref:切点表达式 aop:after标签:表示后置通知 method属性:表示前置通知对应的方法 pointcut-ref:切点表达式 -->
    <aop:config>
        <aop:pointcut expression="execution(* com.cn.cmc.aop.Student.*() )" id="pointcut"/>
        <aop:aspect ref="logging" order="1">
            <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
            <aop:after method="afterMethod" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
</beans>
  • 运行
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext("config/beans-aop.xml") ;
        Student student = (Student) context.getBean("student") ;
        student.addStudent();
        student.delStudent();
    }
}

切面的优先级

通过aop:aspect的order属性可以配置切面的优先级,值越小优先级越大

AOP的通知类型

  • Spring中AOP的通知类型包括:

    1. 前置通知:业务接口方法代码执行前执行
    2. 后置通知:业务接口方法代码执行后执行
    3. 返回通知:业务接口方法代码返回时执行(可以取得返回值)
    4. 异常通知:业务接口方法代码异常时执行
    5. 环绕通知:类似于执行动态代理的全过程
  • 通知位置的理解代码

/*动态代理*/
//当调用代理类时,实际执行的方法
    InvocationHandler handler = new InvocationHandler() {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //执行业务代码
            try{
                /*前置通知执行位置*/

                /*执行业务代码*/
                student.addStudent();

                /*返回通知执行位置*/
            }catch(Exception e){
                /*异常通知执行位置*/
            }
            /*后置通知执行位置*/
            return proxy;
        }
    };

前置通知

业务接口方法代码执行前执行

  • xml中配置
<!--aop:before标签:表示前置通知 method属性:表示前置通知对应的方法 pointcut-ref:切点表达式 -->
<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
  • 通知代码
/** * 前置通知Advice * JoinPoint:获取方法执行细节 */
public void beforeMethod(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName() ;
    System.out.println("方法"+methodName+"开始执行");
}

后置通知

业务接口方法代码执行后执行,发生异常时同样执行

  • xml中配置
<!--aop:after标签:表示前置通知 method属性:表示前置通知对应的方法 pointcut-ref:切点表达式 -->
<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
  • 通知代码
/** * 后置通知Advice * JoinPoint:获取方法执行细节 */
public void afterMethod(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName() ;
    System.out.println("方法"+methodName+"执行结束");
}

返回通知

业务接口方法代码返回后执行
– xml

<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
  • 通知代码
/** * 返回通知afterReturning * 可以获取返回值 */
public void afterReturning(JoinPoint joinPoint ,Object result){
    String methodName = joinPoint.getSignature().getName() ;
    System.out.println("方法"+methodName+"执行结束"+"返回值:"+result);
    }

异常通知

业务接口方法代码抛出异常时执行

  • xml
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
  • 通知代码
/** * 异常通知afterThrowing */
public void afterThrowing(JoinPoint joinPoint ,Exception e){
    String methodName = joinPoint.getSignature().getName() ;
    System.out.println("方法"+methodName+"执行结束"+"异常:"+e);
}

环绕通知

类似于执行动态代理的全过程

  • xml
<aop:around method="aroundMethod" pointcut-ref="pointcut"/>
  • 通知代码
/** * 环绕通知aroundMethod * 环绕通知需要携带ProceedingJoinPoint参数 */
public Object aroundMethod(ProceedingJoinPoint pjp){
    System.out.println("环绕通知");
    return 1000 ;
}
    原文作者:AOP
    原文地址: https://blog.csdn.net/u013634252/article/details/81090086
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞