08.spring-aop示例

60 阅读3分钟

目的

示例目标为:对所有service类中带有add开头的方法进行功能扩展。从而演示前置通知、后置通知、返回通知、异常通知、环绕通知具体在方法的什么时间点执行。

POM引用

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${spring.version}</version>
</dependency>

前面说到了springaop和AspectJ的关系。所以在引入sprng-aspects时,连带着依赖了AspectJ的jar包,如下图:

image-20230303084738215

注解方式实现

1. 引入依赖

<properties>
    <spring.version>5.2.22.RELEASE</spring.version>
    <lombok.version>1.18.26</lombok.version>
    <junit.version>4.13.2</junit.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>

2. 准备要代理的类

public interface UserServcie {
    /**
     * 添加用户
     *
     * @param userId 用户id
     * @param userName 用户名称
     * @return 用户id
     */
    int addUser(int userId, String userName);
}
@Service
@AllArgsConstructor
public class UserServiceImpl implements UserServcie {
    private final UserMapper userMapper;
​
    /**
     * 添加用户
     *
     * @param userId 用户id
     * @param userName 用户姓名
     * @return 用户id
     */
    public int addUser(int userId, String userName) {
        return this.userMapper.insertUser(userId, userName);
    }
}
public interface DeptService {
​
    /**
     * 添加部门
     *
     * @param deptId 部门id
     * @param deptName 部门名称
     * @return 部门id
     */
​
    int addDept(int deptId, String deptName);
}
@Service
@AllArgsConstructor
public class DeptServiceImpl implements DeptService {
​
    private final DeptMapper deptMapper;
​
    /**
     * 添加部门
     *
     * @param deptId   部门id
     * @param deptName 部门名称
     * @return 部门id
     */
    public int addDept(int deptId, String deptName) {
        int retDeptId = this.deptMapper.insertDept(deptId, deptName);
        retDeptId = retDeptId / 0;
        return retDeptId;
    }
}

3. 切面方法

@Component
@Aspect
public class AddAspect {
    /**
     * 定义切点
     */
    @Pointcut("execution(* com.mayuanfei.springaop.service..add*(..))")
    public void addPointCut(){
    }
​
    /**
     * 前置通知 切点方法执行前执行的功能
     *
     * @param joinPoint 切点对象,可以获取方法执行参数
     */
    @Before("addPointCut()")
    public void methodBefore(JoinPoint joinPoint) {
        System.out.println("前置通知执行:" + joinPoint.getTarget().getClass());
    }
​
    /**
     * 后置通知 方法执行后要增强的功能
     *
     * @param joinPoint 切点对象
     */
    @After("addPointCut()")
    public void methodAfter(JoinPoint joinPoint) {
        System.out.println("后置通知执行:" + joinPoint.getTarget().getClass());
    }
​
    /**
     * 返回通知:切点方法正常运行结束后增强的功能
     * 如果方法运行过程中出现异常,则该功能不运行
     *
     * @param joinPoint 切点对象
     * @param res res接收方法返回值,需要用returning指定返回值名称
     */
    @AfterReturning( value = "addPointCut()",returning = "res")
    public void methodAfterReturning(JoinPoint joinPoint,Object res){
        System.out.println("返回通知执行:" + joinPoint.getTarget().getClass() + ", 返回结果:"+res);
    }
​
    /**
     * 异常通知:切点方法出现异常时运行的增强功能
     * 如果方法运行没有出现异常,则该功能不运行
     *
     * @param joinPoint 切点对象
     * @param e 接收异常对象 需要通过throwing指定异常名称
     */
    @AfterThrowing( value = "addPointCut()",throwing = "e")
    public void methodAfterThrowing(JoinPoint joinPoint, Exception e){
        System.out.println("异常通知执行:" + joinPoint.getTarget().getClass() + ", 异常信息:"+e.getMessage());
    }
​
    /**
     * 环绕通知 在切点方法之前和之后都进行功能的增强
     * 方法列表可以通过ProceedingJoinPoint获取执行的切点
     * 通过proceedingJoinPoint.proceed()方法控制切点方法的执行位置
     * 在环绕通知的最后需要将切点方法的返回值继续向上返回,否则切点方法在执行时接收不到返回值
     *
     * @param proceedingJoinPoint 执行的切点
     * @return 原方法返回的结果
     * @throws Throwable 异常
     */
    @Around("addPointCut()")
    public Object methodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知方法前执行:" + proceedingJoinPoint.getTarget().getClass());
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("环绕通知方法后执行:" + proceedingJoinPoint.getTarget().getClass());
        return proceed;
    }
}

4.测试方法

@Test
public void test1() {
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserServcie userServcie = context.getBean(UserServcie.class);
    userServcie.addUser(111, "laoma111");
    DeptService deptService = context.getBean(DeptService.class);
    deptService.addDept(222, "dept222");
}

5.代码地址

gitee.com/mayuanfei/S… 下的SpringIoc06

XML方式实现

这里省略具体的类代码,仅仅展示xml配置的内容。因为xml方式的情况用的不太多,仅仅作为了解即可。

1.创建对象

<!-- 创建对象 -->
<bean id="userMapper" class="com.mayuanfei.springaop.dao.UserXmlMapper"/>
<bean id="deptMapper" class="com.mayuanfei.springaop.dao.DeptXmlMapper"/>
<bean id="userService" class="com.mayuanfei.springaop.service.impl.UserServiceXmlImpl">
    <constructor-arg name="userMapper" ref="userMapper"/>
</bean>
<bean id="deptServcie" class="com.mayuanfei.springaop.service.impl.DeptServiceXmlImpl">
    <constructor-arg name="deptMapper" ref="deptMapper"/>
</bean>
<bean id="addAspectXml" class="com.mayuanfei.springaop.aop.AddAspectXml"/>

2.AOP增强

<!-- aop增强 -->
<aop:config>
    <!--切入点-->
    <aop:pointcut id="pointCutAdd" expression="execution(* com.mayuanfei.springaop.service..add*(..))"/>
    <!-- 配置切面 -->
    <aop:aspect ref="addAspectXml">
        <!--增强作用在具体的方法上-->
        <aop:before method="methodBefore" pointcut-ref="pointCutAdd"/>
        <aop:after method="methodAfter" pointcut-ref="pointCutAdd"/>
        <aop:around method="methodAround" pointcut-ref="pointCutAdd"/>
        <aop:after-returning method="methodAfterReturning"  pointcut-ref="pointCutAdd" returning="res"/>
        <aop:after-throwing method="methodAfterThrowing"  pointcut-ref="pointCutAdd" throwing="e"/>
    </aop:aspect>
</aop:config>

3.代码地址

gitee.com/mayuanfei/S… 下的SpringIoc06