我有一个需要像这样动态初始化的类:
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");
}
}
问题在于log
类中的classnameOne
在初始化时将被null
进行,因此log.info()
调用将引发NullPointerException。
我需要那个记录器,所以在用newInstance()
创建类时是否有可能初始化注入的属性?
或者还有其他可能性可以基于字符串动态创建对象吗?
答案 0 :(得分:1)
首先,您正在使用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(),所以我想这将是您想要做的最接近的方法。
答案 1 :(得分:0)
AutowireCapableBeanFactory
可能对您的问题有所帮助,但请注意,这种方法有点臭,不建议在典型的用例中使用。请参阅此链接:
答案 2 :(得分:0)
使用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");
}
}
老实说,这是最好的解决方案,因为它不会更改任何子类,并且维护非常简单(只需在Inject
类中添加新的ClassnameCollection
)。