目录
1.什么是aop
AOP(Aspect Oriented Programming)面向切面思想,是Spring的三大 核心思想之一(AOP-面向切面、IOC-控制反转、DI-依赖注入)。
AOP,一般称为面向切面,作为面向对象OOP的一种补充,用于将那 些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并 封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少 系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。 可用于权限认证、日志、事务处理。
2.为什么使用aop
Java是一个面向对象(OOP)的编程语言,但它有个弊端就是当需要为多 个不具有继承关系的对象引入一个公共行为时,例如日志记录、权限 校验、事务管理、统计等功能,只能在每个对象里都引用公共行为, 这样做不便于维护,而且有大量重复代码,AOP的出现弥补了OOP的 这点不足。
3. AOP体系结构
AOP要做的三件事在哪里切入,也就是权限校验等非业务操作在哪些业务 代码中执行;什么时候切入,是业务代码执行前还是执行后;切入后做什 么事,比如做权限校验、日志记录等。
- Pointcut:切点,决定处理如权限校验、日志记录等在何处切入业务代 码中(即织入切面)。切点分为execution方式和annotation方式。前 者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修 饰的代码织入切面。
- Advice:处理,包括处理时机和处理内容。处理内容就是要做什么事, 比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分 为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。
- Aspect:切面,即Pointcut和Advice。
- Joint point:连接点,是程序执行的一个点。例如,一个方法的执行或 者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法 执行。
- Weaving:织入,就是通过动态代理,在目标对象方法中执行处理内容 的过程。
4.使用aop完成添加日志
(1)导入pom依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.15.RELEASE</version>
</dependency>
<!--切面依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.15.RELEASE</version>
</dependency>
</dependencies>
(2)配置spring.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:comtext="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--包扫描-->
<comtext:component-scan base-package="com.aaaa.demo02"/>
<!--开启切面注解驱动 : 让spring可以识别切面的注解-->
<aop:aspectj-autoproxy/>
</beans>
(3)准备一个测试接口
public interface MatheService {
public int fun();
//加法运算
public void add(double a , double b);
//减法运算
public void sub(double a , double b);
//乘法运算
public void mul(double a , double b);
//除法运算
public void div(double a , double b);
}
(4)测试接口的实现类
@Service
public class MatheServiceImpl implements MatheService {
//TODO 需求:执行业务代码前后添加日志
//带返回值的方法
public int fun(){
System.out.println("fun方法的业务代码");
return 100;
}
//加
@Override
public void add(double a, double b) {
double result = a + b;
System.out.println("计算的结果:" + result);
}
//减
@Override
public void sub(double a, double b) {
double result = a-b ;
System.out.println("计算的结果:"+result);
}
//乘
@Override
public void mul(double a, double b) {
double result = a*b ;
System.out.println("计算的结果:"+result);
}
//除
@Override
public void div(double a, double b) {
double result = a/b ;
System.out.println("计算的结果:"+result);
}
}
(5)创建切面类 添加的日志
@Component //表示该类有spring容器创建
@Aspect //表示该类为切面类
public class LogAspectj {
/**
*
* execution:表示哪些方法
* TODO 通配符:
* 第一个*:表示访问任意修饰符和返回类型
* 第二个*:表示该类下的任意类
* 第三个*:表示任意方法
* .. : 表示任意参数类型和个数
*/
//@Pointcut:切点
@Pointcut("execution(* com.aaaa.demo02.*.*(..))")
private void pointcut(){}
@Before("pointcut()")//前置处理
public void before(){
System.out.println("前置日志~~~~~~");
}
}
(6)测试
public class Test02 {
public static void main(String[] args) {
//读取spring配置文件
ApplicationContext app = new ClassPathXmlApplicationContext("spring02.xml");
//从spring容器获取指定的bean对象
MatheService matheServiceImpl = (MatheService) app.getBean("matheServiceImpl");
matheServiceImpl.div(10,10);
}
}
5.aop通知类型
切面类中加入连接点(可以获取方法名称和参数)
切面类中加入(后置通知 / 后置返回通知 / 异常通知)
//后置通知 :不管有没有异常都会执行
@After("pointcut()")
public void afterMethod(JoinPoint joinPoint){ //joinPoint:连接点
Signature signature = joinPoint.getSignature(); //获取被切入的方法对象
String name = signature.getName(); //获取方法名称
System.out.println("后置日志~~~~~~ 方法名称:"+name);
}
//后置返回通知: 可以获取方法的返回值
@AfterReturning(value = ("pointcut()") , returning = "r")
public void afterReturningMethod(Object r){
System.out.println("后置返回通知~~~~~返回值是:"+r);
}
//异常通知:发生异常执行
@AfterThrowing(value = ("pointcut()") , throwing = "e")
public void ex(Exception e){
System.out.println("发生异常");
}
6.切点使用系统注解模式
配置pom/web/spring
Controller层
@RestController //表示该类为控制层并且该类中所有的方法返回的数据都是json数据
public class HelloController {
@GetMapping("/hello") //GetMapping:只处理get请求方式
public String hello(){
return "返回结果hello";
}
@PostMapping("/index")
public String index(){
return "返回index数据";
}
}
日志
@Component
@Aspect
public class MyAspect {
//定义切点
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
private void my(){}
//前置
@Before("my()")
public void aa(){
System.out.println("前置++++++");
}
}
7.切点使用自定义注解模式
定义自定义注解
@Target({ElementType.METHOD}) //表示该注解只能使用在方法上
@Retention(RetentionPolicy.RUNTIME) //表示该注解在什么时候有效【Source源码时有效 CLASS字节码时有效 RUNTIME运行时有效】
@Documented
public @interface MyAnnotation {
int value() default 0;
}
定义切点
@Component
@Aspect
public class MyAspect {
//定义切点
@Pointcut("@annotation(com.aaaa.demo03.MyAnnotation)")
private void my(){}
//前置
@Before("my()")
public void aa(){
System.out.println("前置++qianqina++++");
}
}
使用(使用自定义注解才会执行前置日志内容)
@MyAnnotation
@GetMapping("/show")
public void show(){
System.out.println("自定义注解+++show+++");
}