当前位置: 首页 > 后端技术 > Java

【曹公杂谈】说说Maven框架和插件的契约

时间:2023-04-02 09:39:02 Java

说说Maven框架和插件的契约在这个平台做生态共建。Maven也是如此。其实就是一个插件执行的框架。Maven刚开始肯定不知道谁来贡献插件。如果插件以各种方式编写,对于平台方来说可能是一场灾难。所以,平台方要负责制定标准,在我的平台上写插件,必须要做的事情。Maven为插件设置了契约,这个契约是通过apijar包来实现的。每次发布新版本的Maven,都会附带一个apijar包。如果有人要基于这个版本的apijar包开发插件,需要在自己的插件工程中引入这个插件。然后根据apijar包中的合约接口实现自己的插件逻辑。比如mavenclean插件的项目代码依赖apijar包。如下:apijar包中的合约接口是什么样子的?publicinterfaceMojo{...voidexecute()throwsMojoExecutionException,MojoFailureException;}核心方法就是这个,只要实现这个接口就大功告成了。作为框架党,如何调用这个插件呢?简而言之就是:1.找到插件的实现类jar包,然后为插件构造一个类加载器,加载jar包,然后找到对应的实现合约接口的类,比如这里的CleanMojo2,加载CleanMojo类创建之后,当然是通过反射生成对象,然后cast到合约接口,然后调用合约接口。例如:ClasscleanMojoClass=插件的类加载器加载插件的jar包;MojocleanMojo=(Mojo)cleanMojoClass.newInstance();cleanMojo.execute();到目前为止,我们的理论知识已经足够了,我们可以展示代码吗?工程实践我们将模拟上述过程,构建一个Maven模块来存放插件api合约接口;构建Maven模块,导入api,实现插件api,这样我们的插件就实现了;接下来,把这两个项目编译好,把jar包安装到本地仓库;新建一个项目,模拟Maven框架加载插件,执行插件。插件api工程直接使用maven的archetype中的quickstart新建模块,很简单,一个界面就可以:然后执行mvninstall安装到本地仓库。插件实现工程在pom里面,我们会引入api。org.examplemy-plugin-api1.0-SNAPSHOT代码也是很简单,只是一个实现类。然后执行mvninstall安装到本地仓库。主工程,调用插件的模拟框架主工程是模拟我们的Maven框架。既然我们调用插件,那肯定是通过api,所以必须在pom.xml中引入api。org.examplemy-plugin-api1.0-SNAPSHOT接下来,我们写了一个测试类:publicstaticvoidmain(String[]args)throwsMalformedURLException,ClassNotFoundException,InstantiationException,IllegalAccessException{//1.1处URLurlForPluginApi=newURL("file:/C:\\Users\\Administrator\\.m2\\repository\\org\\example\\my-plugin-api\\1.0-SNAPSHOT\\my-plugin-api-1.0-SNAPSHOT.jar");URLurlForPluginImpl=newURL("file:/C:\\Users\\Administrator\\.m2\\repository\\org\\example\\my-plugin-implementation\\1.0-SNAPSHOT\\my-plugin-implementation-1.0-SNAPSHOT.jar");URL[]urls={urlForPluginApi,urlForPluginImpl};//1.2URLClassLoaderurlClassLoader=newURLClassLoader(urls,ClassLoader.getSystemClassLoader()){@OverridepublicClassloadClass(Stringname)throwsClassNotFoundException{try{//保证:找类的时候,先搜索自己的类路径,如果找不到,再交给父类加载器Classclazz=findClass(名称);返回克拉兹;}catch(ClassNotFoundException异常){returnsuper.loadClass(name);}}};//1.3类implClazzByPluginClassloader=urlClassLoader.loadClass("org.example.MyMojoImplementation");//1.4MojoInterfacemojoInterface=(MojoInterface)implClazzByPluginClassloader.newInstance();//1.5mojoInterface.execute();System.out.println("你好,世界!");}简单解释一下上面的代码:在1.1处,构造了两个url,指向我本地仓库中的两个文件,即插件对应实现的api.jar和jar1.2。使用1.1中的url,构造了一个classloader。这个类加载器的父类加载器就是我们传递的,系统的Ap??pClassloader同时,我们重写了这个类加载器的行为。改写后的行为是:遇到要加载的类,先加载它,即在它的两个url中搜索,看能不能找到。如果没有,它将进入异常。异常被我们捕获后,交给父类加载器加载;1.3时,我们使用新建的classloader加载1.4时插件的实现类,使用1.3时加载的实现类的类。反射生成的对象强制为MojoInterface接口对象1.5,插件逻辑以多态方式执行。你不妨考虑一下。你认为最终的执行结果是什么?我们的“helloworld”可以打印出来吗?我们把这段代码上传到了gitee,大家可以拉下来看。https://gitee.com/ckl111/mave...给大家看一下执行结果:大家看看,这说得通吗?很明显,接口是在我的插件代码中实现的,为什么不能上去改造呢?:publicclassMyMojoImplementationimplementsMojoInterface{@Overridepublicvoidexecute(){System.out.println("执行业务逻辑");}}这。..怎么说。..这样给大家解释一下,我们在加载MyMojoImplementation的时候,找到了这个类,也实现了接口MojoInterface,那么这个接口类也需要加载,因为我们的类加载器已经被重写了(先自己加载),所以,最后,MojoInterface和MyMojoImplementation一样,由插件类加载器加载。最终在向上转型的过程中,会出现如下情况。如果两侧不匹配,就会报错。MojoInterface(框架中的这个类由框架类加载器加载)mojoInterface=(MojoInterface)implClazzByPluginClassloader.newInstance();(该实现类实现的接口由插件类加载器加载)修改代码,改成如下。结果,我们可以运行我们的helloworld。为什么?我是朱日,深圳腾讯员工,反复跳槽成都和深圳的后端java程序员。我在深圳3年,然后去了成都4年。需要推荐的可以找我。对一线编码实践、网络、数据库、高并发等有浓厚兴趣,也欢迎大家加我,拉进技术群一起交流;也可以在腾讯内推找到我。本文由博客多发平台OpenWrite发布!