赞
踩
1.3 在Spring配置文件中声明AspectJ的自动代理生成器
承接了上一篇文章:Spring——AOP基本概念的理解_spring aop概念-CSDN博客
这篇文章中,主要介绍一下通过 AspectJ 框架更好的理解和使用 AOP 的5大通知注解。
在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签,均是 AspectJ 框架使用的,而非 Spring 框架本身在实现 AOP 时使用的。AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。
使用apsectj框架的注解,实现前置通知,步骤如下: 1.新建Maven项目 2.修改pom.xml,加入依赖 spring-context依赖、spring-aspects依赖、junit 3.创建业务接口和实现类 4.创建一个切面类(普通类) 1) 在类的上面加入@Aspect 2) 在类中定义方法,方法表示切面的功能。在方法的上面加入AspectJ框架中的通知注解 例如:@Before(value="切入点表达式") 5.创建spring配置文件 1) 声明目标对象 2) 声明切面类对象 3) 声明自动代理生成器 6.创建测试类,测试目标方法执行时,增加切面的功能
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>5.2.5.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-aspects</artifactId>
- <version>5.2.5.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.11</version>
- <scope>test</scope>
- </dependency>
- <?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 https://www.springframework.org/schema/aop/spring-aop.xsd">
-
- <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
- <aop:aspectj-autoproxy />
- </beans>
/** * 前置通知方法的定义 * 1) 方法是public * 2) 返回值是void * 3) 方法名称自定义 * 4) 可以有参数,也可以无参数。如果有,参数是JoinPoint * * @Before: 前置通知 * 属性: value 切入点表达式,表示切面的执行位置。在这个方法执行时,会同时执行切面的功能 * 位置: 放在方法的上面 * 特点: 1) 执行时间在目标方法之前先执行 * 2) 不会影响目标方法的执行 * 3) 不会修改目标方法的执行结果 * 切面类中的通知方法,可以有参数。必须是JoinPoint * JoinPoint: 表示正在执行的业务方法,相当于反射中的Method * 使用要求: 必须是参数列表的第一个 * 作用: 获取方法执行时的信息,例如方法名称、方法的参数集合 */
- package com.bjpowernode.service;
-
- /**
- *
- */
- public interface SomeService {
-
- void doSome(String name,Integer age);
-
- void doOther();
- }
- package com.bjpowernode.service.impl;
-
- import com.bjpowernode.service.SomeService;
-
- public class SomeServiceImpl implements SomeService {
- @Override
- public void doSome(String name, Integer age) {
- System.out.println("业务方法doSome(),创建商品的订单");
- }
-
- @Override
- public void doOther() {
- System.out.println("业务方法doOther()");
- }
- }
- package com.bjpowernode.handle;
-
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
-
- import java.util.Date;
-
- /**
- * @Aspect: 切面类的注解
- * 位置: 放在某个类的上面
- * 作用: 表示当前类是切面类
- */
- @Aspect
- public class MyAspect {
-
- @Before(value = "execution(public void com.bjpowernode.service.impl.SomeServiceImpl.do*(..))")
- public void myBefore(JoinPoint joinPoint) {
- //获取方法的完整定义
- System.out.println("前置通知,获取目标方法的定义: " + joinPoint.getSignature());
- //获取方法的名称
- System.out.println("前置通知,获取目标方法的名称: " + joinPoint.getSignature().getName());
- //获取方法执行时的参数
- Object[] args=joinPoint.getArgs();
- for(Object obj : args) {
- System.out.println("前置通知,获取目标方法的参数: " + obj);
- }
- String methodName=joinPoint.getSignature().getName();
- if(methodName.equals("doSome")) {
- //切面的代码
- System.out.println("doSome输出日志=========前置通知,切面的功能,在目标方法之前先执行: " + new Date());
- }else if(methodName.equals("doOther")) {
- //切面的代码
- System.out.println("doOther输出日志========前置通知,作为方法名称、参数的记录");
- }
- }
- }
- <!-- 声明目标对象 -->
- <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
-
- <!-- 声明切面类对象 -->
- <bean id="myAspect" class="com.bjpowernode.handle.MyAspect"/>
-
- <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
- <aop:aspectj-autoproxy />
- @Test
- public void test01() {
- String config="applicationContext.xml";
- ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
- SomeService service= (SomeService) ctx.getBean("someService");
- System.out.println("service === " + service.getClass().getName());
- service.doSome("张起灵",20);
- //service.doOther();
- }
/** * 后置通知方法的定义 * 1) 方法是public * 2) 返回值是void * 3) 方法名称自定义 * 4) 方法有参数,推荐使用Object * * @AfterReturning: 后置通知 * 属性: value 切入点表达式 * returning 自定义的变量,表示目标方法的返回值的返回值。 * 自定义变量的名称必须和通知方法的形参名一样 * 位置: 放在方法的上面 * 特点: 1) 在目标方法之后执行 * 2) 能获取到目标方法的执行结果 * 3) 不会影响目标方法的执行 * 方法的参数Object res,表示目标方法的返回值,使用res接收doOther的调用结果 */
- package com.bjpowernode.service;
-
- /**
- *
- */
- public interface SomeService {
-
- String doOther(String name,Integer age);
- }
- package com.bjpowernode.service.impl;
-
- import com.bjpowernode.service.SomeService;
-
- public class SomeServiceImpl implements SomeService {
-
- @Override
- public String doOther(String name, Integer age) {
- System.out.println("执行业务方法doOther(),处理库存");
- return "abcd";
- }
- }
- package com.bjpowernode.handle;
-
- import org.aspectj.lang.annotation.AfterReturning;
- import org.aspectj.lang.annotation.Aspect;
-
- /**
- * @Aspect: 切面类的注解
- * 位置: 放在某个类的上面
- * 作用: 表示当前类是切面类
- */
- @Aspect
- public class MyAspect {
-
- @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",
- returning = "res")
- public void myAfterReturning(Object res) {
- System.out.println("后置通知,在目标方法之后执行的,可以拿到执行结果" + res);
- }
- }
- <!-- 声明目标对象 -->
- <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
-
- <!-- 声明切面类对象 -->
- <bean id="myAspect" class="com.bjpowernode.handle.MyAspect"/>
-
- <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
- <aop:aspectj-autoproxy />
- @Test
- public void test01() {
- String config="applicationContext.xml";
- ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
- SomeService service= (SomeService) ctx.getBean("someService");
- System.out.println("service === " + service.getClass().getName());
- service.doOther("张三",25);
- }
/** * 环绕通知方法的定义 * 1) 方法是public * 2) 必须有返回值,推荐使用Object类型 * 3) 方法名称自定义 * 4) 必须有ProceedingJoinPoint参数 * * @Around: 环绕通知 * 属性: value 切入点表达式 * 位置: 在方法定义的上面 * 测试方法中,调用目标方法doFirst(String name)去执行, * 实际上目标方法doFirst(String name)并未执行,而是执行了myAround(ProceedingJoinPoint proceedingJoinPoint) * 特点: 1) 在目标方法的前和后都能增强功能 * 2) 控制目标方法是否执行 * 3) 修改目标方法的执行结果 */
- package com.bjpowernode.service;
-
- /**
- *
- */
- public interface SomeService {
-
- String doFirst(String name);
- }
- package com.bjpowernode.service.impl;
-
- import com.bjpowernode.service.SomeService;
-
- public class SomeServiceImpl implements SomeService {
-
- @Override
- public String doFirst(String name) {
- System.out.println("执行业务方法doFirst(),处理库存");
- return "doFirst";
- }
- }
- package com.bjpowernode.handle;
-
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
-
- import java.util.Date;
-
- /**
- * @Aspect: 切面类的注解
- * 位置: 放在某个类的上面
- * 作用: 表示当前类是切面类
- */
- @Aspect
- public class MyAspect {
-
- @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
- public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
- //获取方法执行时的参数值
- String name="";
- Object[] args=proceedingJoinPoint.getArgs();
- if(args!=null && args.length>0) {
- Object arg=args[0];
- if(arg!=null) {
- name=(String)arg;
- }
- }
- System.out.println("执行了环绕通知,在目标方法之前,输出日志时间=== " + new Date());
- Object methodReturn=null;
- if(name.equals("李四")) {
- //相当于执行目标方法doFirst(String name)
- methodReturn = proceedingJoinPoint.proceed();
- }
- System.out.println("执行了环绕通知,在目标方法之后,增加了事务功能");
- return methodReturn;
- }
- }
- <!-- 声明目标对象 -->
- <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
-
- <!-- 声明切面类对象 -->
- <bean id="myAspect" class="com.bjpowernode.handle.MyAspect"/>
-
- <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
- <aop:aspectj-autoproxy />
- @Test
- public void test01() {
- String config="applicationContext.xml";
- ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
- SomeService service= (SomeService) ctx.getBean("someService");
- System.out.println("service === " + service.getClass().getName());
- String ret=service.doFirst("李四");
- System.out.println("ret调用目标方法的结果 === " + ret);
- }
/** * 异常通知方法的定义 * 1) 方法是public * 2) 返回值是void * 3) 方法名称自定义 * 4) 方法有参数Exception * * @AfterThrowing: 异常通知 * 属性: value 切入点表达式 * throwing 自定义变量,表示目标方法抛出的异常。变量名必须和通知方法的形参名一样 * 位置: 在方法上面 * 特点: 1) 在目标方法抛出异常之后执行的,若没有异常 则不执行 * 2) 能获取到目标方法的异常信息 * 3) 不是异常处理程序,可以得到发生异常的通知,可以发送邮件、短信通知开发人员 * 可以看作是目标方法的监控程序 * 可以看作以下语句块: * try { * SomeServiceImpl.doSecond(..) * }catch(Exception ex) { * myAfterThrowing(Exception ex) * } */
- package com.bjpowernode.service;
-
- /**
- *
- */
- public interface SomeService {
-
- void doSecond(String name);
- }
- package com.bjpowernode.service.impl;
-
- import com.bjpowernode.service.SomeService;
-
- public class SomeServiceImpl implements SomeService {
-
- @Override
- public void doSecond(String name) {
- System.out.println("执行业务方法doSecond(),处理库存" + (10/0));
- }
- }
- package com.bjpowernode.handle;
-
- import org.aspectj.lang.annotation.AfterThrowing;
- import org.aspectj.lang.annotation.Aspect;
-
- /**
- * @Aspect: 切面类的注解
- * 位置: 放在某个类的上面
- * 作用: 表示当前类是切面类
- */
- @Aspect
- public class MyAspect {
-
- @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing = "ex")
- public void myAfterThrowing(Exception ex) {
- System.out.println("异常通知,在目标方法抛出异常时执行的,异常原因是:" + ex.getMessage());
- }
- }
- <!-- 声明目标对象 -->
- <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
-
- <!-- 声明切面类对象 -->
- <bean id="myAspect" class="com.bjpowernode.handle.MyAspect"/>
-
- <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
- <aop:aspectj-autoproxy />
/** * 最终通知方法的定义 * 1) 方法是public * 2) 返回值是void * 3) 方法名称自定义 * 4) 方法无参数 * * @After: 最终通知 * 属性: value 切入点表达式 * 位置: 在方法上面 * 特点: 1) 在目标方法之后执行的 * 2) 总是会被执行 * 3) 可以用来做程序最后的收尾工作,例如清除临时数据、变量,清理内存 * 可以看作如下语句块: * try { * SomeServiceImpl.doThird(..) * }finally { * myAfter() * } */
- package com.bjpowernode.service;
-
- /**
- *
- */
- public interface SomeService {
-
- void doThird();
- }
- package com.bjpowernode.service.impl;
-
- import com.bjpowernode.service.SomeService;
-
- public class SomeServiceImpl implements SomeService {
-
- @Override
- public void doThird() {
- System.out.println("执行业务方法doThird()");
- }
- }
- package com.bjpowernode.handle;
-
- import org.aspectj.lang.annotation.After;
- import org.aspectj.lang.annotation.Aspect;
-
- /**
- * @Aspect: 切面类的注解
- * 位置: 放在某个类的上面
- * 作用: 表示当前类是切面类
- */
- @Aspect
- public class MyAspect {
-
- @After(value = "execution(* *..SomeServiceImpl.doThird(..))")
- public void myAfter() {
- System.out.println("最终通知,总是会被执行的");
- }
- }
- <!-- 声明目标对象 -->
- <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
-
- <!-- 声明切面类对象 -->
- <bean id="myAspect" class="com.bjpowernode.handle.MyAspect"/>
-
- <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
- <aop:aspectj-autoproxy />
- @Test
- public void test01() {
- String config="applicationContext.xml";
- ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
- SomeService service= (SomeService) ctx.getBean("someService");
- System.out.println("service === " + service.getClass().getName());
- service.doThird();
- }
当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。AspectJ 提供了 @Pointcut 注解,用于定义 execution 切入点表达式。
其用法是,将 @Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用 @Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。
下面的代码只给出切面类:
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/911062
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。