class.newInstance 创建所有属性都为 null 的对象
class.newInstance creates Object with all properties being null
我有一个 class 需要像这样动态初始化:
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();
}
与示例配对 class(遵循相同模式的众多示例之一):
public class classnameOne implements TestInterface {
@Inject
private Logger log;
// ...
@Override
public void doStuff() {
// Do stuff
log.info("done");
}
}
问题是 classnameOne
class 中的 log
在初始化时将是 null
,因此 log.info()
调用将抛出 NullPointerException。
我需要那个记录器在那里,所以在用 newInstance()
创建 class 时是否有可能初始化注入的属性?
或者是否有任何其他可能基于字符串动态创建对象?
首先,你使用的是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());
}
}
将 transient 关键字添加到记录器属性也很重要,因为 produces 不能产生 non-serializable 个实例。
public class classnameOne implements TestInterface{
@Inject
private transient Logger log;
// Some more functions and stuff
}
有趣的读物:
更新
如果你坚持使用Class::newInstance()方法,那么你可以通过以下方式来实现:
向您的 TestInterface 添加一个方法,该方法将 return 一个 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
可能对您的问题有帮助,但请注意,这种方法有点难闻,不建议在典型用例中使用。参考这个link:
我找到了更好的解决方案
使用 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();
}
}
一个例子class供参考:
public class ClassnameOne implements TestInterface {
@Inject
private Logger log;
// ...
@Override
public void doStuff() {
// Do stuff
log.info("done");
}
}
使用此解决方案,无需对现有 classes 或类似内容进行任何更改。
旧版本
我可能找到的最佳解决方案是这样的:
创建所有可用的集合 classes:
public class ClassnameCollection {
@Inject
public ClassnameOne classnameOne;
@Inject
public ClassnameTwo classnameTwo;
// ...
}
并将其注入 class 你想要动态 class:
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();
}
}
一个例子class供参考:
public class ClassnameOne implements TestInterface {
@Inject
private Logger log;
// ...
@Override
public void doStuff() {
// Do stuff
log.info("done");
}
}
老实说,我认为这是最好的解决方案,因为它不会更改任何子classes,而且维护起来非常容易(只需在ClassnameCollection
class).
我有一个 class 需要像这样动态初始化:
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();
}
与示例配对 class(遵循相同模式的众多示例之一):
public class classnameOne implements TestInterface {
@Inject
private Logger log;
// ...
@Override
public void doStuff() {
// Do stuff
log.info("done");
}
}
问题是 classnameOne
class 中的 log
在初始化时将是 null
,因此 log.info()
调用将抛出 NullPointerException。
我需要那个记录器在那里,所以在用 newInstance()
创建 class 时是否有可能初始化注入的属性?
或者是否有任何其他可能基于字符串动态创建对象?
首先,你使用的是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());
}
}
将 transient 关键字添加到记录器属性也很重要,因为 produces 不能产生 non-serializable 个实例。
public class classnameOne implements TestInterface{
@Inject
private transient Logger log;
// Some more functions and stuff
}
有趣的读物:
更新
如果你坚持使用Class::newInstance()方法,那么你可以通过以下方式来实现:
向您的 TestInterface 添加一个方法,该方法将 return 一个 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
可能对您的问题有帮助,但请注意,这种方法有点难闻,不建议在典型用例中使用。参考这个link:
我找到了更好的解决方案
使用 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();
}
}
一个例子class供参考:
public class ClassnameOne implements TestInterface {
@Inject
private Logger log;
// ...
@Override
public void doStuff() {
// Do stuff
log.info("done");
}
}
使用此解决方案,无需对现有 classes 或类似内容进行任何更改。
旧版本
我可能找到的最佳解决方案是这样的:
创建所有可用的集合 classes:
public class ClassnameCollection {
@Inject
public ClassnameOne classnameOne;
@Inject
public ClassnameTwo classnameTwo;
// ...
}
并将其注入 class 你想要动态 class:
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();
}
}
一个例子class供参考:
public class ClassnameOne implements TestInterface {
@Inject
private Logger log;
// ...
@Override
public void doStuff() {
// Do stuff
log.info("done");
}
}
老实说,我认为这是最好的解决方案,因为它不会更改任何子classes,而且维护起来非常容易(只需在ClassnameCollection
class).