一、后台某服务需要连接运行多个组件(每个组件可能有多个版本),如kafka、mongodb、es、mysql等,后续需要适配更多组件。主要难点:连接运行多个组件、多个版本,同一组件不同版本依赖的jar包可能不同,运行的源码也可能发生变化。项目不能直接依赖jar包,会造成类冲突。不同组件的不同版本依赖不同的jar包。我们可以借鉴tomcat的实现,通过自定义类加载器打破双亲委派机制,实现类隔离,从而达到操作多组件、多版本的目的。2.1创建依赖目录对于每个组件,我们创建一个目录,如/data/kafka、/data/mongodb、/data/es等,并为每个组件的不同版本创建相应的子目录,如/data/kafka/0.10,/data/kafka/0.11,目录结构如下|----/数据|------/卡夫卡|----------/0.10|-----------/0.11|--------/mysql|----------/5.7|----------/8.0|...将每个组件不同版本对应的依赖包放在每个子目录下。2.2定义操作接口在commonpublic模块中定义一个接口AbstractOperator,定义了一些常用的方法,如下:publicinterfaceOperator{/***testconnection*@paramconnectionInfo*@return*/booleantestConnection(StringconnectionInfo);/***获取组件版本*@return*/StringgetVersion(StringconnectionInfo);}定义各个组件的接口,如KafkaOperator、MysqlOperator等,让它们继承通用的接口。组件接口包含组件本身的一些操作。例如在KafkaOperator中定义了getTopics、createTopic、deleteTopic等方法。代码如下:publicinterfaceKafkaOperatorextendsOperator{/***获取主题列表*@paramconnectionInfo*@return*/ListgetTopics(StringconnectionInfo);/***创建主题*@paramconnectionInfo*@paramtopic*@return*/booleancreateTopic(StringconnectionInfo,Stringtopic);/***deletetopic*@paramconnectionInfo*@paramtopic*@return*/booleandeleteTopic(StringconnectionInfo,Stringtopic);}2.3编写构建业务包一般步骤如下:针对各个组件的不同版本,可以在项目下新建一个模块,依赖commonpublic模块创建入口类com.kamier.Entry(所有组件不同版本的入口类全限定名统一为com.kamier.Entry),并实现相应的组件接口,如Kafka0.10版本,则实现KafkaOperator接口。编写业务逻辑代码publicclassEntryimplementsKafkaOperator{@OverridepublicListgetTopics(StringconnectionInfo){returnnull;}@OverridepublicbooleancreateTopic(StringconnectionInfo,Stringtopic){returnfalse;}@OverridepublicbooleandeleteTopic(StringconnectionInfo,Stringtopic){returnfalse;}@OverridepublicbooleantestConnection(StringconnectionInfo){返回false;}@OverridepublicStringgetVersion(StringconnectionInfo){返回空值;}}制作一个jar包,将jar包放在对应的目录下,与依赖包同级,比如/data/kafka/0.102.4经过前面自定义类加载器的准备工作,有相应的各版本组件目录下的依赖包和业务包。开始编写自定义类加载器,继承URLClassLoader,重写loadClass方法,优先加载当前类加载器路径下的类,打破双亲委派模式。代码如下publicstaticclassMyClassLoaderextendsURLClassLoader{publicMyClassLoader(URL[]urls){super(urls);}publicClass>loadClass(Stringname)throwsClassNotFoundException{synchronized(getClassLoadingLock(name)){//首先检查当前类加载器是否加载了类Class>c=findLoadedClass(name);if(c==null){try{//在当前类加载器的路径中查找c=findClass(name);}catch(ClassNotFoundExceptione){//表示在当前类加载器的路径中没有找到}if(c==null){//使用双亲委托机制if(getParent()!=null){c=getParent().loadClass(名称);}}}返回c;针对每个组件的不同版本,我们创建对应的自定义类加载器,并将该版本对应目录下的所有jar包(包括依赖包和业务包)的url传递给2.5main流程步骤如下:当我们收到页面请求获取Kafka(0.10版本)的topic列表时,首先判断Kafka(0.10版本)的类加载器是否已经初始化,如果还没有初始化,继续进行类加载器初始化。URL[]urls=null;Filedir=newFile("/data/kafka/0.10");if(dir.isDirectory()){File[]files=dir.listFiles();urls=新URL[文件。长度];for(inti=0;ia=(List)method.invoke(输入,参数);获取结果并返回。3.小结至此,整个实现步骤就结束了。我们通过自定义类加载器来实现类隔离,从而达到操作多组件、多版本的目的。