AOP相关名词
Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
Target(目标对象):织入 Advice 的目标对象.。
schema-base方式增强
前置增强类

package com.demo;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

/*自定义类实现MethodBeforeAdvice接口*/
public class MyBefore implements MethodBeforeAdvice {
    /*实现before()方法,方法体为增强内容
    * 参数method 切点方法名
    * objects 参数集合
    * o 切点所在类的对象*/
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置通知,执行了");
    }
}

后置增强类

package com.demo;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/*自定义类实现AfterReturningAdvice接口*/
public class MyAfter implements AfterReturningAdvice {

    /*实现afterReturning()方法,方法体为增强内容
     * o 返回值对象
     * 参数method 切点方法名
     * objects 参数集合
     * o1 切点所在类的对象*/
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("后置增强,执行了");
    }
}

异常增强

package com.demo;

import org.springframework.aop.ThrowsAdvice;

/*自定义类实现ThrowsAdvice接口*/
public class MyThrow implements ThrowsAdvice {

    /*异常通知spring不提供方法,需要自己写。并且名字固定
    * 参数 e 异常信息*/
    public void afterThrowing(Exception e) throws Throwable{
        System.out.println("异常通知,执行了");
    }
}

环绕增强

package com.demo;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/*自定义类实现MethodInterceptor接口*/
public class MyAround implements MethodInterceptor {

    /*实现invoke()方法*
    参数 methodInvocation 切点方法/
     */
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        /*前置增强先执行*/
        System.out.println("环绕前置通知,执行了");
        /*通过反射执行切点方法*/
        Object obj = methodInvocation.proceed();
        /*执行后置增强*/
        System.out.println("环绕后置通知,执行了");
        return obj;
    }
}

配置文件

<?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"
       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.3.xsd">
        <!--将Demo类和写好的通知类交给spring管理-->
        <bean id="demo" class="com.demo.Demo"></bean>
        <bean id="myBefore" class="com.demo.MyBefore"></bean>
        <bean id="myAfter" class="com.demo.MyAfter"></bean>
        <bean id="myAround" class="com.demo.MyAround"></bean>
        <bean id="myThrow" class="com.demo.MyThrow"></bean>
        <!--aop配置-->
        <aop:config>
            <!--将Demo类的a()方法定义为切点
            expression 格式固定 execution(* com.demo.Demo.a()) *为定义通配符
            进行模糊匹配可写成execution(* com.demo.*.*(..)) com.demo包下的所有类的所有方法都是切点-->
            <aop:pointcut id="mypoint" expression="execution(* com.demo.Demo.a(..))"/>
            <!--MyBefore类实现了MethodBeforeAdvice接口,spring就知道他是前置通知-->
            <aop:advisor advice-ref="myBefore" pointcut-ref="mypoint"/>
            <!--MyAfter类实现了AfterReturningAdvice接口-->
            <aop:advisor advice-ref="myAfter" pointcut-ref="mypoint"/>
            <!--MyAround类实现了MethodInterceptor接口-->
            <aop:advisor advice-ref="myAround" pointcut-ref="mypoint"/>
            <!--MyThrow类实现了ThrowsAdvice接口,没有异常不执行-->
            <aop:advisor advice-ref="myThrow" pointcut-ref="mypoint"/>
        </aop:config>
</beans>

切点类

package com.demo;

import org.springframework.stereotype.Component;

public class Demo {

    public String a(int a){
        System.out.println("Hello!");
        return "hello";
    }
}

测试类

package com.test;

import com.demo.Demo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test1 {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Demo demo = (Demo) ac.getBean("demo");
        demo.a(1);
    }
}

运行结果

七月 25, 2019 2:43:50 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31221be2: startup date [Thu Jul 25 14:43:50 CST 2019]; root of context hierarchy
七月 25, 2019 2:43:50 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
前置通知,执行了
环绕前置通知,执行了
Hello!
环绕后置通知,执行了
后置增强,执行了

Process finished with exit code 0

AspectJ方式增强
通知类

package com.demo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/*不需要实现任何接口或继承类*/
public class MyAdvice {

        public void before(){
            System.out.println("前置增强,执行了");
        }
        /*发生异常不执行后置增强*/
        public void afterReturning(){
            System.out.println("后置增强,执行了");
        }
        /*无论是否发生异常都会执行after*/
        public void after(){
            System.out.println("最终增强,执行了");
        }
        public void afterThrow(){
            System.out.println("异常增强,执行了");
        }
        /*不发生异常,环绕前置,后置都执行
        * 发生异常只执行前置增强*/
        public void around(ProceedingJoinPoint joinPoint) throws Throwable{
            /*前置增强先执行*/
            System.out.println("环绕前置通知,执行了");
            /*通过反射执行切点方法*/
            Object obj = joinPoint.proceed();
            /*执行后置增强*/
            System.out.println("环绕后置通知,执行了");
        }

}

配置文件

<?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"
       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.3.xsd">
        <!--将Demo类和写好的通知类交给spring管理-->
        <bean id="demo" class="com.demo.Demo"></bean>
        <bean id="myAdvice" class="com.demo.MyAdvice"></bean>
    <aop:config>
        <!--引用myAdvice为增强类
        ref 增强类的id-->
        <aop:aspect ref="myAdvice">
            <!--定义切点-->
            <aop:pointcut expression="execution(* com.demo.Demo.a(..))" id="mypoint"/>
            <!--前置增强
            method 前置增强方法
            pointcut-ref 切点的id-->
            <aop:before method="before" pointcut-ref="mypoint"/>
            <!--后置增强,发生异常不执行-->
            <aop:after-returning method="afterReturning" pointcut-ref="mypoint"/>
            <!--最终增强,无论是否发生异常都会执行-->
            <aop:after method="after" pointcut-ref="mypoint"/>
            <!--异常增强,发生异常才会执行-->
            <aop:after-throwing method="afterThrow" pointcut-ref="mypoint"/>
            <!--环绕增强,前置+后置-->
            <aop:around method="around" pointcut-ref="mypoint"/>
        </aop:aspect>
    </aop:config>
</beans>

不出异常运行结果

七月 25, 2019 3:04:34 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31221be2: startup date [Thu Jul 25 15:04:34 CST 2019]; root of context hierarchy
七月 25, 2019 3:04:34 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
前置增强,执行了
环绕前置通知,执行了
Hello!
环绕后置通知,执行了
最终增强,执行了
后置增强,执行了

出异常运行结果

七月 25, 2019 3:05:08 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31221be2: startup date [Thu Jul 25 15:05:08 CST 2019]; root of context hierarchy
七月 25, 2019 3:05:08 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
前置增强,执行了
环绕前置通知,执行了
异常增强,执行了
最终增强,执行了

Process finished with exit code 0

使用注解方式
配置文件

<?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.3.xsd
        http://www.springframework.org/schema/context
     	http://www.springframework.org/schema/context/spring-context-4.3.xsd">
        <!--开启spring的扫描,beans中需要加上对应得xsd-->
        <context:component-scan base-package="com.demo"></context:component-scan>
        <!-- 开启aop注解方式,默认为false -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

切点类

package com.demo;

import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
public class Demo {

    public String a(int a) throws Exception{
        /*制造异常,向上抛*/
        //int i = 1/0;
        System.out.println("Hello!");
        return "hello";
    }
}

增强(切面)类

package com.demo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/*交给spring管理*/
@Component
/*指定该类为切面类*/
@Aspect
public class MyAdvice {

    /*定义切点*/
    @Pointcut("execution(* com.demo.Demo.a(..))")
    public void pointCut() {
    }

    @Before("pointCut()")
    public void before() {
        System.out.println("前置增强,执行了");
    }

    /*发生异常不执行后置增强*/
    @AfterReturning("pointCut()")
    public void afterReturning() {
        System.out.println("后置增强,执行了");
    }

    /*无论是否发生异常都会执行after*/
    @After("pointCut()")
    public void after() {
        System.out.println("最终增强,执行了");
    }

    @AfterThrowing("pointCut()")
    public void afterThrow() {
        System.out.println("异常增强,执行了");
    }

    /*不发生异常,环绕前置,后置都执行
     * 发生异常只执行前置增强*/
    @Around("pointCut()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        /*前置增强先执行*/
        System.out.println("环绕前置通知,执行了");
        /*通过反射执行切点方法*/
        Object obj = joinPoint.proceed();
        /*执行后置增强*/
        System.out.println("环绕后置通知,执行了");
    }

}

测试类

package com.test;

import com.demo.Demo;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test1 {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Demo demo = (Demo) ac.getBean("demo");
        try {
            demo.a(1);
        } catch (Exception e) {
        }
    }
}

无异常运行结果

七月 25, 2019 3:16:14 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31221be2: startup date [Thu Jul 25 15:16:14 CST 2019]; root of context hierarchy
七月 25, 2019 3:16:14 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
环绕前置通知,执行了
前置增强,执行了
Hello!
环绕后置通知,执行了
最终增强,执行了
后置增强,执行了

Process finished with exit code 0

有异常运行结果

七月 25, 2019 3:19:44 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31221be2: startup date [Thu Jul 25 15:19:44 CST 2019]; root of context hierarchy
七月 25, 2019 3:19:45 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
环绕前置通知,执行了
前置增强,执行了
最终增强,执行了
异常增强,执行了

Process finished with exit code 0