前言
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP的使用场景:
日志记录,跟踪,优化和监控
事务的处理
持久化
性能的优化
资源池,如数据库连接池的管理
系统统一的认证、权限管理等
应用系统的异常捕捉及处理。文章课程链接:尚硅谷spring注解驱动教程(雷神)
AOP案例实现
首先,我们引入AOP依赖和Springboot的测试依赖,如下
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
1.编写目标类
目标类就是我们需要使用到AOP的类,比如在目标类的某个方法执行前打印日志(前置通知)。
public class TargetClass {
public String testAop (String x) {
if (x == null) {
throw new NullPointerException("参数能不能为空!");
}
System.out.println("方法执行了!");
return "2021英雄联盟全球总决赛:" + x;
}
}
2.编写切面类
AOP中有如下通知方法:
前置通知(@Before):在目标方法运行之前运行。
后置通知(@After):在目标方法运行结束之后运行,无论目标方法是正常结束还是异常结束都会执行。
返回通知(@AfterReturning):也称正常返回通知,在目标方法正常返回之后运行。
异常通知(@AfterThrowing):在目标方法运行出现异常之后运行。
环绕通知(@Around):环绕通知围绕在目标方法前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。
切面类用于动态的感知目标类方法的执行情况,并做出相应的操作。该类需要加上 @Aspect 注解,告诉Spring这是一个切面类。
切点表达式:如果切入点表达式都一样,可以抽取出一个公共的切入点表达式。如:
@Pointcut(“execution(public void aop.test.TargetClass.(…))")
不一样,则需要单独标在方法上,如:
@Before("execution(public void aop.test.XXX.(…))”)
@Aspect
public class AopAspect {
// 如果切入点表达式都一样,那么我们可以抽取出一个公共的切入点表达式
@Pointcut("execution(public String aop.test.TargetClass.*(..))")
public void pointCut() {}
@Before("pointCut()")
public void methodStart(JoinPoint joinPoint) {
System.out.println("方法运行......@Before");
}
@After("pointCut()")
public void methodEnd() {
System.out.println("方法结束......@After");
}
@AfterReturning("pointCut()")
public void methodReturn() {
System.out.println("方法正常返回......");
}
@AfterThrowing("pointCut()")
public void methodException() {
System.out.println("方法出现异常......");
}
}
可以看到,上面的切面类中,各个通知方法只是进行了简单的打印,实际开发工作中,我们肯定需要获取到是哪个方法、参数、结果等。为实现此功能,我们在方法上加上JoinPoint 参数。如下
@Aspect
public class AopAspect {
// 切点表达式一样,可抽取为一个
@Pointcut("execution(public String aop.test.TargetClass.*(..))")
public void pointCut() {
}
@Before("pointCut()")
public void methodStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println(joinPoint.getSignature().getName() + "方法运行......@Before,参数列表是:{" + Arrays.toString(args) + "}");
}
@After("pointCut()")
public void methodEnd(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature().getName() + "方法结束......@After,");
}
@AfterReturning(pointcut = "pointCut()", returning = "result")
public void methodReturn(JoinPoint joinPoint, Object result) {
System.out.println(joinPoint.getSignature().getName() + "方法正常返回......@AfterReturning,运行结果是: " + result + "");
}
// 在目标方法(即div方法)出现异常,被调用
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void methodException(JoinPoint joinPoint, Exception exception) {
System.out.println(joinPoint.getSignature().getName() + "方法出现异常......异常信息:" + exception + "");
}
}
注意,当我们需要返回结果或者异常信息时,需要加入相应参数,并在注解中指定,注解中的名称与参数名一致。
特别注意:JoinPoint joinPoint 只能放在第一个参数位置,否则Spring无法识别,会报错!
3.编写配置类
配置类用于将我们的目标类和切面类注册到IOC容器中,并开启AOP自动代理,注解为 @EnableAspectJAutoProxy,这样AOP才会生效。
@EnableAspectJAutoProxy
@Configuration
public class AopConfig {
@Bean
public AopAspect aopAspect() {
return new AopAspect();
}
@Bean
public TargetClass targetClass() {
return new TargetClass();
}
}
4.编写测试类
测试类中的目标对象要从IOC容器中获取,否则AOP怎么可能生效呐。
public class Test {
@org.junit.Test
public void test() {
// 获取容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
// 不要自己创建这个对象,应从IOC容器中获取
TargetClass targetClass = applicationContext.getBean(TargetClass.class);
targetClass.testAop("我们是冠军,恭喜EDG!");
// 关闭容器
applicationContext.close();
}
}
结果,分别为传入 null 时和 我们是冠军,恭喜EDG! 时。
----手动分隔线 -----
end…
如果总结的还行,就点个赞呗 @_@ 如有错误,欢迎指点,下一篇
spring注解驱动开发-6 Spring AOP实现原理···