SpringAOP的基础使用

SpringAOP

概述

AOP的全称是Aspect Oriented Programming(面向切面编程)

OOP语言提供了类与类之间纵向的关系(继承、接口),而AOP补充了横向的关系(比如在不改变目标类中源代码的情况下给com.john.demo.dao包下所有类中以insert和update开头的方法添加事务管理)

SpringAOP和AspectJ的区别

​ AspectJ是一个专门主打面向切面编程的框架。 它是使用一种特殊的语言(扩展自Java语言)来编写切面代码,后缀是.aj格式,并且需要使用专门的编译器将其编译成jvm可以运行的class文件。

​ SpringAOP底层也是使用了AspectJ的方案,但是在上层做了很多封装层面的工作,可以让开发人员直接使用Java代码来编写切面。并且由于使用的是标准的Java语言,所以并不需要在额外安装一个专门的编译器。但是由于开发人员直接接触的是Spring AOP,那么凡是Spring中没有实现的那些AOP功能,我们就用不了了,这种情况下只能跟产品经理撕逼或者去学习原生的AspectJ。

AOP的术语

  • 切面(Aspect)

    简单来说,切面就是我们要往目标代码中插入进去的代码。

  • 连接点(Join Pointer)

    理论上所有可能会被切入的地方都可以称之为连接点

  • 切入点(Pointcut)

    选择某个连接点切入,将切面代码织入进去。这个连接点就叫做切入点。

  • 织入(Weaving)

    把切面代码糅合到目标代码中的过程就是织入。

  • 通知(Advice)

    通知决定了切面代码织入到目标代码中后,运行的时机(比如是在目标方法执行前,还是执行后)。

在Spring中使用AOP

基于XML方式使用

  1. 把aop的schema引入

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           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.xsd">
    
  2. 创建一个切面类,并且以bean的方式配置到IOC容器中

    package com.lanou3g.spring;
    public class MyAspect {
    
        public void wakeup() {
            System.out.println("[前置通知]我刚学习SpringAOP睡着了,刚才谁打了我一下?");
        }
    
    
        public void goToBed() {
            System.out.println("[后置通知]SpringAOP太难了,一不小心又睡着了");
        }
    
    
        public void afterRetuing(Object message) {
            System.out.println("[后置返回通知]方法执行已经return了,方法返回值是:" + message);
        }
    
        public void afterThrowing(Throwable ex) {
            System.out.println("[后置异常通知]方法执行出现异常,异常原因:" + ex.getMessage());
        }
    
        /**
         * 环绕通知
         * 可以接受一个ProceedingJoinPoint参数
         *      通过此参数可以获取到被切入方法的所有信息
         *      还可以通过此参数来决定是否调用目标方法
         */
        public void aroundAdvice(ProceedingJoinPoint joinPoint) {
    
            // 连接点参数可以获取到被切入方法的所有信息
            // 这里演示了如何获取被切入方法的名称
            String targetMethodName = joinPoint.getSignature().getName();
            System.out.println("[环绕通知]被切入的方法名:" + targetMethodName);
    
            //
            System.out.println("[环绕通知]即将开始新的一天, 早起的鸟儿有虫吃!");
            try {
                joinPoint.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            System.out.println("[环绕通知]这真是充实的一天, 早睡早起,方能养生!");
        }
    }
    
    <bean id="myAspect" class="com.lanou3g.spring.MyAspect" />
    
  3. 使用aop:config标签配置aop(将切面、切入点、通知结合到一起)

    1. 定义切入点表达式
    2. aop:aspect
      1. 引用外部定义的切面bean
      2. 配置通知,引用切入点表达式
    <aop:config>
        
    	<aop:pointcut id="myPointcut" expression="execution(* com.lanou3g.spring..*.oneDay*(..))" />
        <aop:aspect ref="myAspect">
        	
                <aop:before method="wakeup" pointcut-ref="beforeOneDay" />
                
                <aop:after method="goToBed" pointcut-ref="beforeOneDay" />
                
                <aop:around method="aroundAdvice" pointcut-ref="beforeOneDay" />
                
                <aop:after-returning method="afterRetuing" pointcut-ref="beforeOneDay" returning="message" />
                
                <aop:after-throwing method="afterThrowing" pointcut-ref="beforeOneDay" throwing="ex" />
        aop:aspect>
    aop:config>
    

基于注解方式使用

  1. 开启AOP注解支持

    方式一:注解的方式

    @Configuration
    @EnableAspectJAutoProxy
    public class AppConfig {
    
    }
    

    方式二:xml中开启

    <aop:aspectj-autoproxy/>
    
    1. 定义切面类
    /**
     * 该切面用来插入起床的逻辑
     */
    @Aspect
    @Component  //@Aspect注解没有将bean交给ioc容器管理的功能
    public class MyAspect {
    
        @Before("com.lanou3g.spring.aop.MyPointcut.allOneDayMehtod()")
        public void wakeup() {
            System.out.println("[前置通知]我刚学习SpringAOP睡着了,刚才谁打了我一下?");
        }
    
        @After("com.lanou3g.spring.aop.MyPointcut.allOneDayMehtod()")
        public void goToBed() {
            System.out.println("[后置通知]SpringAOP太难了,一不小心又睡着了");
        }
    
        @AfterReturning(value = "com.lanou3g.spring.aop.MyPointcut.allOneDayMehtod()", returning = "message")
        public void afterRetuing(Object message) {
            System.out.println("[后置返回通知]方法执行已经return了,方法返回值是:" + message);
        }
    
        @AfterThrowing(value = "com.lanou3g.spring.aop.MyPointcut.allOneDayMehtod()", throwing = "ex")
        public void afterThrowing(Throwable ex) {
            System.out.println("[后置异常通知]方法执行出现异常,异常原因:" + ex.getMessage());
        }
    
        /**
         * 环绕通知
         * 可以接受一个ProceedingJoinPoint参数
         *      通过此参数可以获取到被切入方法的所有信息
         *      还可以通过此参数来决定是否调用目标方法
         */
    //    @Around("com.lanou3g.spring.aop.MyPointcut.allOneDayMehtod()")
        public Object aroundAdvice(ProceedingJoinPoint joinPoint) {
    
            // 连接点参数可以获取到被切入方法的所有信息
            // 这里演示了如何获取被切入方法的名称
            String targetMethodName = joinPoint.getSignature().getName();
            System.out.println("[环绕通知]被切入的方法名:" + targetMethodName);
    
            //
            System.out.println("[环绕通知]即将开始新的一天, 早起的鸟儿有虫吃!");
            Object ret = null;
            try {
                ret = joinPoint.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            System.out.println("[环绕通知]这真是充实的一天, 早睡早起,方能养生!");
            return ret;
        }
    }
    

    注意:@Aspect注解没有将bean交给ioc容器管理的功能,我们需要额外添加一个@Component注解

    1. 定义切入点

    官方建议我们将所有的切入点统一定义到一个地方管理,在配置通知时通过引入的方式来使用。方便后期维护(一处修改,处处生效)

    @Component
    public class MyPointcut {
        // 通过@Pointcut注解定义一个切入点
        @Pointcut("execution(* oneDay(..))")
        public void allOneDayMehtod() {}
    }
    
    1. 在切面类中添加要切入的代码

    参见定义切面部分

    1. 在切入的代码方法上添加通知的注解

    参见定义切面部分

你可能感兴趣的:(SpringAOP的基础使用)