AOP(面向切面)

AOP:Aspect Oriented Programming,面向切面编程。

Aop的优点:

1、降低模块之间的耦合度。

2、使系统更容易扩展。

3、更好的代码复用。

4、非业务代码更加集中,便于统一管理。

5、业务代码更加简洁。

AOP是面向对象编程的一个补充,在运行时,动态的将代码切入到制定方法、指定位置上的编程思想,就是面向切面编程。将不同方法的同一个位置抽象成一个切面对象,对该切面对象进行编程就是AOP。

代码示例:

首先我们需要先导入对应的jar包。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.22</version>
</dependency>
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-aop</artifactId>
     <version>5.3.22</version>
</dependency>
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-aspects</artifactId>
     <version>5.3.22</version>
</dependency>

 首先我们定义一个计算器接口,接口里边实现了加减乘除4个方法,并定义实现类,并且记录入参及出参。

package com.exambner.aop;

public interface Cal {
    int add(int num1, int num2);

    int subtract(int num1, int num2);

    int multiply(int num1, int num2);

    int divide(int num1, int num2);
}
package com.exambner.aop;

import java.lang.reflect.InvocationHandler;

public class CalImpl implements Cal {

    /**
     * 加法
     *
     * @param num1
     * @param num2
     * @return
     */
    @Override
    public int add(int num1, int num2) {
        int res = num1 + num2;
        return res;
    }

    /**
     * 减法
     *
     * @param num1
     * @param num2
     * @return
     */
    @Override
    public int subtract(int num1, int num2) {
        int res = num1 - num2;
        return res;
    }

    /**
     * 乘法
     *
     * @param num1
     * @param num2
     * @return
     */
    @Override
    public int multiply(int num1, int num2) {
        int res = num1 * num2;
        return res;
    }

    /**
     * 除法
     *
     * @param num1
     * @param num2
     * @return
     */
    @Override
    public int divide(int num1, int num2) {
        int res = num1 / num2;
        return res;
    }
}

然后我们去调用它的方法。

public static void main(String[] args) {
        Cal cal = new CalImpl();
        cal.add(1, 1);
        cal.subtract(5, 1);
        cal.multiply(3, 3);
        cal.divide(6, 2);
    }

输出结果:

add方法开始执行,参数为:1,1
add方法执行结果:2
subtract方法开始执行,参数为:5,1
add方法执行结果:4
multiply方法开始执行,参数为:3,3
add方法执行结果:9
divide方法开始执行,参数为:6,2
add方法执行结果:3

这时我们会发现每个方法都存在相同的代码,也就是入参和出参的打印,如果我们后续需要在添加方法时,可能还需要再加上信息打印,这时候就造成了代码的复用,并且提高了代码的耦合性。所以我们可以将这部分代码提取出来统一进行管理,这时就用到了AOP。

我们可以通过实现 InvocationHandler 接口来实现AOP的功能。

package com.exambner.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class MyInvocationHandler implements InvocationHandler {
    /**
     * 接收委托对象
     */
    private Object object;

    public Object getProxyInstance(Object object) {
        //赋值委托对象
        this.object = object;
        //返回一个代理对象
//        newProxyInstance,方法有三个参数:
//        loader: 用哪个类加载器去加载代理对象。
//        interfaces:动态代理类需要实现的接口。动态代理类需要具备委托对象所有的功能。
//        h:动态代理方法在执行时,会调用h里面的invoke方法去执行。也就是当前类中的invoke方法。
        return Proxy.newProxyInstance(this.object.getClass().getClassLoader(), this.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 o = method.invoke(this.object, args);
        System.out.println(method.getName() + "方法执行结果:" + o);
        return o;
    }
}

更改Cal实现类,不再做输出操作,只保留纯粹的业务代码。

package com.exambner.aop;

public class CalImpl implements Cal {

    /**
     * 加法
     *
     * @param num1
     * @param num2
     * @return
     */
    public int add(int num1, int num2) {
        int res = num1 + num2;
        return res;
    }

    /**
     * 减法
     *
     * @param num1
     * @param num2
     * @return
     */
    public int subtract(int num1, int num2) {
        int res = num1 - num2;
        return res;
    }

    /**
     * 乘法
     *
     * @param num1
     * @param num2
     * @return
     */
    public int multiply(int num1, int num2) {
        int res = num1 * num2;
        return res;
    }

    /**
     * 除法
     *
     * @param num1
     * @param num2
     * @return
     */
    public int divide(int num1, int num2) {int res = num1 / num2;return res;
    }
}

通过AOP来执行方法的调用。

public static void main(String[] args) {
        //实例化委托对象
        Cal cal = new CalImpl();
        //创建AOP实例
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        //通过AOP实例创建代理对象
        Cal proxyInstance = (Cal) myInvocationHandler.getProxyInstance(cal);
        //通过代理对象执行委托对象中的方法
        proxyInstance.add(1, 2);
        proxyInstance.subtract(5, 3);
        proxyInstance.multiply(1, 3);
        proxyInstance.divide(4, 2);

    }

输出结果:

add方法开始执行,参数为:[1, 2]
add方法执行结果:3
subtract方法开始执行,参数为:[5, 3]
subtract方法执行结果:2
multiply方法开始执行,参数为:[1, 3]
multiply方法执行结果:3
divide方法开始执行,参数为:[4, 2]
divide方法执行结果:2

 

以上是通过动态代理的方式实现AOP的过程,比较复杂,且不好理解。

Spring对AOP进行了封装,使用Spring框架可以用面对对象的思想来实现AOP。

Spring中不需要创建 InvocationHandler  ,只需要创建一个切面对象,将所有的非业务代码放在切面对象中实现。Spring框架底层会直接根据切面类和目标类来生成一个代理对象。

 

创建代理对象:

package com.exambner.aop;

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

import java.util.Arrays;

/**
 * Component:将切面类交由IOC容器进行管理
 * Aspect:指定当前类为切面对象
 */
@Component
@Aspect
public class LoggerAspect {

    /**
     * 在业务代码执行之前进行的操作
     * 使用Before注解,切入CallImpl中所有的方法。使用*()通配符表示所有的方法;..通配符表示参数
     *
     * @param joinPoint 切点
     */
    @Before(value = "execution(public int com.exambner.aop.CalImpl.*(..))")
    public void before(JoinPoint joinPoint) {
        //根据切点获取方法
        Signature signature = joinPoint.getSignature();
        //获取方法名称
        String name = signature.getName();
        //获取方法参数
        Object[] args = joinPoint.getArgs();
        System.out.println(name + "方法开始执行,参数为:" + Arrays.toString(args));
    }

    /**
     * 业务代码执行完成后进行的操作
     *
     * @param joinPoint 切点
     */
    @After(value = "execution(public int com.exambner.aop.CalImpl.*(..))")
    public void after(JoinPoint joinPoint) {
        //根据切点获取方法
        Signature signature = joinPoint.getSignature();
        //获取方法名称
        String name = signature.getName();
        System.out.println(name + "方法执行结束");
    }

    /**
     * 业务代码返回结果后进行的操作
     *
     * @param joinPoint 切点
     * @param result    业务代码执行结果
     */
    @AfterReturning(value = "execution(public int com.exambner.aop.CalImpl.*(..))", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        //根据切点获取方法
        Signature signature = joinPoint.getSignature();
        //获取方法名称
        String name = signature.getName();
        System.out.println(name + "方法执行结果是:" + result);
    }

    /**
     * 业务代码发生异常后进行的操作
     *
     * @param joinPoint 切点
     * @param exception 异常信息
     */
    @AfterThrowing(value = "execution(public int com.exambner.aop.CalImpl.*(..))", throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Exception exception) {
        //根据切点获取方法
        Signature signature = joinPoint.getSignature();
        //获取方法名称
        String name = signature.getName();
        System.out.println(name + "方法抛出异常:" + exception);
    }
}

将CalImpl交由IoC容器进行管理。

package com.exambner.aop;

import org.springframework.stereotype.Component;

/**
 * 将CalImpl交由IoC容器进行管理
 */
@Component
public class CalImpl implements Cal {

    /**
     * 加法
     *
     * @param num1
     * @param num2
     * @return
     */
    public int add(int num1, int num2) {
        int res = num1 + num2;
        return res;
    }

    /**
     * 减法
     *
     * @param num1
     * @param num2
     * @return
     */
    public int subtract(int num1, int num2) {
        int res = num1 - num2;
        return res;
    }

    /**
     * 乘法
     *
     * @param num1
     * @param num2
     * @return
     */
    public int multiply(int num1, int num2) {
        int res = num1 * num2;
        return res;
    }

    /**
     * 除法
     *
     * @param num1
     * @param num2
     * @return
     */
    public int divide(int num1, int num2) {
        int res = num1 / num2;
        return res;
    }
}

配置文件中配置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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">


    <!-- 自动扫描包 -->
    <context:component-scan base-package="com.exambner"></context:component-scan>
    <!-- 使Aspect注解生效 ,为目标类自动生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
context:component-scan:对 com.exambner 包中的所有类进行扫描,如果该类同时添加了@Component注解,则将该类交由IoC容器进行管理。
aop:aspectj-autoproxy:让Spring框架结合切面类和目标类自动生成代理对象。

通过Spring框架封装的AOP执行方法的调用。
//解析配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
        //获取代理对象
        Cal proxy = (Cal) applicationContext.getBean("calImpl");
        proxy.add(1, 4);
        proxy.subtract(7, 4);
        proxy.multiply(4, 4);
        proxy.divide(6, 4);
add方法开始执行,参数为:[1, 4]
add方法执行结果是:5
add方法执行结束
subtract方法开始执行,参数为:[7, 4]
subtract方法执行结果是:3
subtract方法执行结束
multiply方法开始执行,参数为:[4, 4]
multiply方法执行结果是:16
multiply方法执行结束
divide方法开始执行,参数为:[6, 4]
divide方法执行结果是:1
divide方法执行结束

 

posted @ 2023-01-05 03:23  Amireux-126  阅读(174)  评论(0)    收藏  举报