Java 如何从类名中获取作为字符串的单例实例

Java 如何从类名中获取作为字符串的单例实例,java,design-patterns,reflection,singleton,Java,Design Patterns,Reflection,Singleton,我遇到了一个奇怪的问题。我有一个接口,它的实现往往是无状态的。所以,我希望他们是单身 我将实现类名作为字符串。比如说 String clazz = "com.foo.Bar"; 我有一个规则工厂来获取IRule实现的实例 public class RulesFactory { private static final Logger logger = LoggerFactory.getLogger(RulesFactory.class); @SuppressWarnings(

我遇到了一个奇怪的问题。我有一个接口,它的实现往往是无状态的。所以,我希望他们是单身

我将实现类名作为字符串。比如说

String clazz = "com.foo.Bar";
我有一个规则工厂来获取
IRule
实现的实例

public class RulesFactory {

    private static final Logger logger = LoggerFactory.getLogger(RulesFactory.class);

    @SuppressWarnings("unchecked")
    public static <T extends IRule> T getRuleInstance(String clazz) {
        try {
            Class<?> ruleObject = Class.forName(clazz);
            Method factoryMethod = ruleObject.getMethod("getInstance");
            return (T) factoryMethod.invoke(null);
        } catch (ClassNotFoundException e) {
            logger.error("ClassNotFoundException", e);
        } catch (IllegalAccessException e) {
            logger.error("IllegalAccessException", e);
        } catch (SecurityException e) {
            logger.error("SecurityException", e);
        } catch (NoSuchMethodException e) {
            logger.error("NoSuchMethodException", e);
        } catch (IllegalArgumentException e) {
            logger.error("IllegalArgumentException", e);
        } catch (InvocationTargetException e) {
            logger.error("InvocationTargetException", e);
        }
        return null;
    }
}
公共类规则工厂{
私有静态最终记录器Logger=LoggerFactory.getLogger(RulesFactory.class);
@抑制警告(“未选中”)
公共静态T getRuleInstance(字符串clazz){
试一试{
Class ruleObject=Class.forName(clazz);
方法factoryMethod=ruleObject.getMethod(“getInstance”);
return(T)factoryMethod.invoke(null);
}catch(classnotfounde异常){
logger.error(“ClassNotFoundException”,e);
}捕获(非法访问例外e){
记录器错误(“IllegalacessException”,e);
}捕获(安全异常e){
logger.error(“SecurityException”,e);
}捕获(无此方法例外){
logger.error(“NoSuchMethodException”,e);
}捕获(IllegalArgumentException e){
logger.error(“IllegalArgumentException”,e);
}捕获(调用TargetException e){
logger.error(“InvocationTargetException”,e);
}
返回null;
}
}

如果类没有静态的
getInstance()
方法,上述代码将抛出
NullPointerException
。在Java6中,我不能在接口中使用静态方法。我不想创建多个
IRule
实现实例。如果我可以强制执行一个静态方法并调用该静态方法,我将获得已兑现的实例。但我无法做到这一点。如何解决这个问题?

有几种不同利弊的解决方案:

  • 不要使用
    静态方法。如果该方法不是静态的,则可以将其添加到
    IRule
    ,从而强制该方法存在
  • 检查
    factoryMethod
    的限定符,如果它们不是
    static
  • 对于解决方案#1,您需要一个
    映射
    。调用
    getRuleInstance()
    时,检查映射中的实例。如果没有,请使用接口中的方法创建一个,并将其放入映射中。这样,就可以使实例成为单例

    同时,您可以获取实例的所有字段,并确保所有字段都是
    final
    ,以强制执行无状态状态

    如果应用程序是多线程的,请确保使用并发映射并正确同步

    示例代码:

    private Map<String, IRule> rules = Maps.newHashMap();
    
    public static <T extends IRule> T getRuleInstance(String clazz) {
        try {
            synchronized( rules ) {
                IRule result = rules.get(clazz);
                if(null == result) {
                    result = clazz.newInstance();
                    rules.put(clazz, result);
                }
                @SuppressWarnings("unchecked")
                T tmp = (T) result;
                return tmp;
            }
        } catch (Exception e) {
            log( "Unable to create IRule for {}", clazz );
        }
    }
    
    private Map rules=Maps.newHashMap();
    公共静态T getRuleInstance(字符串clazz){
    试一试{
    已同步(规则){
    IRule result=rules.get(clazz);
    if(null==结果){
    结果=clazz.newInstance();
    规则。放置(clazz,result);
    }
    @抑制警告(“未选中”)
    T tmp=(T)结果;
    返回tmp;
    }
    }捕获(例外e){
    日志(“无法为{}创建IRule”,clazz);
    }
    }
    
    你让你的生活变得不必要的艰难

    如果您还记得伟大的“
    enum
    singleton模式”,并要求所有实现者都使用它,例如

    public enum Foo implements IRule
    {
      INSTANCE;
    
      // IRule implementation methods here
    
      public String toString() { return "the sole Foo instance"; }
    }
    
    整个
    RulesFactory
    变得非常简单:

    private static final ConcurrentMap<String, IRule> instancesMap
                                                 = new ConcurrentHashMap<String, IRule>();
    
    public static IRule getRuleInstance(String clazz) {
      try {
          IRule iRuleInstance=instancesMap.get(clazz);
          if(iRuleInstance!=null) return iRuleInstance;
          Class<? extends IRule> ruleObject=Class.forName(clazz).asSubclass(IRule.class);
          IRule[] enumConstants=ruleObject.getEnumConstants();
          if(enumConstants==null || enumConstants.length!=1) {
            logger.error("InvalidClassException",
                         new InvalidClassException(clazz, "not a singleton enum"));
            return null;
          }
          iRuleInstance=enumConstants[0];
          instancesMap.put(clazz, iRuleInstance);
          return iRuleInstance;
        } catch (ClassNotFoundException e) {
            logger.error("ClassNotFoundException", e);
        }
        return null;
    }
    
    私有静态最终ConcurrentMap实例映射
    =新的ConcurrentHashMap();
    公共静态IRule getRuleInstance(字符串clazz){
    试一试{
    IRule iRuleInstance=instancesMap.get(clazz);
    如果(iRuleInstance!=null)返回iRuleInstance;
    
    CLASS另一个想法:让构造函数包的实现受到保护。让它们与RulesFactory位于同一个包中,然后基于clazz创建“new”实现并按类名将其存储在映射中。在创建新实例之前,请始终检查映射。如果方法是静态的,则Null是要传递以调用的有效值。因此,请检查
    getInstance()
    methods.@Kayaman是的。它是有效的。但是如何强制客户机实现getInstance,以及如何将其作为静态方法来实现呢?请使用文档和良好的编程实践来强制执行它。@BRS看到您对Kayaman的回复,我觉得您应该编辑您的问题,将其作为您的关注点,而不是NPE。我认为,它已经变成了一个“鸡和e”选择一的问题。除非我有一个实例,否则我不能调用invoke方法,然后调用getInstance()就没有意义了.关于如何解决这个问题有什么想法吗?实际上我正在尝试使用Bill Pugh模式实例化实现。因此,创建两个实例会带来麻烦。一个用于调用invoke,另一个通过调用getInstance()。对于#1,调用
    ruleObject.newInstance()
    。实际上,您应该将
    ruleObject
    重命名为
    ruleType
    。但是,我没有正确使用该接口吗?:有解决方案吗?有两种解决方案:1.您可以使用一个新的接口
    IRuleFactory
    ,它创建
    IRule
    的实例。这样,您可以混合使用有状态和无状态规则(工厂可以返回相同的实例,也可以创建新实例)。2.不调用
    getInstance()
    ,而是在
    getRuleInstance()
    中创建实例并将其放入映射中。