Spring AOP

 AOP核心概念

AOP的思想是为了实现  在不惊动我们原始代码的情况下为其增加功能。

 在本案例中,增删改查方法中的逻辑是我们书写的原始代码,我们想为其增加记录执行时间的功能,根据上面的概念:

1.增删改查方法中的业务逻辑是"连接点"(我们的原始代码)

2.我们将记录执行时间的代码加以封装为method方法,它是"通知"(需要复用,或者说我们追加的代码)

3.这里定义"通知"的类是MyAdvice,它是"通知类"(里面有通知的类)

4.我们需要一个"切入点"来匹配一个或者多个"连接点",比如这里我们只想记录update和delete方法的执行时间,就用"切入点"来匹配他们两个,而另外两个方法没有被匹配到

5.使用"切面"来匹配切入点和通知之间的关系

入门案例

1.导入AOP相关坐标

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

<!--        Spring AOP要用的包   SpringAOP在springContext里面-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>

2.定义连接点(Service中我们的原始代码),通知类和通知(我们追加的可复用的代码)

StudentServiceImpl实现StudentService接口

package com.example.test.service.impl;

import com.example.test.dao.StudentDao;
import com.example.test.service.StudentService;
import lombok.Data;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class StudentServiceImpl implements StudentService {


    @Override
    public void add() {
        System.out.println("student add...");
    }

    @Override
    public void delete() {
        System.out.println("student delete...");
    }

    @Override
    public void update() {
        System.out.println("student update...");
    }

    @Override
    public void select() {
        System.out.println("student select...");
    }
}

通知类和通知(在里面定义切入点,绑定切入点和通知)

package com.example.test.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;

@Component
@Aspect  //让context知道我们这里要用aop
public class StudentAdvice {
    //切入点
    @Pointcut("execution(* com.example.test.service.StudentService.*(..))")
    public void pt(){}

    @After("pt()")
    public void getTime(){
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM--dd  HH-mm-ss");//打印时间
        System.out.println(sdf.format(date));
    }

}

这里的getTime通知匹配:  com.example.test.service.StudentService中的返回值和参数为任意类型的方法,在连接点后面执行。 (看不懂没关系,后面会讲)

它的作用是返回业务完成后的系统时间。

3.SpringConfig配置类中开启aop

package com.example.test.config;

import org.springframework.context.annotation.*;

@Configuration
@ComponentScan("com.example.test")//包扫描
@PropertySource("db.properties")//加载properties文件
@Import({JdbcConfig.class,MybatisConfig.class})//加载第三方bean
@EnableAspectJAutoProxy  //开启AOP
public class SpringConfig {
}

测试代码及结果

可以看到,在add和delete方法被调用后都执行了getTime方法。

工作流程

 

由上述描述:aop为我们创建的是一个代理对象。 

切入点表达式

 标准格式

一般情况下: execution (返回值类型  包名.类/接口名.方法名(参数类型))

                       有接口和其实现类的情况下写它的接口,比较规范

使用通配符描述切入点

*   单个任意(必须有一个)

..  多个连续任意(也可以没有

例: execution(* com.test..*value(*)) 

匹配 com.test包里面的任意   方法名以value结尾的  只有一个传入参数  返回值为任意  的方法

书写技巧

AOP通知类型

@Before

@After

@Around (重点) 

 定义一个ProceedingJoinPoint对象pjp,调用pjp.proceed()表示在这个位置执行连接点的代码。

注意事项

 

对于第三点和第四点的解释:你的通知方法可能作用于多个原始方法,他们的返回值类型可能不同。 如果通知方法接收了返回值,那么通知方法return的结果会直接作为返回值赋给原始方法。

测试案例

@Component
@Aspect  //让context知道我们这里要用aop
public class StudentAdvice {
    //切入点
//    @Pointcut("execution(* com.example.test.service.StudentService.select(..))")
//    @Pointcut("execution(* com.example.test..*Se*vice.*dd(..))")  只有add方法匹配上了
    @Pointcut("execution(int com.example.test.service.StudentService.select(int))")//只匹配select方法
    public void pt(){}

    @Around("pt()")
    public void getTime(ProceedingJoinPoint pjp) throws Throwable {
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM--dd  HH-mm-ss");//打印时间
        System.out.println("前:"+sdf.format(date));
        pjp.proceed();//连接点方法执行
        System.out.println("后:"+sdf.format(date));
    }

}

测试代码及结果 

 @AfterReturning(了解)

 

@AfterThrowing(了解)

AOP通知获取数据

获取参数数据

除了@Around用ProceedingJoinPoint,其他4种都用JoinPoint来获取参数数据

测试案例

例1

修改select方法 (通知方法也要返回int)

 

测试代码及结果

取到id=1

例2
    @Around(value = "pt()")
    public int getTime(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();//直接pjp.getArgs()获取参数
        System.out.println("修改前的id:"+args[0]);

        args[0]=666;//在这里修改传入参数

        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM--dd  HH-mm-ss");//打印时间
        System.out.println("前:"+sdf.format(date));


        Object result = pjp.proceed(args);//连接点方法执行,这里传入我们修改后的参数

        System.out.println("修改后的id:"+args[0]);
        System.out.println("后:"+sdf.format(date));
        return (int)result;
    }

测试代码同上,结果如下,成功获取修改后的参数为666

获取返回值数据(只有@AfterReturning和@Around能用)

@After作用于原始代码执行完毕后, return前

 测试案例

例1

这个通知是在原始方法return后调用的

例2
    @Around(value = "pt()")
    public int getTime(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();//直接pjp.getArgs()获取参数

        Object result = pjp.proceed(args);//连接点方法执行

        System.out.println("原来的返回值:"+result);
        return 888;
    }

总结:

@Around中:

Object[] args = pjp.getArgs();获取传入的参数
Object result = pjp.proceed(args);获取原始函数执行的返回值

获取异常数据(了解) 

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值