AspectJ
4.6.2 在 Spring 中启用 AspectJ 注解支持• 要在 Spring 应用中使用 AspectJ 注解, 必须在 classpath 下包含 AspectJ 类库.• 将 Schema 下的 aop 添加到 beans 根元素中.• 要在 Spring IOC 容器中启用 AspectJ 注解支持, 只要在 Bean 配置文件中定义一个空的 XML 元素 aop:aspectj-autoproxy, 称之为AOP的自动代理• 当 Spring IOC 容器侦测到 Bean 配置文件中的 aop:aspectj-autoproxy 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理.4.6.3 用 AspectJ 注解声明切面• 要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理.• 在 AspectJ 注解中, 切面只是一个带有 Aspect 注解的 Java 类.• 通知是标注有某种注解的简单的 Java 方法.• AspectJ 支持 5 种类型的通知注解:– Before: 用于配置前置通知。指定增强的方法在切入点方法(⽬标⽅法)之前执⾏.– After: 用于配置后置通知。指定增强的方法在切入点方法(⽬标⽅法)之后执⾏, ⽆论是否有异常都会执⾏.– AfterReturning: 用于配置返回后通知。指定增强的方法在切入点方法(⽬标⽅法)之后执⾏, 有异常不会执⾏.– AfterThrowing:用于配置异常抛出通知。指定增强的方法在出现异常时执行.– Around: 用于配置环绕通知。指定增强的方法在切入点方法(⽬标⽅法)之前和之后都执行.通知的配置语法通知注解(“切点表达式)4.6.4 切入点表达式说明表达式语法execution([修饰符] 返回值类型 包名.类名.方法名(参数))• 访问修饰符可以省略(修饰符加了中括号, 代表可以省略)• 返回值类型、包名、类名、方法名可以使用星号 * 代表任意• 包名与类名之间一个点 . 代表当前包下的类两个点 .. 表示当前包及其子包下的类• 参数列表可以使用两个点 .. 表示任意个数任意类型的参数列表(如果参数有一个就写一个, 有两个就写两个, 不过可以使用 .. 表示任意个数)例如:全匹配方式:execution(public void com.sy.aop.Target.method(参数))访问修饰符可以省略:execution( void com.sy.aop.Target.method(com.sy.pojo.User))返回值可以使用*号表示任意返回值:execution( * com.sy.aop.Target.method(com.sy.pojo.User))包名可以使用*号表示任意包但是有几级包需要写几个*execution( * *.*.*.Target.method(com.sy.pojo.User))使用..来表示当前包及其子包execution( * com..Target.method(com.sy.pojo.User))类名可以使用*号表示任意类execution( * com..*.method(com.sy.pojo.User))方法名可以使用*号表示任意方法execution( * com..*.*(com.sy.pojo.User))参数列表可以使用*表示参数可以是任意数据类型但是必须有参数execution( * com..*.*(*))参数列表可以使用..表示有无参数均可有参数可以是任意类型execution( * com..*.*(..))全通配方式execution(* *..*.*(..))注意通常情况下我们都是对业务层的方法进行增强所以切入点表达式都是切到业务层实现类。execution(* com.sy.service.impl.*.*(..))五、Spring基于注解的AOP配置基于注解的aop开发步骤1. 创建目标接口和目标类内部有切点2. 创建切面类内部有增强方法3. 将目标类和切面类的对象创建权交给 spring4. 在切面类中使用注解配置织入关系5. 在配置文件中开启组件扫描和 AOP 的自动代理6. 测试程序构建Maven工程并添加依赖properties maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding spring.version5.2.5.RELEASE/spring.version /properties dependencies dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency dependency groupIdorg.aspectj/groupId artifactIdaspectjweaver/artifactId version1.8.7/version /dependency /dependencies 创建 Spring 的配置文件并导入约束?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:aophttp://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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd !--开启注解扫描器-- context:component-scan base-packagecom.study/context:component-scan !--配置基于注解的AspactJ(AOP的自动代理)-- aop:aspectj-autoproxy /aop:aspectj-autoproxy /beans前置后置 环绕 异常// 切面类 Component //创建当前对象并存入IOC容器 Aspect //标注当前MyAspect类是一个切面类 Order(3) public class MyAspect { /** * 抽取切点表达式(重用切入点表达式): 根据方法名复用即可 * 声明切入点 */ Pointcut(execution(* com.sy.service.*.*(..))) public void myPointcut(){ } /** * 在add方法执行前执行beforeMethod方法 * 目标方法前执行的通知前置通知 * JoinPoint: 连接的对象该对象包含了与目标方法相关的一些信息 * execution: 配置切入点表达式指定切入点 */ // Before(execution(public int com.sy.service.impl.CalculatorServiceImpl.add(int,int))) Before(myPointcut()) public void beforeMethod(JoinPoint joinPoint){ //获取目标方法名 String method joinPoint.getSignature().getName(); //获取目标方法的参数 Object[] args joinPoint.getArgs(); System.out.println(MyAspect 这是一个beforeMethod方法, 在 method 方法前执行了, 参数有 Arrays.asList(args)); } /** * 目标方法后执行的通知后置通知 * 1.目标方法不管有没有执行异常后置通知都会执行 * 2.后置通知获取不到目标方法的返回值 */ // After(execution(* com.sy.service.impl.CalculatorServiceImpl.*(..))) After(myPointcut()) public void afterMethod(JoinPoint joinPoint){ //获取目标方法名 String method joinPoint.getSignature().getName(); //获取目标方法的参数 Object[] args joinPoint.getArgs(); System.out.println(MyAspect 这是一个afterMethod方法, 在 method 方法后执行了, 参数有 Arrays.asList(args)); } /** * 目标方法正常执行结束后返回通知 * returning用于指定接收目标方法的返回值必须与通知方法的形参名一致 */ // AfterReturning(value execution(* com.sy.service.*.*(..)),returning result) AfterReturning(value myPointcut(), returning result) public void afterReturningMethod(JoinPoint joinPoint, Object result){ //获取方法名 String method joinPoint.getSignature().getName(); //获取方法的参数 Object[] args joinPoint.getArgs(); System.out.println(MyAspect 这是一个afterReturningMethod方法, 在 method 方法返回结果后执行了, 参数有 Arrays.asList(args) , 结果是 result); } /** * 目标方法抛出异常后执行异常通知 * throwing用于指定接收目标方法的异常信息必须与通知方法的形参名一致 */ // AfterThrowing(value execution(* com.sy.service.*.*(..)), throwing e) AfterThrowing(value myPointcut(), throwing e) public void afterThrowingMethod(JoinPoint joinPoint, Exception e){ //获取方法名 String methodjoinPoint.getSignature().getName(); //获取方法的参数 Object[] args joinPoint.getArgs(); System.out.println(MyAspect 这是一个afterThrowingMethod方法, 在 method 方法执行异常后执行了, 参数有 Arrays.asList(args) , 异常是 e); } /** * 环绕着整个目标方法执行环绕通知 * 环绕通知综合了前置 后置 返回 异常 四个通知的功能 */ // Around(execution(* com.sy.service.impl.CalculatorServiceImpl.*(..))) Around(myPointcut()) public Object aroundMethod(ProceedingJoinPoint point){ try { // 1.目标方法前执行的通知前置通知 String method point.getSignature().getName(); Object[] args point.getArgs(); System.out.println(前置通知 目标方法为 method -- 参数有: Arrays.asList(args)); //point.proceed()执行目标方法 Object result point.proceed(); // 3.返回通知 System.out.println(返回通知 目标方法为 method -- 参数有: Arrays.asList(args) -- 结果为: result); return result; } catch (Throwable throwable) { throwable.printStackTrace(); // 4.异常通知 System.out.println(异常通知 目标方法为 point.getSignature().getName() , 异常是: throwable); }finally { // 2.目标方法后执行的通知后置通知 String method point.getSignature().getName(); Object[] args point.getArgs(); System.out.println(后置通知 目标方法为: method -- 参数有: Arrays.asList(args)); } return null; } }