java学习笔记——spring之aop、切面类中五种通知的使用、存在多个切面类时切面通知的执行顺序、注释的方式定义切面类与通知、xml配置的方式定义切面类与通知

3、AOP

AOP:(Aspect Oriented Programming)面向切面编程;

OOP:(Object Oriented Programming )面向对象编程;

 

面向切面编程:基于OOP基础之上新的编程思想;

指在程序运行期间,将某段代码动态的切入指定方法指定位置进行运行的这种编程方式,面向切面编程;

 

场景:计算器运行计算方法的时候进行日志记录;

加日志记录:

1)、直接编写在方法内部;不推荐,修改维护麻烦;

          

          日志记录:系统的辅助功能;

          业务逻辑:(核心功能)

          耦合;

2)、我们希望的是;

          业务逻辑:(核心功能);日志模块;在核心功能运行期间,自己动态的加上;

          运行的时候,日志功能可以加上;

  可以使用动态代理来将日志代码动态的在目标方法执行前后先进行执行;

 

package com.atguigu.proxy;

 

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.util.Arrays;

 

import com.atguigu.inter.Calculator;

import com.atguigu.utils.LogUtils;

 

/**

 * Calculator.java生成代理对象的类

 * Object newProxyInstance

 * (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

 *

 * @author lfy

 *

 */

public class CalculatorProxy {

 

    /**

     * 为传入的参数对象创建一个动态代理对象

     * @param calculator

     * @return

     *

     * Calculator calculator:被代理对象;(宝宝)

     * 返回的:宋喆

     */

    public static Calculator getProxy(final Calculator calculator) {

        // TODO Auto-generated method stub

 

        //方法执行器。帮我们目标对象执行目标方法

        InvocationHandler h = new InvocationHandler() {

            /**

             * Object proxy:代理对象;给jdk使用,任何时候都不要动这个对象

             * Method method:当前将要执行的目标对象的方法

             * Object[] args:这个方法调用时外界传入的参数值

             */

            @Override

            public Object invoke(Object proxy, Method method, Object[] args)

                    throws Throwable {

                

                //System.out.println("这是动态代理将要帮你执行方法...");

                Object result = null;

                try {

                    LogUtils.logStart(method, args);

 

                    // 利用反射执行目标方法

                    //目标方法执行后的返回值

                    result = method.invoke(calculator, args);

                    LogUtils.logReturn(method, result);

                } catch (Exception e) {

                    LogUtils.logException(method,e);

                }finally{

                    LogUtils.logEnd(method);

 

                }

 

                //返回值必须返回出去外界才能拿到真正执行后的返回值

                return result;

            }

        };

        Class<?>[] interfaces = calculator.getClass().getInterfaces();

        ClassLoader loader = calculator.getClass().getClassLoader();

 

        //Proxy为目标对象创建代理对象;

        Object proxy = Proxy.newProxyInstance(loader, interfaces, h);

        return (Calculator) proxy;

    }

 

}

动态代理:

     1)、写起来难;

     2)、jdk默认的动态代理,如果目标对象没有实现任何接口,是无法为他创建代理对象的;

Spring动态代理难;Spring实现了AOP功能;底层就是动态代理;

     1)、可以利用Spring一句代码都不写的去创建动态代理;

               实现简单,而且没有强制要求目标对象必须实现接口;

将某段代码(日志)动态的切入(不把日志代码写死在业务逻辑方法中)指定方法(加减乘除)指定位置(方法的开始、结束、异常。。。)进行运行的这种编程方式(Spring简化了面向切面编程)

 


AOP专业术语;

java学习笔记——spring之aop、切面类中五种通知的使用、存在多个切面类时切面通知的执行顺序、注释的方式定义切面类与通知、xml配置的方式定义切面类与通知

 


AOP使用步骤:

1)、导包;

commons-logging-1.1.3.jar

spring-aop-4.0.0.RELEASE.jar

spring-beans-4.0.0.RELEASE.jar

spring-context-4.0.0.RELEASE.jar

spring-core-4.0.0.RELEASE.jar

spring-expression-4.0.0.RELEASE.jar

 

Spring支持面向切面编程的包是:

spring-aspects-4.0.0.RELEASE.jar:基础版

加强版的面向切面编程(即使目标对象没有实现任何接口也能创建动态代理)

com.springsource.net.sf.cglib-2.2.0.jar

com.springsource.org.aopalliance-1.0.0.jar

com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

 

2)、写配置;

     1)、将目标类和切面类(封装了通知方法(在目标方法执行前后执行的方法))加入到ioc容器中

     2)、还应该告诉Spring到底哪个是切面类@Aspect

     3)、告诉Spring,切面类里面的每一个方法,都是何时何地运行;

①MyMathCalculator.java

package com.gome.impl;

import org.springframework.stereotype.Service;

import com.gome.inter.Calculator;


@Service
public class MyMathCalculator {

    
    public int add(int i,double j){
        System.out.println("方法内部执行");
        return 0;
    }
    //@Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("方法内部执行");
        return result;
    }

    //@Override
    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("方法内部执行");
        return result;
    }

    //@Override
    public int mul(int i, int j) {
        //方法的兼容性;
        int result = i * j;
        System.out.println("方法内部执行");
        return result;
    }

    //@Override
    public int div(int i, int j) {
        int result = i / j;
        System.out.println("方法内部执行");
        return result;
    }

}

② LogUtils.java

package com.gome.utils;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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;

/**
 * 如何将这个类(切面类)中的这些方法(通知方法)动态的在目标方法运行的各个位置切入
 * @author lfy
 *
 */
@Aspect
@Component
@Order(1)//使用Order改变切面顺序;数值越小优先级越高
public class LogUtils {
    
    /**
     * 告诉Spring每个方法都什么时候运行;
     * try{
     *         @Before
     *         method.invoke(obj,args);
     *         @AfterReturning
     * }catch(e){
     *         @AfterThrowing
     * }finally{
     *         @After
     * }
     *     
     * 5个通知注解
     * @Before:在目标方法之前运行;                       前置通知
     * @After:在目标方法结束之后                        后置通知
     * @AfterReturning:在目标方法正常返回之后            返回通知
     * @AfterThrowing:在目标方法抛出异常之后运行            异常通知
     * @Around:环绕                                环绕通知
     *
     *
     * 抽取可重用的切入点表达式;
     * 1、随便声明一个没有实现的返回void的空方法
     * 2、给方法上标注@Pointcut注解
     */
    @Pointcut("execution(public int com.atguigu.impl.MyMathCalculator.*(..))")
    public void hahaMyPoint(){};
    


    
    //想在执行目标方法之前运行;写切入点表达式
    //execution(访问权限符  返回值类型  方法签名)
    @Before("hahaMyPoint()")
    public static void logStart(JoinPoint joinPoint){
        //获取到目标方法运行是使用的参数
        Object[] args = joinPoint.getArgs();
        //获取到方法签名
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println("[LogUtils-前置]【"+name+"】方法开始执行,用的参数列表【"+Arrays.asList(args)+"】");
    }
    
    /**
     * 切入点表达式的写法;
     * 固定格式: execution(访问权限符  返回值类型  方法全类名(参数表))
     *   
     * 通配符:
     *         *:
     *             1)匹配一个或者多个字符:execution(public int com.atguigu.impl.MyMath*r.*(int, int))
     *             2)匹配任意一个参数:第一个是int类型,第二个参数任意类型;(匹配两个参数)
     *                 execution(public int com.atguigu.impl.MyMath*.*(int, *))
     *             3)只能匹配一层路径
     *             4)权限位置*不能;权限位置不写就行;public【可选的】
     *         ..:
     *             1)匹配任意多个参数,任意类型参数
     *             2)匹配任意多层路径:
     *                 execution(public int com.atguigu..MyMath*.*(..));
     *
     * 记住两种;
     * 最精确的:execution(public int com.atguigu.impl.MyMathCalculator.add(int,int))
     * 最模糊的:execution(* *.*(..)):千万别写;
     *
     * &&”、“||”、“!
     *
     * &&:我们要切入的位置满足这两个表达式
     *     MyMathCalculator.add(int,double)
     * execution(public int com.atguigu..MyMath*.*(..))&&execution(* *.*(int,int))
     *
     *
     * ||:满足任意一个表达式即可
     * execution(public int com.atguigu..MyMath*.*(..))&&execution(* *.*(int,int))
     *
     * !:只要不是这个位置都切入
     * !execution(public int com.atguigu..MyMath*.*(..))
     *
     * 告诉Spring这个result用来接收返回值:
     *     returning="result";
     */
    //想在目标方法正常执行完成之后执行
    @AfterReturning(value="hahaMyPoint()",returning="result")
    public static void logReturn(JoinPoint joinPoint,Object result){
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println("[LogUtils-返回]【"+name+"】方法正常执行完成,计算结果是:"+result);
    }

    
    /**
     * 细节四:我们可以在通知方法运行的时候,拿到目标方法的详细信息;
     * 1)只需要为通知方法的参数列表上写一个参数:
     *         JoinPoint joinPoint:封装了当前目标方法的详细信息
     * 2)、告诉Spring哪个参数是用来接收异常
     *         throwing="exception":告诉Spring哪个参数是用来接收异常
     * 3)、Exception exception:指定通知方法可以接收哪些异常
     *
     * ajax接受服务器数据
     *     $.post(url,function(abc){
     *         alert(abc)
     *     })
     */
    //想在目标方法出现异常的时候执行
    @AfterThrowing(value="hahaMyPoint()",throwing="exception")
    public static void logException(JoinPoint joinPoint,Exception exception) {
        System.out.println("[LogUtils-异常]【"+joinPoint.getSignature().getName()+"】方法执行出现异常了,异常信息是【"+exception+"】:;这个异常已经通知测试小组进行排查");
    }

    //想在目标方法结束的时候执行
    /**
     * Spring对通知方法的要求不严格;
     * 唯一要求的就是方法的参数列表一定不能乱写?
     *     通知方法是Spring利用反射调用的,每次方法调用得确定这个方法的参数表的值;
     *     参数表上的每一个参数,Spring都得知道是什么?
     *     JoinPoint:认识
     *     不知道的参数一定告诉Spring这是什么?
     *
     * @param joinPoint
     */
    @After("hahaMyPoint()")
    private int logEnd(JoinPoint joinPoint) {
        System.out.println("[LogUtils-后置]【"+joinPoint.getSignature().getName()+"】方法最终结束了");
        return 0;
    }
    
    /**
     * @throws Throwable
     * @Around:环绕    :是Spring中强大的通知;
     * @Around:环绕:动态代理;
     *     try{
     *             //前置通知
     *             method.invoke(obj,args);
     *             //返回通知
     *     }catch(e){
     *             //异常通知
     *  }finally{
     *             //后置通知
     *     }
     *         
     *     四合一通知就是环绕通知;
     *     环绕通知中有一个参数:    ProceedingJoinPoint pjp
     *
     *环绕通知:是优先于普通通知执行,执行顺序;
     *
     *[普通前置]
     *{
     *    try{
     *        环绕前置
     *        环绕执行:目标方法执行
     *        环绕返回
     *    }catch(){
     *        环绕出现异常
     *    }finally{
     *        环绕后置
     *    }
     *}
     *
     *
     *[普通后置]
     *[普通方法返回/方法异常]
     *新的顺序:
     *        (环绕前置---普通前置)----目标方法执行----环绕正常返回/出现异常-----环绕后置----普通后置---普通返回或者异常
     *注意:
     */
    
    @Around("hahaMyPoint()")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
        
        Object[] args = pjp.getArgs();
        String name = pjp.getSignature().getName();
        //args[0] = 100;
        Object proceed = null;
        try {
            //@Before
            System.out.println("【环绕前置通知】【"+name+"方法开始】");
            //就是利用反射调用目标方法即可,就是method.invoke(obj,args)
            proceed = pjp.proceed(args);
            //@AfterReturing
            System.out.println("【环绕返回通知】【"+name+"方法返回,返回值"+proceed+"】");
        } catch (Exception e) {
            //@AfterThrowing
            System.out.println("【环绕异常通知】【"+name+"】方法出现异常,异常信息:"+e);
            //为了让外界能知道这个异常,这个异常一定抛出去
            throw new RuntimeException(e);
        } finally{
            //@After
            System.out.println("【环绕后置通知】【"+name+"】方法结束");
        }
        
        //反射调用后的返回值也一定返回出去
        return proceed;
    }
}

③ BValidateApsect.java

package com.gome.utils;

import java.util.Arrays;

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

@Aspect
@Component
@Order(2)
public class BValidateApsect {
    
    
    @Before("com.atguigu.utils.LogUtils.hahaMyPoint()")
    public void logStart(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println("[VaApsect-前置]【"+name+"】方法开始执行,用的参数列表【"+Arrays.asList(args)+"】");
    }
    
    @AfterReturning(value="com.atguigu.utils.LogUtils.hahaMyPoint()",returning="result")
    public void logReturn(JoinPoint joinPoint,Object result){
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println("[VaApsect-返回]【"+name+"】方法正常执行完成,计算结果是:"+result);
    }
    
    @AfterThrowing(value="com.atguigu.utils.LogUtils.hahaMyPoint()",throwing="exception")
    public void logException(JoinPoint joinPoint,Exception exception) {
        System.out.println("[VaApsect-异常]【"+joinPoint.getSignature().getName()+"】方法执行出现异常了,异常信息是【"+exception+"】:;这个异常已经通知测试小组进行排查");
    }

    @After("com.atguigu.utils.LogUtils.hahaMyPoint()")
    private int logEnd(JoinPoint joinPoint) {
        System.out.println("[VaApsect-后置]【"+joinPoint.getSignature().getName()+"】方法最终结束了");
        return 0;
    }
}

④ AOPTest.java

package com.gome.test;

import static org.junit.Assert.*;

import java.util.Arrays;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.gome.impl.MyMathCalculator;

public class AOPTest {
    
    ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    
    
    
    /**
     * 通知方法的执行顺序;
     *
     *   try{
     *       @Before
     *       method.invoke(obj,args);
     *       @AfterReturning
     *   }catch(){
     *       @AfterThrowing
     *   }finally{
     *       @After
     *   }
     *
     * 正常执行:  @Before(前置通知)[email protected](后置通知)[email protected](正常返回);
     * 异常执行: @Before(前置通知)[email protected](后置通知)[email protected](方法异常)
     *
     *
     *
     *
     */
    @Test
    public void test03(){
        MyMathCalculator bean = ioc.getBean(MyMathCalculator.class);
        
        
        int add = bean.add(1, 1);
        System.out.println("========="+add);
        //bean.div(1, 0);
        //System.out.println("======");
    }
    
    @Test
    public void test02(){
        MyMathCalculator bean = ioc.getBean(MyMathCalculator.class);
        bean.add(1, 0.2);
        
    }

    /**
     commons-logging-1.1.3.jar
spring-aop-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar:基础版

加强版的面向切面编程(即使目标对象没有实现任何接口也能创建动态代理)
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

     *
     */
    @Test
    public void test() {
        
//        MyMathCalculator myMathCalculator = new MyMathCalculator();
//        myMathCalculator.add(2, 12);
        
        //1、从ioc容器中拿到目标对象;注意:如果想要用类型,一定用 他的接口类型,不要用它本类
        //细节一:[email protected]
        //class com.sun.proxy.$Proxy12
        
        
        //AOP的底层就是动态代理,容器中保存的组件是他的代理对象;$Proxy12。当然不是本类的类型
        
        //Calculator bean = ioc.getBean(Calculator.class);
        //bean.add(2, 1);
        //System.out.println(bean);
        //System.out.println(bean.getClass());
        
        
        
        //Calculator bean2 = (Calculator) ioc.getBean("myMathCalculator");
        //System.out.println(bean2.getClass());
        
        //没有接口就是本类类型
        //class com.atguigu.impl.MyMathCalculator$$EnhancerByCGLIB$$fe279f42
        //cglib帮我们创建好的代理对象
        MyMathCalculator bean = (MyMathCalculator) ioc.getBean("myMathCalculator");
        bean.add(1, 2);
        System.out.println(bean.getClass());
        
        
    }

}

⑤ 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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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-4.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <context:component-scan base-package="com.atguigu"></context:component-scan>

    <!--  开启基于注解的AOP功能;aop名称空间-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

          4)、开启基于注解的AOP模式

① 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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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-4.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <context:component-scan base-package="com.atguigu"></context:component-scan>

    <!--  开启基于注解的AOP功能;aop名称空间-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

 

3)、测试;

 

 

Calculator bean = ioc.getBean(Calculator.class);

        bean.add(2, 1);

 

AOP使用场景:

     1)、AOP加日志保存到数据库;

     2)、AOP做权限验证;

     3)、AOP做安全检查;

     4)、AOP做事务控制;

 

以上是使用注释的方式实现aop,现在我们再来看看使用xml配置的方式实现aop

MyMathCalculator.java

package com.gome.impl;

import org.springframework.stereotype.Service;

import com.atguigu.inter.Calculator;


public class MyMathCalculator {

    
    public int add(int i,double j){
        System.out.println("方法内部执行");
        return 0;
    }
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("方法内部执行");
        return result;
    }

    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("方法内部执行");
        return result;
    }

    public int mul(int i, int j) {
        //方法的兼容性;
        int result = i * j;
        System.out.println("方法内部执行");
        return result;
    }

    public int div(int i, int j) {
        int result = i / j;
        System.out.println("方法内部执行");
        return result;
    }

}

② LogUtils.java

package com.gome.utils;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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;

public class LogUtils {
    
    
    public static void logStart(JoinPoint joinPoint){
        //获取到目标方法运行是使用的参数
        Object[] args = joinPoint.getArgs();
        //获取到方法签名
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println("[LogUtils-前置]【"+name+"】方法开始执行,用的参数列表【"+Arrays.asList(args)+"】");
    }
    public static void logReturn(JoinPoint joinPoint,Object result){
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println("[LogUtils-返回]【"+name+"】方法正常执行完成,计算结果是:"+result);
    }

    
    public static void logException(JoinPoint joinPoint,Exception exception) {
        System.out.println("[LogUtils-异常]【"+joinPoint.getSignature().getName()+"】方法执行出现异常了,异常信息是【"+exception+"】:;这个异常已经通知测试小组进行排查");
    }

    public int logEnd(JoinPoint joinPoint) {
        System.out.println("[LogUtils-后置]【"+joinPoint.getSignature().getName()+"】方法最终结束了");
        return 0;
    }
    
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
        
        Object[] args = pjp.getArgs();
        String name = pjp.getSignature().getName();
        //args[0] = 100;
        Object proceed = null;
        try {
            //@Before
            System.out.println("【环绕前置通知】【"+name+"方法开始】");
            //就是利用反射调用目标方法即可,就是method.invoke(obj,args)
            proceed = pjp.proceed(args);
            //@AfterReturing
            System.out.println("【环绕返回通知】【"+name+"方法返回,返回值"+proceed+"】");
        } catch (Exception e) {
            //@AfterThrowing
            System.out.println("【环绕异常通知】【"+name+"】方法出现异常,异常信息:"+e);
            //为了让外界能知道这个异常,这个异常一定抛出去
            throw new RuntimeException(e);
        } finally{
            //@After
            System.out.println("【环绕后置通知】【"+name+"】方法结束");
        }
        
        //反射调用后的返回值也一定返回出去
        return 1000;
    }
}

③ BValidateApsect.java

package com.gome.utils;

import java.util.Arrays;

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

public class BValidateApsect {
    
    
    public void logStart(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println("[VaApsect-前置]【"+name+"】方法开始执行,用的参数列表【"+Arrays.asList(args)+"】");
    }
    
    public void logReturn(JoinPoint joinPoint,Object result){
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println("[VaApsect-返回]【"+name+"】方法正常执行完成,计算结果是:"+result);
    }
    
    public void logException(JoinPoint joinPoint,Exception exception) {
        System.out.println("[VaApsect-异常]【"+joinPoint.getSignature().getName()+"】方法执行出现异常了,异常信息是【"+exception+"】:;这个异常已经通知测试小组进行排查");
    }

    public int logEnd(JoinPoint joinPoint) {
        System.out.println("[VaApsect-后置]【"+joinPoint.getSignature().getName()+"】方法最终结束了");
        return 0;
    }
}

④ AOPTest.java

package com.gome.test;

import static org.junit.Assert.*;

import java.util.Arrays;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.gome.impl.MyMathCalculator;

public class AOPTest {
    
    ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    
    
    
    /**
     * 通知方法的执行顺序;
     *
     *   try{
     *       @Before
     *       method.invoke(obj,args);
     *       @AfterReturning
     *   }catch(){
     *       @AfterThrowing
     *   }finally{
     *       @After
     *   }
     *
     * 正常执行:  @Before(前置通知)[email protected](后置通知)[email protected](正常返回);
     * 异常执行: @Before(前置通知)[email protected](后置通知)[email protected](方法异常)
     *
     *
     *
     *
     */
    @Test
    public void test03(){
        MyMathCalculator bean = ioc.getBean(MyMathCalculator.class);
        
        
        int add = bean.add(1, 1);
        System.out.println("========="+add);
        //bean.div(1, 0);
        //System.out.println("======");
    }
    
    @Test
    public void test02(){
        MyMathCalculator bean = ioc.getBean(MyMathCalculator.class);
        bean.add(1, 0.2);
        
    }

    /**
     commons-logging-1.1.3.jar
spring-aop-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar:基础版

加强版的面向切面编程(即使目标对象没有实现任何接口也能创建动态代理)
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

     *
     */
    @Test
    public void test() {
        
//        MyMathCalculator myMathCalculator = new MyMathCalculator();
//        myMathCalculator.add(2, 12);
        
        //1、从ioc容器中拿到目标对象;注意:如果想要用类型,一定用 他的接口类型,不要用它本类
        //细节一:[email protected]
        //class com.sun.proxy.$Proxy12
        
        
        //AOP的底层就是动态代理,容器中保存的组件是他的代理对象;$Proxy12。当然不是本类的类型
        
        //Calculator bean = ioc.getBean(Calculator.class);
        //bean.add(2, 1);
        //System.out.println(bean);
        //System.out.println(bean.getClass());
        
        
        
        //Calculator bean2 = (Calculator) ioc.getBean("myMathCalculator");
        //System.out.println(bean2.getClass());
        
        //没有接口就是本类类型
        //class com.atguigu.impl.MyMathCalculator$$EnhancerByCGLIB$$fe279f42
        //cglib帮我们创建好的代理对象
        MyMathCalculator bean = (MyMathCalculator) ioc.getBean("myMathCalculator");
        bean.add(1, 2);
        System.out.println(bean.getClass());
        
        
    }

}

⑤ 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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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-4.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 基于注解的AOP步骤;
    1、将目标类和切面类都加入到ioc容器中。@Component
    2、告诉Spring哪个是切面类。@Aspect
    3、在切面类中使用五个通知注解来配置切面中的这些通知方法都何时何地运行
    4、开启基于注解的AOP功能
     -->
    <!--  开启基于注解的AOP功能;aop名称空间-->
    
    <!--  基于配置的AOP-->
    <bean id="myMathCalculator" class="com.atguigu.impl.MyMathCalculator"></bean>
    <bean id="BValidateApsect" class="com.atguigu.utils.BValidateApsect"></bean>
    <bean id="logUtils" class="com.atguigu.utils.LogUtils"></bean>
    
    <!-- 需要AOP名称空间 -->
    <aop:config>
        <aop:pointcut expression="execution(* com.atguigu.impl.*.*(..))" id="globalPoint"/>
    
    
        <!-- 普通前置  ===== 目标方法  =====(环绕执行后置/返回)====s普通后置====普通返回    -->
        <!-- 指定切面:@Aspect -->
        <aop:aspect ref="logUtils" order="1">
            <!-- 配置哪个方法是前置通知;method指定方法名
            [email protected]("切入点表达式")
            -->
            <!-- 当前切面能用的 -->
            <aop:around method="myAround" pointcut-ref="mypoint"/>
            <aop:pointcut expression="execution(* com.atguigu.impl.*.*(..))" id="mypoint"/>
            <aop:before method="logStart" pointcut="execution(* com.atguigu.impl.*.*(..))"/>
            <aop:after-returning method="logReturn" pointcut-ref="mypoint" returning="result"/>
            <aop:after-throwing method="logException" pointcut-ref="mypoint" throwing="exception"/>
            <aop:after method="logEnd" pointcut-ref="mypoint"/>
        </aop:aspect>
    
        <aop:aspect ref="BValidateApsect" order="3">
            <aop:before method="logStart" pointcut-ref="globalPoint"/>
            <aop:after-returning method="logReturn" pointcut-ref="globalPoint" returning="result"/>
            <aop:after-throwing method="logException" pointcut-ref="globalPoint" throwing="exception"/>
            <aop:after method="logEnd" pointcut-ref="globalPoint"/>
        </aop:aspect>
        <!-- 在切面类中使用五个通知注解来配置切面中的这些通知方法都何时何地运行 -->
    </aop:config>

    <!--注解:快速方便
        配置:功能完善;重要的用配置,不重要的用注解;
      -->
</beans>

注意:

存在多个切面类时切面通知的执行顺序默认是按照 类名字的首字母顺序来执行的,我们可以使用(@Order(1)//使用Order改变切面顺序;数值越小优先级越高),并且先执行@Before: 前置通知的切面类会最后执行@After:后置通知,即先进后出原则;

还有就是五种通知中的@Around:环绕通知比较特殊,但也仅仅只是作用在本切面类的而已,无法控制其他的切面类,带有环绕通知的切面类的通知执行顺序为:

(环绕前置---普通前置)----目标方法执行----环绕正常返回/出现异常-----环绕后置----普通后置---普通返回或者异常

剩下的看上边的源码吧,写的很细。。