java之架构基础-动态代理&cglib

简介:

本文核心主要参数动态代理和cglib;

在以前的文章中,有提及到动态代理,它要解决的就是,当我们的某些代码前面或后面都需要一些处理的时候,如写日志、事务控制、做agent、自动化代码跟踪等,此时会给你带来无限的方便,这是JVM级别的提供的一种代理机制,不过在这种机制下调用方法在JVM7出来前还没有invokeDynamic的时候,调用的效率是很低的,此时方法调用都是通过method的invoke去实现。

其基本原理是基于实现JVM提供的一个:

InvocationHandler的接口,实现一个方法叫:public Object invoke(Object proxyed, Method method, Object[] args);

创建类的时候,通过实例化这个类(这个类就是实现InvocationHandler的类),再将实际要实现的类的class放进去,通过Proxy来实例化。

以下为一段简单动态代理的实现代码(以下代码放入一个文件:DynamicProxy.java):

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

//定义了一个接口
interface Hello {
   public String getInfos1();
   public String getInfos2();
   public void setInfo(String infos1, String infos2);
   public void display();
}
//定义它的实现类
class HelloImplements implements Hello {

   private volatile String infos1;

   private volatile String infos2;

   public String getInfos1() {
      return infos1; 
   }

   public String getInfos2() {
     return infos2; 
   }

   public void setInfo(String infos1, String infos2) {
     this.infos1 = infos1;
     this.infos2 = infos2;
   }

   public void display() {
     System.out.println("\t\t" + infos1 + "\t" + infos2);
   }
}

定义AOP的Agent
class AOPFactory implements InvocationHandler {

   private Object proxyed;

   public AOPFactory(Object proxyed) {
     this.proxyed = proxyed;
   }

   public void printInfo(String info, Object... args) {
     System.out.println(info);
     if (args == null) {
       System.out.println("\t空值。");
     }else {
       for(Object obj : args) {
         System.out.println(obj);
       }
    }
}

public Object invoke(Object proxyed, Method method, Object[] args)  throws IllegalArgumentException, IllegalAccessException,
   InvocationTargetException {
     System.out.println("\n\n====>调用方法名:" + method.getName());
     Class<?>[] variables = method.getParameterTypes();
     for(Class<?>typevariables: variables) {
        System.out.println("=============>" + typevariables.getName());
     }
     printInfo("传入的参数为:", args);
     Object result = method.invoke(this.proxyed, args);
     printInfo("返回的参数为:", result);
     printInfo("返回值类型为:", method.getReturnType());
     return result;
  }
}
//测试调用类
public class DynamicProxy {

  public static Object getBean(String className) throws InstantiationException, IllegalAccessException,
     ClassNotFoundException {
      Object obj = Class.forName(className).newInstance();
      InvocationHandler handler = new AOPFactory(obj);
      return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
         .getClass().getInterfaces(), handler);
  }

  @SuppressWarnings("unchecked")
  public static void main(String[] args) {
    try {
        Hello hello = (Hello) getBean("dynamic.HelloImplements");
        hello.setInfo("xieyu1", "xieyu2");
        hello.getInfos1();
        hello.getInfos2();
        hello.display();
     } catch (Exception e) {
        e.printStackTrace();
     }
  }
}



OK,可以看看输出结果,此时的输出结果不仅仅有自己的Hello实现类的中打印结果,还有proxy代理中的结果,它可以捕获方法信息和入参数,也可以捕获返回结果,也可以操作方法,所以这种非侵入式编程本身就是侵入式的,呵呵!


好了,你会发现都有接口,有些时候写太多接口很烦,而且上面的调用性能的确不怎么样,除了JVM提供的动态代理,还有什么办法吗?有的,org的asm包可以动态修改字节码信息,也就是可以动态在内存中创建class类和修改class类信息;但是听起来貌似很复杂,cglib为我们包装了对asm的操作,整个ASM包的操作非常小,但是代码很精炼,很容易看懂。那么cglib实现的时候,就是通过创建一个类的子类,然后在调用时,子类方法肯定覆盖父类方法,然后子类在完成相关动作后,进行super的回调;


我们来看个例子(首先下载asm包,和cglib包,各个版本不同而不同,我使用的是asm-all-3.1.jar和cglib-2.2.jar):

下面的程序只需创建文件:CglibIntereceptor.java 

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

//创建一个类,用来做测试
class TestClass {
   public void doSome() {
     System.out.println("====>咿呀咿呀喂");
   }
}
public class CglibIntereceptor {

   static class MethodInterceptorImpl implements MethodInterceptor {
     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println(method);
        proxy.invokeSuper(obj, args);
        return null;
     }
}


  public static void main(String[] args) {
     Enhancer enhancer = new Enhancer();
     enhancer.setSuperclass(TestClass.class);
     enhancer.setCallback( new MethodInterceptorImpl() );
     TestClass my = (TestClass)enhancer.create();
     my.doSome();
  }
}


看看打印结果:

public void dynamic.TestClass.doSome()
====>咿呀咿呀喂


//注意看黑色粗体标识出来的代码,首先要实现MethodInterceptor,然后实现方法intercept,内部使用invokeSuper来调用父类;下面的实例都是通过Enhancer 来完成的;细节的后续我们继续探讨,现在就知道这样可以使用,而spring的真正实现也是类似于此,只是spring对于cglib的使用做了其他的包装而已;大家可以去看看spring对事务管理器的源码即可了解真相。


下面问题来了,我们有些时候对某些方法不想去AOP,因为我认为只有需要包装的才去包装,就像事务管理器中切入的时候,我们一般会配置一个模式匹配,哪些类和那些方法才需要做AOP;那么cglib怎么实现的,它提供了一个CallbackFilter来实现这个机制。OK,我们来看一个CallbackFilter的实例:

以下代码创建文件:CglibCallBackFilter.java

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;

class CallBackFilterTest {

  public void doOne() {
      System.out.println("====>1");
  }

  public void doTwo() {
      System.out.println("====>2");
  }
}

public class CglibCallBackFilter {

   static class MethodInterceptorImpl implements MethodInterceptor {
      public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
           System.out.println(method);
           return proxy.invokeSuper(obj, args);
     }
   }

    static class CallbackFilterImpl implements CallbackFilter {
           public int accept(Method method) {//返回1代表不会进行intercept的调用
           return ("doTwo".equals(method.getName())) ? 1 : 0;
       }
    }

 public static void main(String[] args) {
     Callback[] callbacks =
          new Callback[] { new MethodInterceptorImpl(),  NoOp.INSTANCE };
      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(CallBackFilterTest.class);
      enhancer.setCallbacks( callbacks );
      enhancer.setCallbackFilter( new CallbackFilterImpl());
      CallBackFilterTest callBackFilterTest = (CallBackFilterTest)enhancer.create();
      callBackFilterTest.doOne();
      callBackFilterTest.doTwo();
  }
}


看下打印结果:

public void dynamic.CallBackFilterTest.doOne()
====>1
====>2

可以看到只有方法1打印出了方法名,方法2没有,也就是方法2调用时没有调用:intercept来做AOP操作,而是直接调用的;可以看出,他上层有一个默认值,而callbacks里面设置了NoOp.INSTANCE,就代表了不做任何操作的一个实例,你应该懂了吧,就是当不做AOP的时候调用那种实例来运行,当需要AOP的时候调用那个实例来运行;怎么对应上的,它自己不知道,accept返回的是一个数组的下标,callbacks是一个数组,那么你猜猜是不是数组的下标呢,你自己换下位置就知道了,呵呵,是的,没错就是数组下标,不相信可以翻翻他的源码就知道了。


其实你可以看出cglib就是在修改字节码,貌似很方面,spring、Hibernate等也大量使用它,但是并不代表你可以大量使用它,尤其是在写业务代码的时候,只有写框架才可以适当考虑使用这些东西,spring的反射等相关一般都是初始化决定的,一般都是单例的,前面谈及到JVM时,很多JVM优化原则都是基于VM的内存结构不会发生变化,如果发生了变化,那么优化就会存在很多的问题了,其次无限制使用这个东西可能会使得VM的Perm Gen内存溢出。


最后我们看个实际应用中没啥用途,但是cglib实现的一些东西,java在基于接口、抽象类的情况下,实现了很多特殊的机制,而cglib可以将两个根本不想管的接口和类合并到一起来操作,这也是字节码的一个功劳,呵呵,它的原理就是在接口下实现了子类,并把其他两个作为回调的方法,即可实现,但是实际应用中这种用法很诡异,cglib中是使用:Mixin来创建,而并非Enhancer了。例子如下:

创建文件:CglibMixin.java

import net.sf.cglib.proxy.Mixin;


interface Interface1 {
   public void doInterface1();
}
interface Interface2 {
   public void doInterface2();
}
class ImpletmentClass1 implements Interface1 {
   public void doInterface1() {
      System.out.println("===========>方法1");
   }
}
class ImpletmentClass2 implements Interface2 {
   public void doInterface2() {
      System.out.println("===========>方法2");
   }
}


public class CglibMixin {


   public static void main(String []args) {
       Class<?>[] interfaces =
             new Class[] { Interface1.class, Interface2.class };
       Object[] implementObjs =
             new Object[] { new ImpletmentClass1(), new ImpletmentClass2()};
       Object obj = Mixin.create(interfaces,implementObjs);
       Interface1 interface1 = (Interface1)obj;
       Interface2 interface2 = (Interface2)obj;
       interface1.doInterface1();
       interface2.doInterface2();
   }
}


结果就不用打印了,上面有描述,主要是两个接口、两个实例,最终用一个对象完成了,传递过程中只有一个,比起传统意义上的多态更加具有多态的效果,呵呵,不过还是建议少用。

本文只是简单阐述框架级别动态代理和cglib的实现,后续会深入探讨一些cglib的实现细节和功能,以及如何在框架中抽象出模型出来。

目录
相关文章
|
消息中间件 Java Kafka
Java 事件驱动架构设计实战与 Kafka 生态系统组件实操全流程指南
本指南详解Java事件驱动架构与Kafka生态实操,涵盖环境搭建、事件模型定义、生产者与消费者实现、事件测试及高级特性,助你快速构建高可扩展分布式系统。
584 7
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
消息中间件 Java 数据库
Java 基于 DDD 分层架构实战从基础到精通最新实操全流程指南
本文详解基于Java的领域驱动设计(DDD)分层架构实战,结合Spring Boot 3.x、Spring Data JPA 3.x等最新技术栈,通过电商订单系统案例展示如何构建清晰、可维护的微服务架构。内容涵盖项目结构设计、各层实现细节及关键技术点,助力开发者掌握DDD在复杂业务系统中的应用。
2180 0
|
人工智能 安全 Java
智慧工地源码,Java语言开发,微服务架构,支持分布式和集群部署,多端覆盖
智慧工地是“互联网+建筑工地”的创新模式,基于物联网、移动互联网、BIM、大数据、人工智能等技术,实现对施工现场人员、设备、材料、安全等环节的智能化管理。其解决方案涵盖数据大屏、移动APP和PC管理端,采用高性能Java微服务架构,支持分布式与集群部署,结合Redis、消息队列等技术确保系统稳定高效。通过大数据驱动决策、物联网实时监测预警及AI智能视频监控,消除数据孤岛,提升项目可控性与安全性。智慧工地提供专家级远程管理服务,助力施工质量和安全管理升级,同时依托可扩展平台、多端应用和丰富设备接口,满足多样化需求,推动建筑行业数字化转型。
545 5
|
12月前
|
负载均衡 Java API
grpc-java 架构学习指南
本指南系统解析 grpc-java 架构,涵盖分层设计、核心流程与源码结构,结合实战路径与调试技巧,助你从入门到精通,掌握高性能 RPC 开发精髓。
1040 8
Java程序员必学:JVM架构完全解读
Java 虚拟机(JVM)是 Java 编程的核心,深入理解其架构对开发者意义重大。本文详细解读 JVM 架构,涵盖类加载器子系统、运行时数据区等核心组件,剖析类加载机制,包括加载阶段、双亲委派模型等内容。阐述内存管理原理,介绍垃圾回收算法与常见回收器,并结合案例讲解调优策略。还分享 JVM 性能瓶颈识别与调优方法,分析 Java 语言特性对性能的影响,给出数据结构选择、I/O 操作及并发同步处理的优化技巧,同时探讨 JVM 安全模型与错误处理机制,助力开发者提升编程能力与程序性能。
Java程序员必学:JVM架构完全解读
|
算法 架构师 Java
Java 开发岗及 java 架构师百度校招历年经典面试题汇总
以下是百度校招Java岗位面试题精选摘要(150字): Java开发岗重点关注集合类、并发和系统设计。HashMap线程安全可通过Collections.synchronizedMap()或ConcurrentHashMap实现,后者采用分段锁提升并发性能。负载均衡算法包括轮询、加权轮询和最少连接数,一致性哈希可均匀分布请求。Redis持久化有RDB(快照恢复快)和AOF(日志更安全)两种方式。架构师岗涉及JMM内存模型、happens-before原则和无锁数据结构(基于CAS)。
466 5
|
缓存 Java 数据库
Java 项目分层架构实操指南及长尾关键词优化方案
本指南详解基于Spring Boot与Spring Cloud的Java微服务分层架构,以用户管理系统为例,涵盖技术选型、核心代码实现、服务治理及部署实践,助力掌握现代化Java企业级开发方案。
589 2
|
机器学习/深度学习 人工智能 Java
Java 技术支撑下 AI 与 ML 技术融合的架构设计与落地案例分析
摘要: Java与AI/ML技术的融合为智能化应用提供了强大支持。通过选用Deeplearning4j、DJL等框架解决技术适配问题,并结合Spring生态和JVM优化提升性能。在金融风控、智能制造、医疗影像等领域实现了显著效果,如审批效率提升3倍、设备停机减少41%、医疗诊断延迟降低80%。这种技术融合推动了多行业的智能化升级,展现了广阔的应用前景。
842 0