Maven打包Groovy为Jar后运行报错AbstractMethodError求助
Hey there, let's break down why you're hitting this AbstractMethodError when using your Groovy-built Jar in another project. This error almost always boils down to a mismatch between how your Jar was compiled and how it's being run, so let's walk through the most likely causes and fixes.
The Problem Recap
You successfully built your Groovy code into a Jar using mvn package, imported it into another project, and it compiles fine—but when you actually call the Request class's ViewPoint method, you get an error pointing to pefservices.restapi.Request.getProperty(Ljava/lang/String;)Ljava/lang/Object;. The stack trace shows this happens when a closure inside ViewPoint tries to access the cache property.
Full Error Log
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.AbstractMethodError: pefservices.restapi.Request.getProperty(Ljava/lang/String;)Ljava/lang/Object;
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1915)
at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:234)
at pefservices.restapi.Request.ViewPoint(Request.groovy:185)
at pefservices.restapi.Request.ViewPoint(Request.groovy)
at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:234)
at pefservices.restapi.Client.profile(Client.groovy:59)
at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:234)
at pefservices.restapi.Client.getName(Client.groovy:20)
at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:234)
at pefservices.viewpoint.Client.(Client.groovy:30)
at pefservices.viewpoint.Client.(Client.groovy)
at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:234)
at pefservices.viewpoint.User.(User.groovy:35)
at pefservices.viewpoint.User.(User.groovy)
at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:234)
at pefservices.setup.Session.enableFramework(Session.groovy:188)
at pefservices.setup.Session.enableFramework(Session.groovy)
at pefservices.setup.Session$enableFramework.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:135)
at main.App.main(App.groovy:15)
Caused by: java.lang.AbstractMethodError: pefservices.restapi.Request.getProperty(Ljava/lang/String;)Ljava/lang/Object;
at org.codehaus.groovy.runtime.InvokerHelper.getProperty(InvokerHelper.java:190)
at groovy.lang.Closure.getPropertyTryThese(Closure.java:313)
at groovy.lang.Closure.getPropertyOwnerFirst(Closure.java:307)
at groovy.lang.Closure.getProperty(Closure.java:296)
at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:234)
at pefservices.restapi.Request$_ViewPoint_closure1.doCall(Request.groovy:182)
at pefservices.restapi.Request$_ViewPoint_closure1.doCall(Request.groovy)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:101)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:263)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1041)
at groovy.lang.Closure.call(Closure.java:405)
at org.codehaus.groovy.runtime.ConvertedClosure.invokeCustom(ConvertedClosure.java:50)
at org.codehaus.groovy.runtime.ConversionHandler.invoke(ConversionHandler.java:122)
at com.sun.proxy.$Proxy7.get(Unknown Source)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
The Request Class Code
import java.nio.CharBuffer import java.util.concurrent.CompletableFuture import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import groovy.json.JsonOutput import okhttp3.MediaType import okhttp3.MultipartBody import okhttp3.OkHttpClient import okhttp3.Request as Req import okhttp3.RequestBody import okhttp3.Response import okhttp3.OkHttpClient.Builder as ClientBuilder import okhttp3.Request.Builder import okio.Buffer import pefservices.restapi.User as UserAPI import pefservices.setup.* public class Request { Map <String, String> cache = new HashMap <String, String>() Map <String, CompletableFuture <?>> inProcess = new HashMap <String, CompletableFuture <?>>() //includes Authorization public def ViewPoint(String url, String RequestMethod, def requestBody='', Map headers=[:], boolean asAdmin=true, def asUser=null) { if(!url.startsWith("http://")) { url='http://'+url } String toBeCached = url+RequestMethod+requestBody+headers+Values+asAdmin+asUser waitForRequestifExists(toBeCached) if(!cache.containsKey(toBeCached)) { def inputLine inProcess.put(toBeCached, new CompletableFuture().supplyAsync( { OkHttpClient client = new ClientBuilder().connectTimeout(120, TimeUnit.SECONDS).readTimeout(120, TimeUnit.SECONDS).build(); println "restAPI [$RequestMethod] URL: "+url Builder builder =new Builder().url(url) // headers+=["Accept-Encoding" :"gzip, deflate"] headers+=["Accept" :"application/json, text/plain, */*"] headers+=["Accept-Language" :"en-US,en;q=0.5"] headers+=["Expires" :"-1"] headers+=["Connection" :"keep-alive"] if(requestBody instanceof File) { String boundary = "-------------" + System.currentTimeMillis(); headers+=['Content-Type':'multipart/form-data; boundary='+boundary] } if(!headers.containsKey("Content-Type")) { headers+=["Content-Type" :"application/json; charset=UTF-8; odata.metadata=minimal"] } if (!url.contains("api/open") && url.contains(Session.url)) { if (asAdmin==true) { headers+=["Authorization" :Session.tokens['adminToken']]; } else if(asUser!=null) { UserAPI userAPI = new UserAPI(); headers+=["Authorization" :userAPI.loginPost(asUser).Token] } else{ headers+=["Authorization" :Session.tokens['userToken']]; } } for(entry in headers) { builder.addHeader(entry.key, entry.value) } if (!(requestBody instanceof String) && headers['Content-Type'].toString().contains("json")) { requestBody = JsonOutput.toJson(requestBody) } if (RequestMethod != 'GET') { try{ println "Json body:->"+ JsonOutput.prettyPrint(requestBody) } catch (Throwable ex) { println "body:"+ requestBody } if(requestBody instanceof File) { RequestBody body = RequestBody.create(MediaType.parse(headers['Content-Type']), requestBody) RequestBody formBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("file", requestBody.getName(), body) .build() builder.method(RequestMethod, formBody) } else{ RequestBody body = RequestBody.create(MediaType.parse(headers['Content-Type']), requestBody) builder.method(RequestMethod, body) } } Req request = builder.build() Response response = client.newCall(request).execute() int responseCode = response.code() String responseTitle = response.message() if(response.header("Content-Disposition")?.contains('attachment')) { String header =response.header("Content-Disposition") String fileExtension = header.substring(header.lastIndexOf('.')) println "Content-Disposition: attachment - File Extension ->$fileExtension" File file = new File(".\TestFiles\temp$fileExtension") InputStream IS= response.body().byteStream() BufferedInputStream input = new BufferedInputStream(IS); OutputStream output = new FileOutputStream(file); byte[] data = new byte[1024]; long total = 0 int count while ((count =input.read(data)) != -1) { total += count; output.write(data, 0, count); } output.flush(); output.close(); input.close(); inputLine = file } else { inputLine = response.body().string() } println "RESTAPI Response Code: -> " + responseCode if (responseCode >= 400) { println "Message: -> " + responseTitle println "Error Resposne body: -> "+inputLine throw new Exception("Error Code =$responseCode URL: $url") } // 报错行 cache[toBeCached] = inputLine }, new Executors().newSingleThreadExecutor())) inProcess[toBeCached].get(120, TimeUnit.SECONDS) inProcess.remove(toBeCached) if(RequestMethod == "POST") {println "restapi.Request() ->"+inputLine } return inputLine } else { return cache.get(toBeCached) } } public void waitForRequestifExists(String key) { if(inProcess.containsKey(key)) { this.inProcess[key].get(120, TimeUnit.SECONDS) } } public void clearCache() { this.cache = new HashMap <String, String>() this.inProcess = new HashMap <String, CompletableFuture <?>>() } }
Root Causes & Fixes
1. Mismatched Groovy Versions (Most Likely)
Groovy dynamically generates methods like getProperty for classes that implement GroovyObject (which all Groovy classes do by default). If you compiled your Jar with Groovy version X, but the project importing the Jar uses Groovy version Y, the method signatures might not line up—leading to this error when the runtime tries to call a method that doesn't exist in its version of Groovy.
Fix:
- Check the
pom.xml(or build.gradle) for both your Jar project and the importing project. Make sure the Groovy dependency version is identical in both. For example, if your Jar usesgroovy-all:3.0.17, ensure the importing project doesn't use3.0.18or an older version like2.5.21.
2. Incorrect Jar Packaging
If you used Maven's default Java compiler plugin to build your Groovy code instead of the Groovy Maven plugin, your Jar might not have the proper bytecode for Groovy's dynamic features. The Java compiler doesn't generate the GroovyObject interface methods that Groovy needs at runtime.
Fix:
- Update your Jar project's
pom.xmlto use thegroovy-maven-pluginfor compilation. Here's a quick snippet:<build> <plugins> <plugin> <groupId>org.codehaus.gmavenplus</groupId> <artifactId>gmavenplus-plugin</artifactId> <version>1.13.1</version> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> </plugin> </plugins> </build> - Rebuild your Jar with this plugin—this ensures the bytecode includes all necessary Groovy runtime methods.
3. Closure Property Access Issues
The error happens inside the async closure in ViewPoint when it tries to access cache[toBeCached]. Groovy closures use getProperty under the hood to access outer class properties. If the closure's runtime environment doesn't have the right Groovy setup, this call fails.
Fix:
- Explicitly reference the outer class instance in the closure to bypass the implicit
getPropertycall. Change the line:
to:cache[toBeCached] = inputLine
This tells Groovy exactly where to find theRequest.this.cache[toBeCached] = inputLinecacheproperty without relying on the dynamicgetPropertymethod.
4. Class Loader Conflicts
In some cases, if the importing project uses multiple class loaders (like in Spring Boot or application servers), Groovy classes might be loaded twice—once by the Jar's class loader and once by the project's. This causes GroovyObject to be considered two different types, leading to method not found errors.
Fix:
- Ensure Groovy dependencies are marked as
compileorruntime(notprovided) in both projects to avoid class loader mismatches. - If you're using Spring Boot, check that you're not including Groovy in both the Jar and the Boot project's dependencies—use
dependencyManagementto align versions.
Next Steps
Start with checking Groovy versions first—it's the most common culprit. If that doesn't work, rebuild your Jar with the Groovy Maven plugin. If you still hit issues, try the explicit closure property reference.
内容的提问来源于stack exchange,提问作者Miguel Acosta

