通过注解的方式配置AOP

AOP介绍

参考博文

Spring AOP——Spring 中面向切面编程

1.什么是AOP思想?

AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。

从《Spring实战(第4版)》图书中扒了一张图:
在这里插入图片描述

2.AOP一般用来干什么

  1. 事务处理:执行方法前,开启事务,执行完成后关闭事务,出现异常后回滚事务
  2. 权限判断:在执行方法前,判断是否具有权限
  3. 日志:在执行前进行日志处理

举个例子,你想给你的网站记录用户访问日志:
对某些url,是不用记录的,而有些是需要记录的
如果你依然使用OOP,面向对象,
那你只能在那些url对应的Controller代码里面,一个一个写上日志记录的代码
而如果你使用了AOP呢?
无需在控制类添加代码,直接添加一个日志类,来进行无侵入式通过切面来记录日志。

3.AOP的优点

  1. 减少重复代码
  2. 提高开发效率
  3. 维护方便

4.AOP相关术语

术语 意义
Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。
Pointcut(切入点) 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
Advice(通知) 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Proxy(代理 一个类被 AOP 织入增强后,就产生一个结果代理类。
Aspect(切面) 是切入点和通知(引介)的结合。
Introduction(引介) 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。
Target(目标对象) 代理的目标对象。

以注解的方式配置AOP

1.切点语法

在这里插入图片描述
我们使用execution指示器选择Instrument的play方法,方法表达式以 * 号开始,标识我们不关心方法的返回值类型。然后我们指定了全限定类名和方法名。对于方法参数列表,我们使用 … 标识切点选择任意的play方法,无论该方法的入参是什么。
多个匹配之间我们可以使用链接符 &&||来表示 “且”、“或”、“非”的关系。但是在使用 XML 文件配置时,这些符号有特殊的含义,所以我们使用 “and”、“or”、“not”来表示。

举例:
限定该切点仅匹配的包是 com.sharpcj.aopdemo.test1,可以使用

execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)) && within(com.sharpcj.aopdemo.test1.*)

在切点中选择 bean,可以使用

execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)) && bean(girl)

2.五种通知类型

注解(名称) 作用
@Before 该通知方法会在目标方法调用之前执行
@After 该通知方法会在目标方法返回或者异常后调用
@AfterReturning 该通知方法会目标方法返回后调用,如果出现异常则不会调用
@AfterThrowing 该通知方法会在目标方法抛出异常后调用
@Around(环绕通知) 该通知法会将目标封装起来

3.简单的实例

User类

package test_aop.pojo;

import org.springframework.stereotype.Component;

@Component
public class User {
    
    

	public String point(boolean s) {
    
    

		System.err.println("User.point() arg s is==" + s);

		return "message";
	}

}

AdviceConfig类

package test_aop.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

// 一定要是用aspect注解
// true代表使用cglib代理方式(使用注解)  false代表使用jdk动态代理模式
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Component
@Aspect
public class UserAdvice {
    
    

	// 方法路径
	final static String FUNTION_NAME = "execution(* test_aop.pojo.User.point(..))";

	// 在point调用之前执行
	// point参数代表当前的目标方法,我们可以通过这个参数获取目标方法的 参数,返回值等相关信息
	@Before(value = FUNTION_NAME)
	public void before(JoinPoint point) {
    
    
		System.out.println("UserAdvice.before()");
	}

	// 在point调用之后执行
	// point参数代表当前的目标方法,我们可以通过这个参数获取目标方法的 参数,返回值等相关信息
	@After(value = FUNTION_NAME)
	public void after(JoinPoint point) {
    
    
		System.out.println("UserAdvice.after()");
	}

	// 该通知方法会目标方法返回后调用,如果出现异常则不会调用
	// returning参数指定用什么参数名接受返回的值
	@AfterReturning(pointcut = FUNTION_NAME, returning = "re")
	public void afterReturning(Object re) {
    
    
		System.out.println("UserAdvice.afterReturning() re==" + re);
	}

	// 该通知方法会在目标方法抛出异常后调用
	// throwing参数指定用什么名称的参数接受异常
	@AfterThrowing(pointcut = FUNTION_NAME, throwing = "e")
	public void afterThrowing(Exception e) {
    
    
		System.err.println(e);
	}

	// 该通知法会将目标封装起来,必须执行proceed方法,相当与放行的操作
	@Around(value = FUNTION_NAME)
	public void arround(ProceedingJoinPoint p) throws Throwable {
    
    

		System.out.println("UserAdvice.arround() before");
		p.proceed();
		System.out.println("UserAdvice.arround() after");

	}
}

main类

package test_aop;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

import test_aop.pojo.User;

@SpringBootApplication
// 扫描注解
@ComponentScan(basePackages = {
    
     "test_aop.pojo", "test_aop.advice" })
public class App {
    
    

	public static void main(String[] args) {
    
    
		ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
		User user = context.getBean(User.class);
		user.point(true);
	}

}

结果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42418169/article/details/112847020
今日推荐