Is that possible implement the same code but only enabled when adding a dependency to SpringBoot project? If possible, how to achieve it?
I want to implement the code like this:
DoSomethingUtil doSomethingUtil = new DoSomethingUtil();
doSomethingUtil.send("API URL", "System A", "Hello");
It would do nothing when project didn't add the implement of the DoSomethingUtil.java.
After adding to pom.xml that which would implement the DoSomethingUtil.java, it would really do something.
CodePudding user response:
Given that you don't need to know about DoSomethingUtil
anywhere else in your code, you can run something on it only if it's present in your classpath (without importing it) if you use reflection all the way:
try {
Class<?> dsuClass = Class.forName("do.something.util.DoSomethingUtil");
Object dsuInstance = dsyClass.getConstructor().newInstance();
Method sendMethod = dsuClass.getDecaredMethod("send", String.class, String.class, String.class);
sendMethod.invoke(dsuInstance, "API URL", "System A", "Hello");
} catch (Exception ignored) {}
You may want to revisit the poor error handling above to distinguish (at least) between class not being present in the classpath and send()
method invocation failure.
CodePudding user response:
What you appear to be describing is adding a dependency, not "importing" something.
Will it work?
Sort of. What you could do is overlay the definition of the.pkg.DoSomethingUtil
with another version of the.pkg.DoSomethingUtil
in a different JAR file. It can work, but it makes your application sensitive to the order of the JARs on the runtime classpath. That makes your application fragile ... to say the least.
You can probably make this work with classic Java if you have full control of the runtime classpath. However:
I'm not sure if it will work with SpringBoot.
If you tried this sort of thing on Android, the APK builder would protest. It treats the scenario of two classes with the same full name as an error.
I think there is a better solution:
Refactor the code so that there is a
DoSomethingUtil
interface and two classes; e.g.RealDoSomethingUtil
andDummyDoSomethingUtil
.Replace
new DoSomethingUtil()
with a call to a factory method.Implement the factory method something like this:
private static Class<?> doSomethingClass; public static synchronized DoSomethingUtil makeDoSomethingUtil() { if (doSomethingClass == null) { try { doSomethingClass = Class.forName("the.pkg.RealDoSomethingUtil"); } catch (Exception ex) { doSomethingClass = the.pkg.DummyDoSomethingUtil.class; } } return (DoSomethingUtil) (doSomethingClass.newInstance()); }
Put
RealDoSomethingUtil
into the add-on JAR file, andDoSomethingUtil
,RealDoSomethingUtil
and the factory method into the main JAR file.
You should probably make the exception handling more selective so that it deals with different classloader errors differently. For example, if RealDoSomethingUtil
exists but can't be loaded, you probably should log that ... or maybe let the exception crash the application.
You could also make use of ServiceLoader
, but I don't know if it would be simpler ...
CodePudding user response:
The java Service Provide API (SPI) is there to detect wether implementation(s) of an interface exists.
- You have a jar with an interface
DoSomethingUtil
in your application. - Possibly on the class path an implementation jar (
MyDoSomethingUtilImpl implements DoSomethingUtil
), with an entry in META-INF/services. - You must check whether the interface is implemented.
- One could make a fallback implementation.