Spring AOP

什么是AOP 

是面向切面 是一种编程范式 指导开发者组织程序结构 OOP面向对象,AOP和OOP是一种编程思想,是如何指导开发者写程序完成某些功能 ,这个两种是不同的编程范式。

AOP的核心概念 

通知:在切入点执行操作 也就是共性功能

通知类: 定义通知的类

切入点:对接连接点,一个切入点可以描述一个具体方法,也可以匹配多个方法

切面 :描述通知与切入点的关系

连接点: 程序执行过程中的任意问题 ,粒度为执行方法,抛出异常,设置变量 。理解为方法执行

目标对象:被代理的对象

代理对象:目标对象不可访问,通过代理对象增加功能访问

代理模式:1.动态代理 jdk  CGLib 动态代理   2.静态代理

AOP的作用 

在不惊动原始设计的情况下对其功能进行增强,这样功能底层实现采用代理模式。

AOP解决了什么问题

横切关注点与业务逻辑进行解耦,降低模块之间的耦合度 ,业务模块所调用的逻辑封装起来,提高可维护性和重用性 减少系统重复代码。

1.创建项目 maven.pom  aop-annotation

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.6</version>
        </dependency>

        <!--junit5测试-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>6.0.6</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>

2.声明了接口

package com.yunda.calculator;

public interface Calculator {


    int add(int i, int j);

    int sub(int i, int j);

    int mul(int i, int j);

    int div(int i, int j);
}

3.实现声明的接口方法 

package com.yunda.calculator.impl;

import com.yunda.calculator.Calculator;

public class CalculatorPureImpl  implements Calculator {
    @Override
    public int add(int i, int j) {
        System.out.println("参数是"+i+","+j);
        int result=i+j;
        System.out.println("内部方法是:"+result);
        return result;
    }

    @Override
    public int sub(int i, int j) {
        System.out.println("参数是"+i+","+j);
        int result=i-j;
        System.out.println("内部方法是:"+result);
        return i-j;
    }

    @Override
    public int mul(int i, int j) {
        System.out.println("参数是"+i+","+j);
        int result=i*j;
        System.out.println("内部方法是:"+result);
        return i*j;
    }

    @Override
    public int div(int i, int j) {
        System.out.println("参数是"+i+","+j);
        int result=i/j;
        System.out.println("内部方法是:"+result);
        return i/j;
    }
}

4.创建静态代理对象

package com.yunda.calculator;

/**
 * 静态代理
 */
public class CalculatorStaticProxy implements Calculator {

    // 将代理的对象声明成员变量
    private Calculator calculator;
    @Override
    public int add(int i, int j) {
        System.out.println("参数是"+i+","+j);
        int result=calculator.add(i,j);
        System.out.println("内部方法是:"+result);
        return 0;
    }

    @Override
    public int sub(int i, int j) {
        return 0;
    }

    @Override
    public int mul(int i, int j) {
        return 0;
    }

    @Override
    public int div(int i, int j) {
        return 0;
    }


}

5.创建代理工厂实现接口

package com.yunda.calculator.py;

import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;

import java.lang.reflect.Array;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * jdk代理类
 */
public class ProxyFactory {

    // 将代理对象写成成员变量
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        // 1.获取类加载器
        ClassLoader classLoader = target.getClass().getClassLoader();
        // 2.获取类接口
        Class<?>[] interfaces = target.getClass().getInterfaces();

        InvocationHandler invocationHandler = new InvocationHandler(){
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object target =null;
                try {
                    System.out.println("动态代理日志"+method.getName()+"参数"+ Arrays.toString(args));
                    target = method.invoke(proxy, args);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("动态代理日志"+method.getName()+"异常"+e.getMessage());
                }
                finally {
                    System.out.println("动态代理日志"+method.getName()+"方法执行完毕");
                }
                return target;
            }
        };
        return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
    }
}

6.进行单元测试

package com.yundacalculator;

import com.yunda.calculator.Calculator;
import com.yunda.calculator.impl.CalculatorPureImpl;
import com.yunda.calculator.py.ProxyFactory;


public class Test {

    @org.junit.jupiter.api.Test
    public void test(){
        ProxyFactory proxyFactory = new ProxyFactory(new CalculatorPureImpl());
        Calculator proxy = (Calculator) proxyFactory.getProxy();
        proxy.div(1,0);
    }
}

采用注解方式实现

       1.添加依赖 pom.xml

  <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.0.6</version>
        </dependency>

2.准备接口

public interface Calculator {
    
    int add(int i, int j);
    
    int sub(int i, int j);
    
    int mul(int i, int j);
    
    int div(int i, int j);
    
}

3.创建类实现接口

@Component
public class CalculatorPureImpl implements Calculator {
    
    @Override
    public int add(int i, int j) {
    
        int result = i + j;
    
        return result;
    }
    
    @Override
    public int sub(int i, int j) {
    
        int result = i - j;
    
        return result;
    }
    
    @Override
    public int mul(int i, int j) {
    
        int result = i * j;
    
        return result;
    }
    
    @Override
    public int div(int i, int j) {
    
        int result = i / j;
    
        return result;
    }
}

4.声明切面

package com.yunda.advier;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect // 标记这个类是一个切面类
@Component // 将切面类交给spring ioc 容器进行管理

public class LogAspect {

    @Before(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))") // 标记是前置通知
    public void printBefore() {
        System.out.println("前置通知...");
    }
    @AfterReturning(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))")
    public void printReturning(){
        System.out.println("后返回通知...");
    }
    @AfterThrowing(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))")
    public void printThrowing(){
        System.out.println("后异常通知...");
    }
    @After( value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))")
    public void printAfter(){
        System.out.println("后置通知....");
    }
}

5.启动面向切面

package com.yunda.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration // 当前类为配置类 交给spring ioc 容器进行管理
@ComponentScan("com.yunda") // 包扫描
@EnableAspectJAutoProxy // 启动切面代理模式
public class MySpringAop {
}

6.测试方法

   @org.junit.jupiter.api.Test
    public void pro(){
        calculator.add(1,1);
    }

获取通知细节信息

@Before(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))") // 标记是前置通知
    public void printBefore(JoinPoint joinPoint) {
        // 1.获取签名
        Signature signature = joinPoint.getSignature();
        // 2.获取目标方法详细信息
        String name = signature.getName();
        System.out.println("目标方法"+name);

        // 3.获取修改器
        int modifiers = signature.getModifiers();
        System.out.println("修改器"+modifiers);
        // 4.获取声明类型名称
        String declaringTypeName = signature.getDeclaringTypeName();
        System.out.println("获取声明类型名称"+declaringTypeName);
        // 5.调用目标方法时传入的实参
        Object[] args = joinPoint.getArgs(); // 获取参数

        // 6. 转换成list集合
        List<Object> objects = Arrays.asList(args);
        System.out.println(objects);
        System.out.println("前置通知..."+"方法开始"+name+"参数列表"+objects);
    }

1.方法返回值

    // returning 返回
    @AfterReturning(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))",returning =
            "targetMethodReturnValue")
    public void printReturning(JoinPoint joinPoint,Object targetMethodReturnValue){
        String name = joinPoint.getSignature().getName();
        System.out.println("后返回通知..."+"方法"+name+"返回值"+targetMethodReturnValue);
    }

2.异常对象

   @AfterThrowing(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))",
                    throwing = "targetMethodException")
    public void printThrowing(JoinPoint joinPoint ,Throwable targetMethodException){
        String name = joinPoint.getSignature().getName();
        System.out.println("后异常通知..."+"方法"+name+"异常"+targetMethodException.getClass().getName());
    }

3.改造将连接点的方法使用引入切入点

package com.yunda.advier;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

@Aspect // 标记这个类是一个切面类
@Component // 将切面类交给spring ioc 容器进行管理

public class LogAspect {

    // 切入点
    @Pointcut("execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))")
    public void pry(){};

    @Before(value = "pry()") // 标记是前置通知
    public void printBefore(JoinPoint joinPoint) {
        // 1.获取签名
        Signature signature = joinPoint.getSignature();
        // 2.获取目标方法详细信息
        String name = signature.getName();
        System.out.println("目标方法"+name);

        // 3.获取修改器
        int modifiers = signature.getModifiers();
        System.out.println("修改器"+modifiers);
        // 4.获取声明类型名称
        String declaringTypeName = signature.getDeclaringTypeName();
        System.out.println("获取声明类型名称"+declaringTypeName);
        // 5.调用目标方法时传入的实参
        Object[] args = joinPoint.getArgs(); // 获取参数

        // 6. 转换成list集合
        List<Object> objects = Arrays.asList(args);
        System.out.println(objects);
        System.out.println("前置通知..."+"方法开始"+name+"参数列表"+objects);
    }
    // returning 返回
    @AfterReturning(value = "pry()",returning =
            "targetMethodReturnValue")
    public void printReturning(JoinPoint joinPoint,Object targetMethodReturnValue){
        String name = joinPoint.getSignature().getName();
        System.out.println("后返回通知..."+"方法"+name+"返回值"+targetMethodReturnValue);
    }
    @AfterThrowing(value = "pry()",
                    throwing = "targetMethodException")
    public void printThrowing(JoinPoint joinPoint ,Throwable targetMethodException){
        String name = joinPoint.getSignature().getName();
        System.out.println("后异常通知..."+"方法"+name+"异常"+targetMethodException.getClass().getName());
    }
    @After( value = "pry()")
    public void printAfter(){
        System.out.println("后置通知....");
    }
}

环绕通知的使用

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    @Around("pt()")
    public void around(){
        System.out.println("around before advice ...");
        System.out.println("around after advice ...");
    }
}

常用注解:

@EnableAspectJAutoProxy 启动切面代理

@Aspect  设置当前类为aop切面类

@Pointcut 设置切入点方法

@Before 前置通知 设置当前通知方法与切入点绑定关系 当前通知方法在切入点方法前运行

@After 后置通知 设置当前通知方法与切入点绑定关系 当前通知方法在切入点方法后运行 

@AfterReturning 后返回通知 设置当前通知方法与切入点绑定关系 当前通知方法在原始切入点正常执行完毕后执行

@AfterThrowing 后异常通知 设置当前通知方法与切入点绑定关系 当前通知方法在原始切入点正常执行运行出现异常后执行

@Around 环绕通知 设置当前通知方法与切入点绑定关系 当前通知方法在原始切入点正常前后运行

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值