首页 > 其他分享 >Spring中AOP的底层原理剖析

Spring中AOP的底层原理剖析

时间:2024-09-21 12:54:51浏览次数:3  
标签:Spring Object 代理 剖析 Student AOP new save public

1 代理模式概述

1 生活中的代理案例在这里插入图片描述

  • 房屋中介代理
    • 客户手中没有房源,找一个中介
  • 商品代购
    • 代购者可以拿到比较低成本的商品,拥有自己的渠道

2 为什么要使用代理

  • 对于消费者而言,可以减少成本, 只需要关心自己需要的商品,不需要去寻找渠道或者是找房源

3 代理模式在java中的应用

  • 统一异常处理
  • Mybatis 使用了代理
  • Spring aop实现原理
  • 日志框架

4 概述

  • 代理模式(proxy pattern):是23种设计模式中的一种,属于结构性的模式。指一个对象本身不做实际的操作,而是通过其它对象来得到自己想得到的结果
  • 意义:目标对象只需要关心自己的实现细节,通过代理对象来实现功能的增强,可以扩展目标对象的功能
  • 体现了一个非常重要的编程思想:不能随便修改源码,如果需要修改源码,通过代理模式来扩展。

5 图示

  • 图示

2 代理的实现方式

1 java中代理图示

  • 图示

  • 元素组成
    • 接口, 定义行为和规范的
    • 被代理类,是目标对象
    • 代理类,做功能增强

2 静态代理

2.1 案例
  • 通过代理模式实现事务操作
2.2 实现案例
  • 创建domain对象

  • 创建service接口来管理规范

  • 创建实现类,被代理类

  • 创建事务类对象

  • 创建代理类对象

  • 测试代理类对象

  • 运行结果

2.3 存在的问题
  • 不利于代码的扩展,比如接口中新添加一个抽象方法时,所有实现类都需要重新实现,否则报错
  • 代理对象需要创建很多,这种设计很不方便和麻烦

3 动态代理

1 概述

  • 概述:在不改变原有功能代码的前提下,能够动态的实现方法的增强

2 JDK动态代理

2.1 基础准备
  • 创建service
package com.ff.service;

/**
 * @author 
 * @version 1.0
 */
public interface IStudentService {
    public void save();

    Student query(Long id);
}
  • 创建service实现类(需要代理的类)
public class IStudentServiceImpl implements IStudentService{
    public void save() {
        System.out.println("保存学生信息");
    }

    public Student query(Long id) {
        System.out.println("查询操作");
        Student student = new Student();
        student.setAge(18);
        student.setName("sy");
        return student;
    }
}
  • 增强类
public class DaoTransaction {
    public void before() {
        System.out.println("开启事务操作");
    }
    public void after() {
        System.out.println("关闭事务");
    }
}
2.2 实现InvocationHandler接口
  • InvocationHandler接口,用来做方法拦截

  • 实现接口

public class TransactionHandler implements InvocationHandler {

    //增强类对象
    private DaoTransaction transaction;
    //需要代理的对象
    private Object object;

    public TransactionHandler(DaoTransaction transaction, Object object) {
        this.transaction = transaction;
        this.object = object;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret = null;
        //判断当前方法是否是save,是才做事务
        if(method.getName().equals("save")) {
            transaction.before();
            ret = method.invoke(object,args);
            transaction.after();
        } else {
            ret = method.invoke(object,args);
        }
        return ret;
    }
}
  • 分别介绍里面的参数

    • Proxy:代理实例,可以通过newProxyInstance创建代理实例
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
    
      • ClassLoader类加载器,直接通过需要代理的类获得就行
      • Class[]:目标类所实现的所有接口
      • InvocationHandler:方法拦截处理器,可以在里面实现方法的增强
    • Method:执行目标方法的,invoke方法执行
    • args:参数数组
2.3 测试
  • 方法
@Test
    public void testSave(){
        //增强类对象
        DaoTransaction daoTransaction = new DaoTransaction();
        //目标执行类
        IStudentServiceImpl iStudentService = new IStudentServiceImpl();
        //方法拦截处理器
        TransactionHandler handler = new TransactionHandler(daoTransaction, iStudentService);
        //获取代理实例对象
        IStudentService proxyStudentService =(IStudentService) Proxy.newProxyInstance(IStudentServiceImpl.class.getClassLoader(), IStudentServiceImpl.class.getInterfaces(), handler);
        proxyStudentService.save();
    }
2.4 底层运行原理
  • 生成代理类的字节码来学习
/**
 * 使用来生成字节码学习使用
 */
private void saveProxyClass(String path) throws IOException {
    byte[] $proxy1s = ProxyGenerator.generateProxyClass("$proxy1",
            IStudentServiceImpl.class.getInterfaces());
    FileOutputStream fileOutputStream = null;
    try {
        fileOutputStream = new FileOutputStream(new File(path + "$Proxy1.class"));
        fileOutputStream.write($proxy1s);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if(fileOutputStream != null) {
            fileOutputStream.flush();
            fileOutputStream.close();
        }
    }
}
  • 生成字节码反编译结果
public final class $proxy1 extends Proxy implements IStudentService {
    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m0;
    private static Method m3;

    public $proxy1(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final Student query(Long var1) throws  {
        try {
            return (Student)super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void save(Student var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("com.ff.service.IStudentService").getMethod("query", Class.forName("java.lang.Long"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m3 = Class.forName("com.ff.service.IStudentService").getMethod("save", Class.forName("com.ff.domain.Student"));
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
  • 执行原理图

这里写一个自己的理解,毕竟纠结了几天的东西,感觉终于搞懂了

  • 首先我们在我们的主测试程序中通过Proxy.newProxyInstance()来进行了一个Proxy类的初始化并返回一个目标类的代理类对象,在这里我们需要注意一点是,我们此时赋值的handler是在其中对Proxy类中的h进行了赋值。
  • 而后我们去看它这一步生成的代理类对象,我们可以发现我们自己生成出来的$Proxy和proxyStudentService其实是一个对象!!,而后我们直接用proxyStudentService.save()来执行
  • 随后我们必然是执行代理对象的save方法,发现代理对象中其实是super.h.invoke(this,m3,new Object[]{val}),细心的你会发现,这里父类的Proxy中的h,在第一步中就被我们自己的handler赋值了,所以我们理所当然他对这个方法拦截器进行了重写,我们当然会执行自己重写的invoke方法
  • 程序进入了我们自己的handler之后,我们就执行,这个是就如同静态代理一样,我们的加强类进行了业务上的加强,并将我们的invoke(这里的invoke是我们最初的被代理类对象的)进行融合加强。
  • 值得注意的是,我们的代理类和实现类都实现了接口,可以说不实现接口,就没有jdk动态代理。

3 CGLIB动态代理

  • jdk动态代理有一个前提,需要代理的类必须实现接口,如果没有实现接口,只能通过CGLIB来实现,其实·就是对于JDK动态代理的一个补充
  • 注意:
    • 类不能被final修饰
    • 方法不能被final修饰
3.1 基础准备
  • 导包
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>
  • 准备需要代理的目标类
public class IStudentServiceImpl implements IStudentService{
    public void save(Student student) {
        System.out.println("保存学生信息");
    }

    public Student query(Long id) {
        System.out.println("查询操作");
        Student student = new Student();
        student.setAge(18);
        student.setName("sy");
        return student;
    }
}
public interface IStudentService {
    public void save(Student student);

    Student query(Long id);
}
3.2实现方法拦截
  • 实现方法拦截
public class CglibInterceptor implements MethodInterceptor {

    DaoTransaction transaction;

    public CglibInterceptor(DaoTransaction transaction) {
        this.transaction = transaction;
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //事务增强
        transaction.before();
        Object ret = methodProxy.invokeSuper(o, objects);
        transaction.after();
        return ret;
    }
}
3.3测试
@Test
public void testSave() throws IOException {
    //得到方法拦截器
    CglibInterceptor interceptor = new CglibInterceptor(new DaoTransaction());
    //使用CGLIB框架生成目标类的子类(代理类)实现增强
    Enhancer enhancer = new Enhancer();
    //设置父类字节码
    enhancer.setSuperclass(IStudentServiceImpl.class);
    //设置拦截处理
    enhancer.setCallback(interceptor);
    IStudentServiceImpl service = (IStudentServiceImpl)enhancer.create();
    service.save(new Student());
}
3.4底层运行原理
  • 底层运行原理图

在这里插入图片描述

  • 文字说明:
    • 通过继承的方式去获取目标对象的方法
    • 通过传递方法拦截器MethodInterceptor实现方法拦截,在这里做具体的增强
    • 调用生成的代理类对象具体执行重写的save方法,直接去调用方法拦截器里面的intercept方法
    • 前后加上了增强操作,从而实现了不修改代码实现业务增强

4 总结

代理类型实现机制回调方式适用场景效率
JDK动态代理通过实现接口,通过反射获取接口里面的方法,并且自定义InvocationHandler接口,实现方法拦截调用invoke方法目标类有接口1.8高于CGLIB
CGLIB动态代理继承机制,通过继承重写目标方法,使用MethodInterceptor调用父类的目标方法从而实现代理而实现代理调用interceptor方法不能使用final修饰的类和方法第一次调用生成字节码比较耗时间,多次调用性能还行

5 另一种CGLIB写法

@Test
public void testSave() throws IOException {
    //生成目标代理类
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\ideaProject\\CGLIB-test-01\\src");
    //得到方法拦截器
    final IStudentServiceImpl service1 = new IStudentServiceImpl();
    CglibInterceptor interceptor = new CglibInterceptor(new DaoTransaction());
    //使用CGLIB框架生成目标类的子类(代理类)实现增强
    Enhancer enhancer = new Enhancer();
    //设置父类字节码
    enhancer.setSuperclass(IStudentServiceImpl.class);
    //设置拦截处理
    enhancer.setCallback(new MethodInterceptor() {
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            DaoTransaction daoTransaction = new DaoTransaction();
            daoTransaction.before();
            Object obj = null;
            obj = method.invoke(service1,objects);//save
            //obj = methodProxy.invokeSuper(o,objects);//CGLIB$save$0
            daoTransaction.after();
            return obj;
        }
    });
    IStudentServiceImpl service = (IStudentServiceImpl)enhancer.create();
    service.save(new Student());
}
  • enhancer.create()来完成的初始化,把我们的父类和拦截器进行了记录缓存。

标签:Spring,Object,代理,剖析,Student,AOP,new,save,public
From: https://blog.csdn.net/2301_80027247/article/details/142415081

相关文章