AOP简介

1.什么是aop

  • aop为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
  • aop是oop的延续,是软件开发中的一个热点,也是sprin框架中的一个重要内容,是函数式编程的一种衍生范型。利用aop可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可用性,同时提升了开发的效率。

2.aop的作用及其优势

  • 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
  • 优势:减少重复代码,提高开发效率,并且便于维护

3.aop的底层实现

  • 实际上,aop的底层使用过spring提供的动态代理技术实现的。在运行期间,spring通过动态代理技术动态的生成代理对象,代码对象方法执行时进行增强方法的介入,再去调用目标对象的方法,从而完成代理的增强。

4.aop的动态代理技术

常用的动态代理技术:

  • JDK代理:基于接口的动态代理技术
  • cglib代理:基于父类的动态代理技术
    在这里插入图片描述

5.JDK的动态代理

1.目标类接口

public interface TargertInterface {
	public void save();
}

2.目标类

public class Targer implements TargetInterface {
	@Override
	public void save() {
		System.out.println("save running....");
	}
}

3.增强对象

public class Advice {
	public void before() {
		System.out.println("前置增强....");
	}

	public void after() {
		System.out.println("后置增强...")
	}
}

4.动态代理代码

public class ProxyTest {
    public static void main(String[] args) {

        //创建目标对象
        final Target target = new Target();
        //获得增强对象
        Advice advice = new Advice();

        //返回值 就是动态生成的代理对象
        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
                //目标对象加载器
                target.getClass().getClassLoader(),
                //目标对象相同的接口字节码对象数组
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    //调用代码方法的任何方法,实质执行的都是 invoke 方法
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //前置增强
                        advice.before();
                        //执行目标方法
                        Object invoke = method.invoke(target, args);
                        //后置增强
                        advice.after();
                        return invoke;
                    }
                }
        );
        //调用代理对象方法
        proxy.save();
    }
}

在这里插入图片描述

6.cglib的动态代理

  • spring核心包中集成了cglib
    1.目标类
public class Target  {
    public void save() {
        System.out.println("save running...");
    }
}

2.增强对象

public class Advice {

    public void before() {
        System.out.println("before...");
    }

    public void after() {
        System.out.println("after...");
    }

}

3.动态代理代码

public class ProxyTest {
    public static void main(String[] args) {

        //创建目标对象
        final Target target = new Target();
        //获得增强对象
        Advice advice = new Advice();

        //返回值 就是动态生成的代理对象,基于 cglib
        //1.创建增强器
        Enhancer enhancer = new Enhancer();
        //2.设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //3.设置回调
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //执行前置
                advice.before();
                //执行目标
                Object invoke = method.invoke(target, args);
                //执行后置
                advice.after();
                return invoke;
            }
        });
        //4.创建代理对象
        Target proxy = (Target) enhancer.create();
        proxy.save();
    }
}

在这里插入图片描述

7. aop的相关概念

  • spring的aop实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式进行指定目标的方法增强
  • Target(目标对象):代理的目标对象(要被增强的对象)
  • Proxy(代理):一个类被aop织入增强后,就产生一个结果代理类
  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。(被拦截到的方法指目标对象内部的方法,就是被增强的方法,先拦截到这些方法才能进行增强,这些方法都叫连接点,可以被增强的方法叫连接点)
  • Pointcut(切入点):所谓切入点是指我们要对那些Joinpoint进行拦截的定义。(对那些连接点进行配置的,连接点真正被增强的方法叫做切入点)
  • Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。(也是方法,拦截到目标方法之后,进行增强,对目标方法进行增强的那个方法叫做Advice)
  • Aspect(切面):是切入点和通知的结合
  • Weaving(织入):是指增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译器织入和类装载期织入。(切点+通知 结合的过程)

8.aop开发的明确事项

  • 需要编写的内容
    • 编写核心业务代码(目标类的目标方法)
    • 编写切面类,切面类有通知(增强功能方法)
    • 在配置文件中,配置织入关系,即将那些通知和那些连接点进行结合
  • aop技术实现的内容
    • spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
  • aop底层使用哪种代理方式
    • 在spring中,框架是根据目标类是否实现了接口来决定采用哪种动态代理的方式。

9.aop 常用注解

  • @Aspect
  • @Before
  • @After
  • @Around
  • @Pointcut
  • @AfterThrowing
  • @AfterReturning

代码示例

  1. 依赖引入
 		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
  1. 创建切入点注解
package com.demo.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author: mt
 * @description: aop测试注解
 * @create: 15:16 2022/7/26
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AopTest {

    int type() default -1;

    String value() default "testValue";
}

  1. 创建切面处理类
package com.demo.aop;

import com.demo.annotation.AopTest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * @author: mt
 * @description: 切面处理类
 * @create: 15:20 2022/7/26
 */
@Component
@Aspect
public class TestAop {


    /**
     * 配置切入点
     */
    @Pointcut("@annotation(com.demo.annotation.AopTest)")
    public void apoPointCut() {
        // 无方法体,主要在@Pointct中体现@AopTest注解类所在位置
    }

    /**
     * 配置环绕通知
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("apoPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 注解类可能被代理,需要获取真实类的注解
        Class<?> targetClass = joinPoint.getTarget().getClass();
        Method method = targetClass.getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());
        // 获取方法注解
        AopTest aopTest = method.getAnnotation(AopTest.class);
        // 获取注解参数
        int type = aopTest.type();
        String value = aopTest.value();

        //获取方法参数
        Parameter[] parameters = method.getParameters();

        // 后续业务处理
        // do something....

        // 方法执行
        return joinPoint.proceed();
    }


    /**
     * 配置异常通知
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "apoPointCut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Throwable e) {
        // 对应业务处理
        // do something..
    }
}

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐