4. AOP 面向切面编程
4.1. 介绍
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*(..) throws | cn.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(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. 实现的过程
- 添加依赖
<?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>
定义两个类UserService、StuService 接口和实现类
使用IOC容器管理Bean
4.4.3. Xml的形式配置AOP
创建新的工程,并将上一个工程的Service四个类拿过来。并使用bean工厂管理service对象
实现的步骤
- 编写目标类
- 编写增强代码(通知) advice
- 配置切面
- 切点 execution com.xxx.service.xxx(..)
- 通知: before
- 从容器中获取某一个Service验证是否有增强的代码被执行了
1 目标类
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");
}
}
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. 定义增强(通知)
定义类,并准备方法
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的配置文件
<?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的目标类
<context:component-scan base-package="com.neuedu.aop"></context:component-scan>
2 使用注解定义AOP的增强(通知)AopAdvice
1. 切点(PointCut)
//定义一个切点,insert方法
@Pointcut("execution(* com.neuedu.service.*.insert(..))")
public void pointcut(){}
2. 增强(通知)-扩展代码
定义一个方法(想要在insert之前或者之后执行的代码)
前置通知
//定义一个需要在insert方法之前指定的逻辑
@Before("pointcut()")
public void before(){
System.out.println("开启事务..代表了一些逻辑");
}
3. 切面(类最终的代码)
使用@Aspectj注解让类中的切点和通知整合到一起 同时需要切面类是 容器中的 Bean 添加@Component
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"/>
- 配置IOC使用切面生成对应的代理对象
<!--使用aop:aspectj-autoproxy 对应的目标对象生成代理对象-->
<!--用于扫描 Aspect 切面注解-->
<aop:aspectj-autoproxy/>
5 完整的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. 单元测试
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();
}
}
4.4.5 使用Java配置类,替换xml
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. 通知的代码
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中的声明
<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. 使用注解的形式
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("正常返回了,");
}
}
