[英]Calling Class.newInstance with null arguments causing IllegalArgumentException
[英]class.newInstance creates Object with all properties being null
我有一個需要像這樣動態初始化的類:
void doSth(classsuffix) throws Exception {
String classname = "org.test.classname" + classsuffix; // classsuffix is dynamic
Class<?> clazz;
clazz = Class.forName(classname);
TestInterface test = (TestInterface) clazz.newInstance();
test.doStuff();
}
與示例類配對(遵循相同模式的許多示例之一):
public class classnameOne implements TestInterface {
@Inject
private Logger log;
// ...
@Override
public void doStuff() {
// Do stuff
log.info("done");
}
}
問題是初始化時classnameOne
類中的log
將為null
,因此log.info()
調用將引發NullPointerException。
我需要那個記錄器,所以用newInstance()
創建類時是否有可能初始化注入的屬性?
還是有其他可能性基於字符串動態創建對象?
首先,您正在使用CDI,因此即使該文件完全為空,也需要將bean.xml文件放入META-INF中,否則它將無法正常工作。
例:
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
然后,您想注入記錄器,但是需要一個生產者,一個簡單的方法是:
public class LoggerProducer {
@Produces
public Logger produceLogger(InjectionPoint injectionPoint) {
return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
}
同樣重要的是,將暫時性關鍵字添加到您的logger屬性中,因為Produce無法產生不可序列化的實例。
public class classnameOne implements TestInterface{
@Inject
private transient Logger log;
// Some more functions and stuff
}
有趣的讀物:
更新
如果您堅持使用Class :: newInstance()方法,則可以通過以下方式進行操作:
向您的TestInterface添加一個方法,該方法將返回一個TestInterface對象,並將其命名為getInstance()
public interface TestInterface { public TestInterface getInstance(); }
在您的每個類中實現該方法
public class classnameOne implements TestInterface { @Inject private transient Logger log; public TestInterface getInstance() { return new classnameOne(); } }
只需將以前的代碼添加到使用構造函數檢索具體實例的新方法即可(將進行適當的依賴注入):
void doSth(classsuffix) throws Exception { String classname = "org.test.classname"+classsuffix; //classsuffix is dynamic Class<?> clazz; clazz = Class.forName(classname); TestInterface test = ((TestInterface) clazz.newInstance()).getInstance(); }
它不漂亮,聞起來很香,但它確實可以滿足您的要求。
PD:注入注釋不適用於Constructor :: newInstance()或Class :: newInstance(),因此我想這將是您想要做的最接近的方法。
AutowireCapableBeanFactory
可能對您的問題有所幫助,但請注意,這種方法有點臭,在典型的用例中不建議使用。 請參閱此鏈接:
使用CDI.current()對象:
class TestClass {
@Inject
ClassnameCollection collection; // Inject
void doSth(classsuffix) throws Exception {
dynamicObject = CDI.current().select(
(Class<TestInterface>) Class.forName("org.test.Classname" + suffix)).get();
dynamicObject.doStuff();
}
}
一個示例類供參考:
public class ClassnameOne implements TestInterface {
@Inject
private Logger log;
// ...
@Override
public void doStuff() {
// Do stuff
log.info("done");
}
}
使用此解決方案,無需對現有類或類似內容進行任何更改。
我可能找到的最佳解決方案是這樣的:
創建所有可用類的集合:
public class ClassnameCollection {
@Inject
public ClassnameOne classnameOne;
@Inject
public ClassnameTwo classnameTwo;
// ...
}
並將其注入您想要動態類的類中:
class TestClass {
@Inject
ClassnameCollection collection; // Inject
void doSth(classsuffix) throws Exception {
Class collectionClass = ClassnameCollection.class;
Field collectionField = collectionClass.getDeclaredField("classname" + suffix); // Get the declared field by String
TestInterface dynamicObject = (TestInterface) collectionField.get(collection); // There you have the dynamic object with all the subclasses (for example Logger) initialized
dynamicObject.doStuff();
}
}
一個示例類供參考:
public class ClassnameOne implements TestInterface {
@Inject
private Logger log;
// ...
@Override
public void doStuff() {
// Do stuff
log.info("done");
}
}
老實說,這是最好的解決方案,因為它不會更改任何子類,並且維護起來非常容易(只需在ClassnameCollection
類中添加一個新的Inject
)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.