Spring Aop 完整详细讲解!!!建议收藏观看

一. AOP 概述:

  1. AOP(Aspect Oriented Programming 面向切面编程) : AOP 是对面向对象编程的一个补充, 在运行时,动态的将代码切入到类的指定方法,指定位置上的编程思想就是面向切面编程。将不同方法的同一位置抽象成一个切面对象,对该切面对象进行编程就是 AOP 。
  2. 优点:1>. 降低模块之间的耦合度
    2>. 使系统容易扩展
    3>. 更好的代码复用
    4>. 非业务代码更加集中, 不分散,便于统一管理
    5>. 业务代码更简洁纯粹,没有其他代码的影响
    6>. 将复杂的需求分解除不同的方面,将散布在系统中的公共功能集中解决

如何使用

  1. 创建Maven ,pom.xml 添加(如果还未下载 Maven 相关配置:请跳转至: Spring IoC 全套资源!!,里面含有Maven框架的下载以及配置)
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>6.0.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.1.11</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.12</version>
        </dependency>
    </dependencies>
  1. 创建一个计算器接口 Cal,定义四个方法:
public interface Cal {
    public int add(int num1, int num2);
    public int sub(int num1, int num2);
    public int mul(int num1, int num2);
    public int div(int num1, int num2);
}
  1. 创建接口的实现类:
public class Calimpl implements Cal {

    @Override
    public int add(int num1, int num2) {
        System.out.println("add 方法的参数是[" + num1 + ", " + num2 + "]");
        int res = num1 + num2;
        System.out.println("add 方法的结果是:" + res);
        return res;
    }

    @Override
    public int sub(int num1, int num2) {
        System.out.println("sub 方法的参数是[" + num1 + ", " + num2 + "]");
        int res = num1 - num2;
        System.out.println("sub 方法的结果是:" + res);
        return res;
    }

    @Override
    public int mul(int num1, int num2) {
        System.out.println("mul 方法的参数是[" + num1 + ", " + num2 + "]");
        int res = num1 * num2;
        System.out.println("mul 方法的结果是:" + res);
        return res;
    }

    @Override
    public int div(int num1, int num2) {
        System.out.println("div 方法的参数是[" + num1 + ", " + num2 + "]");
        int res = num1 / num2;
        System.out.println("div 方法的结果是:" + res);
        return res;
    }
}

在上述代码中我们可以看到,在计算过程中都用到了打印参数和结果的语句,因此日志信息和业务逻辑的耦合度太高,不利于系统的维护,使用 AOP 可以进行优化,如何实现 AOP 呢? 使用动态代理的方式来实现。

给业务代码找一个代理, 打印日志信息的工作交给代理来做,这样的话业务代码就只需要关注自身的业务即可:

public class MyInvocationHandler implements InvocationHandler {
    // 接受委托对象
    private Object object = null;
    // 返回代理对象
    public Object bind(Object object){
        this.object = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName() + "方法的参数是: " + Arrays.toString(args));
        Object res = method.invoke(this.object, args);
        System.out.println(method.getName() + "方法的结果是:" + res);
        return res;
    }
}

以上是通过动态代理实现 AOP 的过程,比较复杂,不好理解,Spring 框架对 AOP 进行了封装,使用了 Spring
框架可以用面向对象的思想来实现 AOP 。 Spring 框架中不需要创建 InvocationHander, 只需要创建一个切面对象,
将所有的非业务代码在切面对象中完成即可,Spring 框架底层会自动根据切面类以及目标类生成一个代理对象。

LoggerAspect

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;

@Aspect // 使类成为一个切面对象
@Component  // 让IoC管理类
public class LoggerAspect {
    @Before("execution(public int com.spring.utils.impl.Calimpl.*(..))")
    public void before(JoinPoint joinPoint){
        // 获取方法名
        String name = joinPoint.getSignature().getName();
        // 获取参数
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println(name + " 方法的参数是: " + args);
    }

    @After(value = "execution(public int com.spring.utils.impl.Calimpl.*(..))")
    public void afterRuning(JoinPoint joinPoint){
        // 获取方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name + " 方法执行完毕!");
    }

    @AfterReturning(value = "execution(public int com.spring.utils.impl.Calimpl.*(..))", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result){
        // 获取方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name + " 方法执行的结果是:" + result);
    }

    @AfterThrowing(value = "execution(public int com.spring.utils.impl.Calimpl.*(..))", throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Exception exception){
        // 获取方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name + " 方法抛出的异常是:" + exception);
    }

}

LoggerAspect 类定义处添加的两个注解:

  1. @Aspect: 表示该类是切面类;
  2. @Component:将该类的对象注入到 IoC 容器中。 用于标注在类上,表明该类是一个Spring组件(Bean),这样Spring容器就会自动扫描到这些类,并将其实例化、组装,然后管理起来。@Component注解本身并没有特别的业务含义,它仅仅是一个标记,告诉Spring这是一个需要被管理的Bean。
  3. 具体方法处添加的注解:@Before:表示方法助兴的具体位置和时机。 Calimpel 也需要添加 @Component,交给IoC 容器来管理。
import com.spring.utils.Cal;
import org.springframework.stereotype.Component;

@Component("test2")
public class Calimpl implements Cal {

    @Override
    public int add(int num1, int num2) {
        int res = num1 + num2;
        return res;
    }

    @Override
    public int sub(int num1, int num2) {
        int res = num1 - num2;
        return res;
    }

    @Override
    public int mul(int num1, int num2) {
        int res = num1 * num2;
        return res;
    }

    @Override
    public int div(int num1, int num2) {
        int res = num1 / num2;
        return res;
    }
}

Spring.xml 中配置 AOP:

<?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:context="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">

    <!-- 自动扫描 -->
    <context:component-scan base-package="com.spring"/>
    <!-- 使Aspect注解生效,为目标类自动生成代理对象 -->
    <aop:aspectj-autoproxy/>
</beans>

获取并执行:

import com.spring.utils.Cal;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) {
        // 加载配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-AOP.xml");
        // 获取代理对象
        Cal proxy = (Cal) applicationContext.getBean("test2");
        proxy.add(1, 1);
        proxy.sub(2, 1);
        proxy.mul(3, 2);
        proxy.div(6, 0);
    }
}

注意:在执行的时候applicationContextgetBean 方法获取对象的名字是通过在 Calimpl 类中添加注解 @Component("test2") 实现的,如果只添加 @Component 不添加名称,则默认是 Calimpl 类的名称首字母小写,即 calimpl。这里的获取方法相当于 IoC 在spring.xml 中获取 id 为test2 一样,只不过在 Spring.xml 中配置是手动添加参数,而使用 @Component 注解之后 Spring 会自动将其处理。 如:
在这里插入图片描述

context:component-scan:将 com.spring 包中的所有类进行扫描,如果该类同时添加了 @Component ,则将该类扫描到 IoC 容器中,即 IoC 管理它的对象。 aop:aspectj-autoproxy:让Spring 框架结合切面类和目标类自动生成动态代理对象。

  1. 切面:横切关注点被模块化的抽象对象。
  2. 通知:切面对象完成的工作。(如上LoggerAspect 中的函数)
  3. 目标:被通知的对象,即被混合之后的对象。
  4. 代理:切面,通知,目标混合之后的对象。
  5. 连接点:通知要插入业务代码的具体位置。(LoggerAspect 中的 JoinPoint joinPoint)
  6. 切点:AOP 通过切点定位到连接点。

SpringAop已完结。

  • 18
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值