什么是AOP?

通过预编译方式 和 运行期间动态代理 实现程度功能统一的维护技术

什么是动态代理模式?

不修改源码的情况下,对目标方法进行相应的增强,降低程序之间的耦合性

AOP 是 OOP(面向对象编程)的延续

AOP 优势

日志控制 解耦合

AOP 动态代理技术

  • JDK代理 基于接口动态代理
  • Cglib代理从 基于父类的动态代理

先说一下 Spring框架已经继承了,不用我们手动去写 动态代理的实现方式,但是我们也要了解一下

JDK动态代理实现方式

由于 JDK 动态代理 是基于JDK 不需要额外引入包

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

/**
 * @author : zanglikun
 * @date : 2021/3/19 15:28
 * @Version: 1.0
 * @Desc : 费劲,没啥好说的
 */

public class ProxtTest {

    public static void main(String[] args) {
        // 目标对象
        final Target target = new Target();
        // 增强对象(自己写的)
        Advice advice = new Advice();
        /**
         * JDK 动态代理方法 newProxyInstance 参数说明
         * 参数1 目标的类加载器
         * 参数2 目标的接口字节码
         * 参数3 new InvocationHandler() 并重写党法
         * 返回值类型 接口,所以 利用接口多态,进行接收
         */
        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 前置增强
                advice.before();
                method.invoke(target,args);
                // 后置增强
                advice.after();
                return null;
            }
        });
        proxy.save();
    }
}

Cglib 动态代理

Cglib是 第三方lib包,但是 被Spring 已经引用了,仅需要按章Spring的依赖即可!!!

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author : zanglikun
 * @date : 2021/3/19 15:28
 * @Version: 1.0
 * @Desc : 费劲,没啥好说的
 */

public class ProxtTest {

    public static void main(String[] args) {
        // 目标对象
        final Target target = new Target();
        // 增强对象(自己写的)
        Advice advice = new Advice();
        // 1、创建增强器
        Enhancer enhancer = new Enhancer();
        // 2、设置父类
        enhancer.setSuperclass(Target.class);
        // 3、设置回调
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                advice.before();
                method.invoke(target,args);
                advice.after();
                return null;
            }
        });
        // 4、创建代理对象
        Target proxy = (Target) enhancer.create();
        proxy.save();
    }
}

AOP相关概念

Target 目标对象:代理的目标对象

Proxy 代理:一个类被AOP 织入增强后,就产生一个结果代理类

Joinpoint 连接点:可以被增强的方法

Poincut 切入点:对joinpoint 进行拦截

Advice 通知/增强:拦截到Joinpoint 之后做的事情

Aspect 切面:切入点和通知的结合 切点 + 通知

Weaving 织入:将切点 + 通知 创建的过程

AOP 开发 明确的事项

  • 编写常规的业务逻辑 (目标类、目标方法)
  • 编写切面类(通知)
  • 在配置文件,配置织入关系,

AOP 底层使用的是目标有无接口 分别使用 jdk 和 cglib 的代理方式

AOP 基于 XML 快速入门

  • 导入AOP相关坐标
  • 创建目标接口 和 目标类
  • 创建切面类
  • 将目标类 和 切面类的对象创建权 交由Spring管理
  • 在配置文件中 配置 织入关系
  • 测试

导坐标

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.4</version>
        </dependency>

编写配置文件 ApplicaitonContext.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: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">

    <!-- 配置目标对象 -->
    <bean id="target" class="com.zanglikun.aop.Target"></bean>
    <!-- 切面对象 -->
    <bean id="myAspect" class="com.zanglikun.aop.MyAspect"></bean>
    <!-- 配置织入 :告诉Spring 那些方法 进行那些增强(前置、后置)-->
    <aop:config>
        <!-- 声明切面 将普通的bean 变位 切面了-->
        <aop:aspect ref="myAspect">
            <aop:before method="before" pointcut="execution(public void com.zanglikun.aop.Target.save())"></aop:before>
            <aop:after method="after" pointcut="execution(public void com.zanglikun.aop.Target.save())"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>

代码简图

这里 缺少一个 切点表达式的引用

切点表达式

AOP 通知种类

前置通知 :方法执行之前

后置通知:方法执行之后

环绕通知:方法执行前后 都执行

异常抛出通知:当目标方法抛异常才执行

最终通知:无论怎样,最终通知都要执行

注解AOP 开发

  • 创建目标接口 目标类
  • 创建切面类
  • 将目标类 与 切面类 交由Spring进行管理

@Component

  • 在切面类中使用 注解 配置织入关系

@Aespact 声明当前是一个切面类

在方法加注解

@Before(value = “切点表达式”)

  • 在配置文件开启组件扫描 和 AOP自动代理
<context:component-scan base-package="com.zanglikun"/>
<aop:aspec-autoproxy/>
  • 测试

@ContextConfiguration(“classpath:applicationContext.xml”)

注解开发时,AOP 切点表达式抽取

使用

@Pointcuy(切点表达式) 用来定义切点表达式 将来其他可直接引用