欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > Spring AOP

Spring AOP

2024/10/25 10:27:51 来源:https://blog.csdn.net/c1tenj2/article/details/140956403  浏览:    关键词: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 {@Overridepublic void add() {System.out.println("student add...");}@Overridepublic void delete() {System.out.println("student delete...");}@Overridepublic void update() {System.out.println("student update...");}@Overridepublic 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);获取原始函数执行的返回值

获取异常数据(了解) 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com