0


Spring中AOP的底层原理剖析

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
packagecom.ff.service;/**
 * @author 
 * @version 1.0
 */publicinterfaceIStudentService{publicvoidsave();Studentquery(Long id);}
  • 创建service实现类(需要代理的类)
publicclassIStudentServiceImplimplementsIStudentService{publicvoidsave(){System.out.println("保存学生信息");}publicStudentquery(Long id){System.out.println("查询操作");Student student =newStudent();
        student.setAge(18);
        student.setName("sy");return student;}}
  • 增强类
publicclassDaoTransaction{publicvoidbefore(){System.out.println("开启事务操作");}publicvoidafter(){System.out.println("关闭事务");}}
2.2 实现InvocationHandler接口
  • InvocationHandler接口,用来做方法拦截
  • 实现接口
publicclassTransactionHandlerimplementsInvocationHandler{//增强类对象privateDaoTransaction transaction;//需要代理的对象privateObject object;publicTransactionHandler(DaoTransaction transaction,Object object){this.transaction = transaction;this.object = object;}publicObjectinvoke(Object proxy,Method method,Object[] args)throwsThrowable{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创建代理实例publicstaticObjectnewProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)- - ClassLoader类加载器,直接通过需要代理的类获得就行- Class[]:目标类所实现的所有接口- InvocationHandler:方法拦截处理器,可以在里面实现方法的增强- Method:执行目标方法的,invoke方法执行- args:参数数组
2.3 测试
  • 方法
@TestpublicvoidtestSave(){//增强类对象DaoTransaction daoTransaction =newDaoTransaction();//目标执行类IStudentServiceImpl iStudentService =newIStudentServiceImpl();//方法拦截处理器TransactionHandler handler =newTransactionHandler(daoTransaction, iStudentService);//获取代理实例对象IStudentService proxyStudentService =(IStudentService)Proxy.newProxyInstance(IStudentServiceImpl.class.getClassLoader(),IStudentServiceImpl.class.getInterfaces(), handler);
        proxyStudentService.save();}
2.4 底层运行原理
  • 生成代理类的字节码来学习
/**
 * 使用来生成字节码学习使用
 */privatevoidsaveProxyClass(String path)throwsIOException{byte[] $proxy1s =ProxyGenerator.generateProxyClass("$proxy1",IStudentServiceImpl.class.getInterfaces());FileOutputStream fileOutputStream =null;try{
        fileOutputStream =newFileOutputStream(newFile(path +"$Proxy1.class"));
        fileOutputStream.write($proxy1s);}catch(Exception e){
        e.printStackTrace();}finally{if(fileOutputStream !=null){
            fileOutputStream.flush();
            fileOutputStream.close();}}}
  • 生成字节码反编译结果
publicfinalclass $proxy1 extendsProxyimplementsIStudentService{privatestaticMethod m1;privatestaticMethod m4;privatestaticMethod m2;privatestaticMethod m0;privatestaticMethod m3;public $proxy1(InvocationHandler var1)throws{super(var1);}publicfinalbooleanequals(Object var1)throws{try{return(Boolean)super.h.invoke(this, m1,newObject[]{var1});}catch(RuntimeException|Error var3){throw var3;}catch(Throwable var4){thrownewUndeclaredThrowableException(var4);}}publicfinalStudentquery(Long var1)throws{try{return(Student)super.h.invoke(this, m4,newObject[]{var1});}catch(RuntimeException|Error var3){throw var3;}catch(Throwable var4){thrownewUndeclaredThrowableException(var4);}}publicfinalStringtoString()throws{try{return(String)super.h.invoke(this, m2,(Object[])null);}catch(RuntimeException|Error var2){throw var2;}catch(Throwable var3){thrownewUndeclaredThrowableException(var3);}}publicfinalinthashCode()throws{try{return(Integer)super.h.invoke(this, m0,(Object[])null);}catch(RuntimeException|Error var2){throw var2;}catch(Throwable var3){thrownewUndeclaredThrowableException(var3);}}publicfinalvoidsave(Student var1)throws{try{super.h.invoke(this, m3,newObject[]{var1});}catch(RuntimeException|Error var3){throw var3;}catch(Throwable var4){thrownewUndeclaredThrowableException(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){thrownewNoSuchMethodError(var2.getMessage());}catch(ClassNotFoundException var3){thrownewNoClassDefFoundError(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>
  • 准备需要代理的目标类
publicclassIStudentServiceImplimplementsIStudentService{publicvoidsave(Student student){System.out.println("保存学生信息");}publicStudentquery(Long id){System.out.println("查询操作");Student student =newStudent();
        student.setAge(18);
        student.setName("sy");return student;}}
publicinterfaceIStudentService{publicvoidsave(Student student);Studentquery(Long id);}
3.2实现方法拦截
  • 实现方法拦截
publicclassCglibInterceptorimplementsMethodInterceptor{DaoTransaction transaction;publicCglibInterceptor(DaoTransaction transaction){this.transaction = transaction;}publicObjectintercept(Object o,Method method,Object[] objects,MethodProxy methodProxy)throwsThrowable{//事务增强
        transaction.before();Object ret = methodProxy.invokeSuper(o, objects);
        transaction.after();return ret;}}
3.3测试
@TestpublicvoidtestSave()throwsIOException{//得到方法拦截器CglibInterceptor interceptor =newCglibInterceptor(newDaoTransaction());//使用CGLIB框架生成目标类的子类(代理类)实现增强Enhancer enhancer =newEnhancer();//设置父类字节码
    enhancer.setSuperclass(IStudentServiceImpl.class);//设置拦截处理
    enhancer.setCallback(interceptor);IStudentServiceImpl service =(IStudentServiceImpl)enhancer.create();
    service.save(newStudent());}
3.4底层运行原理
  • 底层运行原理图

在这里插入图片描述

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

4 总结

代理类型实现机制回调方式适用场景效率JDK动态代理通过实现接口,通过反射获取接口里面的方法,并且自定义

InvocationHandler

接口,实现方法拦截调用invoke方法目标类有接口1.8高于CGLIBCGLIB动态代理继承机制,通过继承重写目标方法,使用MethodInterceptor调用父类的目标方法从而实现代理而实现代理调用interceptor方法不能使用final修饰的类和方法第一次调用生成字节码比较耗时间,多次调用性能还行

5 另一种CGLIB写法

@TestpublicvoidtestSave()throwsIOException{//生成目标代理类System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\ideaProject\\CGLIB-test-01\\src");//得到方法拦截器finalIStudentServiceImpl service1 =newIStudentServiceImpl();CglibInterceptor interceptor =newCglibInterceptor(newDaoTransaction());//使用CGLIB框架生成目标类的子类(代理类)实现增强Enhancer enhancer =newEnhancer();//设置父类字节码
    enhancer.setSuperclass(IStudentServiceImpl.class);//设置拦截处理
    enhancer.setCallback(newMethodInterceptor(){publicObjectintercept(Object o,Method method,Object[] objects,MethodProxy methodProxy)throwsThrowable{DaoTransaction daoTransaction =newDaoTransaction();
            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(newStudent());}
  • enhancer.create()来完成的初始化,把我们的父类和拦截器进行了记录缓存。
标签: spring java 后端

本文转载自: https://blog.csdn.net/2301_80027247/article/details/142415081
版权归原作者 不爱笑的良田 所有, 如有侵权,请联系我们删除。

“Spring中AOP的底层原理剖析”的评论:

还没有评论