有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java类。newInstance创建所有属性都为空的对象

我有一个类需要动态初始化,如下所示:

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()的类时,有没有可能初始化注入的属性

或者有没有其他基于字符串动态创建对象的可能性


共 (3) 个答案

  1. # 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属性添加transient关键字也很重要,因为products无法生成不可序列化的实例

    public class classnameOne implements TestInterface{
    
        @Inject
        private transient Logger log;
    
    // Some more functions and stuff
    
    }
    

    有趣的阅读:

    1. https://dzone.com/articles/cdi-di-p1
    2. http://www.devsniper.com/injectable-logger-with-cdi/

    更新

    如果您坚持使用Class::newInstance()方法,那么您可以通过以下方式进行:

    1. 向TestInterface添加一个方法,该方法将返回TestInterface对象并将其命名为getInstance()

      public interface TestInterface {
          public TestInterface getInstance();
      }
      
    2. 在每个类中实现该方法

      public class classnameOne implements TestInterface {
          @Inject
          private transient Logger log;
      
          public TestInterface getInstance() {
              return new classnameOne();
          }
      }
      
    3. 只需在以前的代码中添加使用构造函数检索具体实例的新方法(这将进行适当的依赖项注入):

      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:inject注释不适用于构造函数::newInstance(),也不适用于类::newInstance(),所以我想这将是最接近您想要做的事情

  2. # 3 楼答案

    我找到了更好的解决办法

    使用CDI。当前()对象:

    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