Skip to content

4. AOP 面向切面编程

4.1. 介绍

img

4.2. 代理机制

Spring AOP使用了两种代理机制:一种是基于JDK的动态代理;另一种是基于CGLib的动态代理。之所以需要两种代理机制,是因为JDK本身只提供接口的代理,而不支持类的代理。

4.2.1. JDK动态代理

Spring的AOP的默认实现就是采用jdk的动态代理机制实现的。

自Java1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例。

4.2.2. CGLib代理

JDK只能为接口创建代理实例,对于那些没有通过接口定义业务方法的类,可以通过CGLib创建代理实例。

CGLib采用底层字节码技术,可以为一个类创建子类,并在子类中采用方法拦截技术拦截所有父类方法的调用,这时可以顺势织入横切逻辑。

4.3. AOP的概念

4.3.1. 切面(Aspect)

切面是切点和通知组成,通知和切点共同定义了切面的全部内容即:它是什么,在何时何处完成其功能;

举例:将pointcut和Advice配置整合的过程切面

4.3.2. 连接点(Joinpoint)

连接点是在应用执行过程中能够插入切面的一个点,Spring仅支持方法的连接点,即仅能在方法调用前,方法调用后,方法抛出异常时及方法调用前后插入切面代码。 .

举例:如果在insert之前执行扩展(增强、通知),简单理解insert这个方法可以称之为连接点。

连接点: 1 Com.neuedu.service.StuServiceImpl.insert()

连接点: 2 Com.neuedu.service.UserServiceImpl.insert()

4.3.3. 切点(Pointcut)

Supported Pointcut Designators

Spring AOP supports the following AspectJ pointcut designators (PCD) for use in pointcut expressions:

  • execution: For matching method execution join points. This is the primary pointcut designator to use when working with Spring AOP.

  • within: Limits matching to join points within certain types (the execution of a method declared within a matching type when using Spring AOP).

  • this: Limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type.

  • target: Limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type.

  • args: Limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types.

  • @target: Limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type.

  • @args: Limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given types.

  • @within: Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP).

  • @annotation: Limits matching to join points where the subject of the join point (the method being run in Spring AOP) has the given annotation.

切点定义了在何处应用切面,AOP通过“切点”定位特定的连接点。切点相当于查询条件,一个切点可以匹配多个连接点。

举例:多个insert方法都需要进行扩展,统一一次配置,将多个连接点一起匹配称之为切点

切点匹配:Com.neuedu.service.*Impl.insert(..);

表达式:

Execution表达式语法

execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)

除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。

execution(public * *(..))

匹配所有目标类的public方法,第一个代表返回类型,第二个代表方法名,而..代表任意入参的方法;

execution(* *To(..))

匹配目标类所有以To为后缀的方法,第一个代表返回类型,而To代表任意以To为后缀的方法;

execution(* com.neuedu.UserService.*(..))

匹配UserService接口的所有方法,第一个*代表返回任意类型,com.neuedu.UserService.*代表UserService接口中的所有方法;

execution(* com.neuedu.*(..))

匹配com.neuedu包下所有类的所有方法

execution(* com.neuedu..*(..))

匹配com.neuedu包、子孙包下所有类的所有方法,“..”出现在类名中时,后面必须跟“*”,表示包、子孙包下的所有类;

execution(* com..*.Dao.find(..))

匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀

一、execution:使用“execution(方法表达式)”匹配方法执行;

模式描述
public * *(..)任何公共方法的执行
* cn.javass..IPointcutService.*()cn.javass包及所有子包下IPointcutService接口中的任何无参方法
* cn.javass..*.*(..)cn.javass包及所有子包下任何类的任何方法
* cn.javass..IPointcutService.*(*)cn.javass包及所有子包下IPointcutService接口的任何只有一个参数方法
* (!cn.javass..IPointcutService+).*(..)非“cn.javass包及所有子包下IPointcutService接口及子类型”的任何方法
* cn.javass..IPointcutService+.*()cn.javass包及所有子包下IPointcutService接口及子类型的的任何无参方法
* cn.javass..IPointcut*.test*(Java.util.Date)cn.javass包及所有子包下IPointcut前缀类型的的以test开头的只有一个参数类型为java.util.Date的方法,注意该匹配是根据方法签名的参数类型进行匹配的,而不是根据执行时传入的参数类型决定的

如定义方法:public void test(Object obj);即使执行时传入java.util.Date,也不会匹配的;
* cn.javass..IPointcut*.test*(..)  throwscn.javass包及所有子包下IPointcut前缀类型的的任何方法,且抛出IllegalArgumentException和ArrayIndexOutOfBoundsException异常
IllegalArgumentException, ArrayIndexOutOfBoundsException
* (cn.javass..IPointcutService+任何实现了cn.javass包及所有子包下IPointcutService接口和java.io.Serializable接口的类型的任何方法
&& java.io.Serializable+).*(..)
@java.lang.Deprecated * *(..)任何持有@java.lang.Deprecated注解的方法
@java.lang.Deprecated @cn.javass..Secure  * *(..)任何持有@java.lang.Deprecated和@cn.javass..Secure注解的方法
@(java.lang.Deprecated || cn.javass..Secure) * *(..)任何持有@java.lang.Deprecated或@ cn.javass..Secure注解的方法
(@cn.javass..Secure  *)  *(..)任何返回值类型持有@cn.javass..Secure的方法
*  (@cn.javass..Secure *).*(..)任何定义方法的类型持有@cn.javass..Secure的方法
* *(@cn.javass..Secure (*) , @cn.javass..Secure (*))任何签名带有两个参数的方法,且这个两个参数都被@ Secure标记了,

如public void test(@Secure String str1,

@Secure String str1);
* *((@ cn.javass..Secure *))或任何带有一个参数的方法,且该参数类型持有@ cn.javass..Secure;
* *(@ cn.javass..Secure *)如public void test(Model model);且Model类上持有@Secure注解
* *(任何带有两个参数的方法,且这两个参数都被@ cn.javass..Secure标记了;且这两个参数的类型上都持有@ cn.javass..Secure;
@cn.javass..Secure (@cn.javass..Secure *) ,
@ cn.javass..Secure (@cn.javass..Secure *))
* *(任何带有一个java.util.Map参数的方法,且该参数类型是以< cn.javass..Model, cn.javass..Model >为泛型参数;注意只匹配第一个参数为java.util.Map,不包括子类型;
java.util.Map<cn.javass..Model, cn.javass..Model>如public void test(HashMap<Model, Model> map, String str);将不匹配,必须使用“* *(
, ..)java.util.HashMap<cn.javass..Model,cn.javass..Model>

, ..)”进行匹配;

而public void test(Map map, int i);也将不匹配,因为泛型参数不匹配
* *(java.util.Collection<@cn.javass..Secure *>)任何带有一个参数(类型为java.util.Collection)的方法,且该参数类型是有一个泛型参数,该泛型参数类型上持有@cn.javass..Secure注解;

如public void test(Collection<Model> collection);Model类型上持有@cn.javass..Secure
* *(java.util.Set<? extends HashMap>)任何带有一个参数的方法,且传入的参数类型是有一个泛型参数,该泛型参数类型继承与HashMap;

Spring AOP目前测试不能正常工作
* *(java.util.List<? super HashMap>)任何带有一个参数的方法,且传入的参数类型是有一个泛型参数,该泛型参数类型是HashMap的基类型;如public voi test(Map map);

Spring AOP目前测试不能正常工作
* *(*<@cn.javass..Secure *>)任何带有一个参数的方法,且该参数类型是有一个泛型参数,该泛型参数类型上持有@cn.javass..Secure注解;

Spring AOP目前测试不能正常工作
二、within:使用“within(类型表达式)”匹配指定类型内的方法执行;

模式

描述

within(cn.javass..*)

cn.javass包及子包下的任何方法执行

within(cn.javass..IPointcutService+)

cn.javass包或所有子包下IPointcutService类型及子类型的任何方法

within(@cn.javass..Secure *)

持有cn.javass..Secure注解的任何类型的任何方法

必须是在目标对象上声明这个注解,在接口上声明的对它不起作用

三、this:使用“this(类型全限定名)”匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口方法也可以匹配;注意this中使用的表达式必须是类型全限定名,不支持通配符;

模式

描述

this(cn.javass.spring.chapter6.service.IPointcutService)

当前AOP对象实现了 IPointcutService接口的任何方法

this(cn.javass.spring.chapter6.service.IIntroductionService)

当前AOP对象实现了 IIntroductionService接口的任何方法

也可能是引入接口

四、target:使用“target(类型全限定名)”匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;注意target中使用的表达式必须是类型全限定名,不支持通配符;

模式

描述

target(cn.javass.spring.chapter6.service.IPointcutService)

当前目标对象(非AOP对象)实现了 IPointcutService接口的任何方法

target(cn.javass.spring.chapter6.service.IIntroductionService)

当前目标对象(非AOP对象) 实现了IIntroductionService 接口的任何方法

不可能是引入接口

五、args:使用“args(参数类型列表)”匹配当前执行的方法传入的参数为指定类型的执行方法;注意是匹配传入的参数类型,不是匹配方法签名的参数类型;参数类型列表中的参数必须是类型全限定名,通配符不支持;args属于动态切入点,这种切入点开销非常大,非特殊情况最好不要使用;

模式

描述

args (java.io.Serializable,..)

任何一个以接受“传入参数类型为 java.io.Serializable” 开头,且其后可跟任意个任意类型的参数的方法执行,args指定的参数类型是在运行时动态匹配的

六、@within:使用“@within(注解类型)”匹配所以持有指定注解类型内的方法;注解类型也必须是全限定类型名;

模式

描述

@within cn.javass.spring.chapter6.Secure)

任何目标对象对应的类型持有Secure注解的类方法;

必须是在目标对象上声明这个注解,在接口上声明的对它不起作用

七、@target:使用“@target(注解类型)”匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;注解类型也必须是全限定类型名;

模式

描述

@target (cn.javass.spring.chapter6.Secure)

任何目标对象持有Secure注解的类方法;

必须是在目标对象上声明这个注解,在接口上声明的对它不起作用

八、@args:使用“@args(注解列表)”匹配当前执行的方法传入的参数持有指定注解的执行;注解类型也必须是全限定类型名;

模式

描述

@args (cn.javass.spring.chapter6.Secure)

任何一个只接受一个参数的方法,且方法运行时传入的参数持有注解 cn.javass.spring.chapter6.Secure;动态切入点,类似于arg指示符;

九、@annotation:使用“@annotation(注解类型)”匹配当前执行方法持有指定注解的方法;注解类型也必须是全限定类型名;

模式

描述

@annotation(cn.javass.spring.chapter6.Secure )

当前执行方法上持有注解 cn.javass.spring.chapter6.Secure将被匹配

十、bean:使用“bean(Bean id或名字通配符)”匹配特定名称的Bean对象的执行方法;Spring ASP扩展的,在AspectJ中无相应概念;

模式

描述

bean(*Service)

匹配所有以Service命名(id或name)结尾的Bean

4.3.4. 通知(Advice)、增强

切面的工作被成为通知,定义了切面是什么及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题,它应该在某个方法被调用之前?之后?等。

举例:额外定义一个方法想要在连接点上进行扩展的功能

4.3.4.1. Spring切面可以应用5种类型的通知:

前置通知(Before)在目标方法被调用之前调用通知功能;

后置通知(After)在目标方法被完成之后调用通知功能,不关心方法的输出是什么;

环绕通知(Around advice)通知包裹了目标方法,在目标方法调用之前和之后执行自定义的行为;

异常通知(After-throwing)在目标方法抛出异常后调用通知;

返回通知(After-returning)在目标方法成功执行之后调用通知;

4.3.5. 目标对象(Target)

通知逻辑的织入目标类。如果没有AOP,那么目标业务类需要自己实现所有的逻辑,在AOP的帮助下,目标类只需要实现那些非横切逻辑的程序逻辑,而比如事务管理等这些横切逻辑就可以使用AOP动态织入特定的连接点上。

举例:两个Service即是目标对象

4.3.6. 引入(Introduction)

引介是一种特殊的通知,为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,也可以动态地为该业务类添加接口的实现逻辑,使业务类成为这个接口的实现类。

几乎由框架完成,我们可以暂时透明

4.3.7. 代理(Proxy)

一个类被AOP织入通知后,就产生一个结果类,它是融合了原类和通知逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以可以采用与调用原类相同的方式调用代理类。

举例:IOC容器创建的两个Service类型对象

4.3.8. 织入(Weaving)

织入是将通知添加到目标类的具体连接点上的过程.AOP就像一台织布机,将目标类,通知或引介编织到一起。 AOP有三种织入方式: ①编译期织入:切面在目标类编译时被织入,需要特殊的编译器; ②类装载期织入:切面在目标类加载到JVM时被织入,需要特殊的类装载器; ③动态代理织入:切面在应用运行的某个时刻呗织入,AOP容器会为目标对象动态创建一个代理对象;

对用户透明,由框架完成

4.4. AOP实战

4.4.1. 需求:

在service层的方法上添加功能:

1)在insert方法之前开启事务(使用System.out.println(‘开启事务’)代替)

4.4.2. 实现的过程

  1. 添加依赖
xml
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-framework</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.neuedu.aop</groupId>
    <artifactId>p8-spring-aop-xml</artifactId>

    <name>p8-spring-aop-xml</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <spring.version>5.2.4.RELEASE</spring.version>
        <aspectjweaver.version>1.9.6</aspectjweaver.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <!--AOP依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectjweaver.version}</version>
        </dependency>

    </dependencies>


</project>
  1. 定义两个类UserService、StuService 接口和实现类

  2. 使用IOC容器管理Bean

4.4.3. Xml的形式配置AOP

创建新的工程,并将上一个工程的Service四个类拿过来。并使用bean工厂管理service对象

img

实现的步骤

  • 编写目标类
  • 编写增强代码(通知) advice
  • 配置切面
    • 切点 execution com.xxx.service.xxx(..)
    • 通知: before
  • 从容器中获取某一个Service验证是否有增强的代码被执行了

1 目标类

java
package com.neuedu.aop.service.impl;

import com.neuedu.aop.service.IUserService;

/**
 * 项目:      spring-framework
 * 类名:       UserServiceImpl
 * 创建时间:  2024/3/13 09:33
 * 描述 :     \
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 */
public class UserServiceImpl implements IUserService {
    @Override
    public void insert() {

        System.out.println("UserServiceImpl.insert");
    }
}
java
package com.neuedu.aop.service.impl;

import com.neuedu.aop.service.IStudentService;

/**
 * 项目:      spring-framework
 * 类名:       UserServiceImpl
 * 创建时间:  2024/3/13 09:33
 * 描述 :     
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 */
public class StudentServiceImpl implements IStudentService {
    @Override
    public void insert() {
        System.out.println("StudentServiceImpl.insert");
    }
}

2. 定义增强(通知)

定义类,并准备方法

java
package com.neuedu.aop.advice;


public class AopAdvice {

    public void before(){
        System.out.println("使用xml的形式,声明的前置通知");
    }
    public void after(){
        System.out.println("使用xml的形式,声明后置的通知 ");
    }
}

在xml中配置AOP

Pointcut和advice整合的切面 aspectj

1)在xml中声明通知的Bean

2)整合切面

3. 最终Spring-aop-xml的配置文件

java
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd  http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">


    <bean class="com.neuedu.aop.service.impl.UserServiceImpl"></bean>
    <bean class="com.neuedu.aop.service.impl.StudentServiceImpl"></bean>


    <!--    使用容器管理 包含通知方法的对象-->
    <bean id="aopAdvice" class="com.neuedu.aop.advice.AopAdvice"></bean>


    <!--    配置切面-->
   <aop:config>
        <!--       全局配置切点-->
        <aop:pointcut id="pointcut" expression="execution(* com.neuedu.aop.service.*.*(..))"/>
        <aop:aspect ref="aopAdvice">
            <!--            前置通知-->
            <aop:before method="before" pointcut-ref="pointcut"></aop:before>
            <!--            后置通知-->
            <aop:after method="after" pointcut-ref="pointcut"></aop:after>
        </aop:aspect>

   </aop:config>

</beans>

4.4.4 使用注解的形式声明Bean

  • 在xml 使用bean的基础上修改
  • 使用包扫描的形式声明Bean
  • 使用注解的形式定义切点 @PointCut
  • 配置切面

1. 使用包扫描bean

在xml中使用componet-scan扫描声明了 @Service的目标类

xml
    <context:component-scan base-package="com.neuedu.aop"></context:component-scan>

2 使用注解定义AOP的增强(通知)AopAdvice

1. 切点(PointCut)
java
//定义一个切点,insert方法
@Pointcut("execution(* com.neuedu.service.*.insert(..))")
public void pointcut(){}
2. 增强(通知)-扩展代码

定义一个方法(想要在insert之前或者之后执行的代码)

前置通知

java
//定义一个需要在insert方法之前指定的逻辑
@Before("pointcut()")
public void before(){
    System.out.println("开启事务..代表了一些逻辑");
}
3. 切面(类最终的代码)

使用@Aspectj注解让类中的切点和通知整合到一起 同时需要切面类是 容器中的 Bean 添加@Component

java
package com.neuedu.aop.advice;


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

@Aspect
@Component
public class AopAdvice {



//    定义切点  方法名就是切点的id
    @Pointcut("execution(* com.neuedu.aop.service.*.*(..))")
    public void pointcut(){  }





    @Before("pointcut()")
    public void before(){
        System.out.println("使用xml的形式,声明的前置通知");
    }

    @After("pointcut()")
    public void after(){
        System.out.println("使用xml的形式,声明后置的通知 ");
    }
}
4. 准备织入的逻辑

1 ) 将切面类配置到IOC容器中,让IOC管理Bean,注解或者bean标签

<bean id="aopAdvice" class="com.neuedu.aop.AopAdvice"/>

  1. 配置IOC使用切面生成对应的代理对象
xml
<!--使用aop:aspectj-autoproxy 对应的目标对象生成代理对象-->
<!--用于扫描 Aspect 切面注解-->
<aop:aspectj-autoproxy/>
5 完整的xml
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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd  http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


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


    <!--    使用容器管理 包含通知方法的对象-->
<!--    扫描  Aspect 切面注解,让定义的切面生效-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>
6. 单元测试
java
package com.neuedu.aop.service;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * 项目:      spring-framework
 * 类名:       IUserServiceTest
 * 创建时间:  2024/3/13 09:35
 * 描述 :
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-aop.xml")
public class IUserServiceTest {

    @Autowired
    IUserService userService;
    @Test
    public void insert() {
        userService.insert();
    }
}

image-20240313111804796

4.4.5 使用Java配置类,替换xml

java
package com.neuedu.aop;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * 项目:      spring-framework
 * 类名:       AppConfig
 * 创建时间:  2024/3/13 11:48
 * 描述 :
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 */
@Configuration
@ComponentScan("com.neuedu.aop")
@EnableAspectJAutoProxy
public class AppConfig {
}

4.5. 各种类型的通知

4.5.1. 通知的代码

java
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 项目    : spring-java1
 * 创建时间 :2020/3/24  9:58 24
 * author  :张金山
 * site    :   https://jshand.gitee.io
 * 描述     : 增强的通知
 */
public class AopAdvice {

    public void before(){
        System.out.println("使用xml的形式,[前置]声明的通知");
    }


    public void after(){ System.out.println("使用xml的形式,[后置]声明的通知");
    }

    public void afterReturn(){ System.out.println("使用xml的形式,[返回通知]声明的通知");
    }


    public void afterEx(Exception ex){
        System.out.println(ex);
        System.out.println("使用xml的形式,[异常通知]声明的通知");
    }


//    环绕通知  
    public void around(ProceedingJoinPoint pjp){
        //之前
        //insert 切点
        System.out.println("aroud    start ...........");
        try {
            pjp.proceed();  //insert方法
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("aroud    end ...........");
        //之后
    }
}

4.5.2. Xml中的声明

xml
<aop:config>
        <!--配置pointcut-->
<!--        <aop:pointcut id="serviceInsert" expression="execution(* com.neuedu.service.*.insert(..))"/>-->
        <!--配置切面的应用-->

        <aop:aspect ref="aopAdvice">
            <!--将通知应用到切点(pointcut)中-->
            <!--aop:before将切点和通知进行整合-->
<!--            <aop:before method="before"  pointcut="execution(* com.neuedu.service.*.insert(..))"/>-->
<!--            <aop:after method="after"  pointcut="execution(* com.neuedu.service.*.insert(..))"/>-->
<!--            <aop:after-returning method="afterReturn"  pointcut="execution(* com.neuedu.service.*.insert(..))"/>-->
<!--            <aop:after-throwing method="afterEx" throwing="ex"   pointcut="execution(* com.neuedu.service.*.insert(..))"/>-->
            <aop:around method="around"  pointcut="execution(* com.neuedu.service.*.insert(..))"/>
        </aop:aspect>
    </aop:config>

4.5.3. 使用注解的形式

java
package com.neuedu.aop.advice;


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

@Aspect
@Component
public class AopAdvice {



//    定义切点  方法名就是切点的id
    @Pointcut("execution(* com.neuedu.aop.service.*.*(..))")
    public void pointcut(){  }


    @Before("pointcut()")
    public void before(){
        System.out.println("使用xml的形式,声明的前置通知");
    }

    @After("pointcut()")
    public void after(){
        System.out.println("使用xml的形式,声明后置的通知 ");
    }





    // 定义了一个切点,想要给Controller的方法进行增强
    @Pointcut("execution(* com.neuedu.aop.controller.*.*(..))")
    public void controllerPointCut(){  }


//    <aop:before
    //前置通知
//    @Before("controllerPointCut()")
//    public void beforeController(){
//        System.out.println("方法即将执行  before");
//    }

//    @After("controllerPointCut()")  //无论是否抛异常都会 执行的
//    public void afterController(){
//        System.out.println("方法即将执行  after");
//    }

//    @Around("controllerPointCut()")  //无论是否抛异常都会 执行的
//    public void aroundController(ProceedingJoinPoint joinpoint ){
//        //手动的控制  切点的执行情况
//
//        //签名
//        Signature signature = joinpoint.getSignature();
//        Object[] args = joinpoint.getArgs();
//        Object target = joinpoint.getTarget();
//        Object aThis = joinpoint.getThis();
//
//        try {
//            //方法开始执行
//            System.out.println("方法即将执行  before");
//            joinpoint.proceed();
//            System.out.println("方法执行成功了, 类似于after");
//        } catch (Throwable e) {
//            System.out.println("方法执行失败了, 类似于Throwing");
//            throw new RuntimeException(e);
//        }
//
//    }


//    @AfterThrowing(value="controllerPointCut()" ,throwing = "ex")
//    public void afterThrowing(Exception ex ){
//
//        System.out.println("方法执行失败了了产生了异常");
//        ex.printStackTrace();
//
//    }



//    正常返回时才执行,出现异常后不执行
    @AfterReturning(value="controllerPointCut()" )
    public void afterReturing(){

        System.out.println("正常返回了,");


    }



}

Released under the MIT License.