Java 具有 init 方法的单例

Java singleton with init method

Singleton 是一项需要注入身份验证和配置数据的服务。我以 class:

结尾
class SingleService {
    private String conn;
    private String user;
    private String pass;

    private SingleService() {
        // Can throw exception!!
        conn = Config.getProperty("conn");
        user = Config.getProperty("user");
        pass = Config.getProperty("pass");
        // Can throw exception!!
        internalService = tryConnect(conn, user, pass);
    }

    private static SingleService instance;

    public static void init() {
        instance = new SingleService();
    }

    public static synchronized SingleService getInstance() {
        if (instance == null) init();
        return instance;
    }
}

专用 init() 方法用于应用程序启动期间的异常处理,以尽早检测到初始化错误,因为稍后我们只是调用 getInstance() 并且不希望得到错误:

class App {
    public static void main(String args[]) {
        try {
             Config.init("classpath:auth.properties");
             SingleService.init();
        } catch (Exception ex) {
            logger.error("Can't init SingleService...");
            System.exit()
        }
        doJob();
    }
    private static void doJob() {
        SingleService.getInstance().doJob();
    }
}

我担心 init() 方法和单例 class 签名。补一下 class 设计的不好但是不明白哪里出了问题

是否可以从 getSingleton()synchronized 中移除初始化并在初始化期间保留对异常的控制?

SingleService ss1 = SingleService.getInstance();
SingleService.init();
SingleService ss2 = SingleService.getInstance();

所以 ss1 是一个不同于 ss2 的对象,这不是 Singleton 的设计目的。如果 ss1 在任何时候被修改 ss2 将不受影响。

这就是我的编码方式,因此您可以在需要时抛出异常,但仍然有一个线程安全的单例。

enum SingleService {
    INSTANCE;

    private String conn;
    private String user;
    private String pass;
    private SingleService instance;

    public synchronized void init(Config config) throws SomeException {
        // don't leave in a half state if we fail.
        internalService = null; 

        conn = config.getProperty("conn");
        user = config.getProperty("user");
        pass = config.getProperty("pass");
        internalService = tryConnect(conn, user, pass);
    }

    public synchronized void methodForService() {
        if (internalService == null) throw new IllegalSateException();
        // do work.
    }
}

您绝对不会公开对象创建方法。如果您想检查某些东西,请使用断言或任何不会破坏实例对象的操作。

public static void checkIfValid() {
   assert Config.getProperty("conn");// do not corrupt instance object
   assert Config.getProperty("user");
   assert Config.getProperty("pass");
}

public static synchronized SingleService getInstance() {
    if (instance == null){ // only here you can initiate instance object
       instance = new SingleService();
    }
    return instance;
}

我寻找的问题的生产代码:

public abstract class AbstractCaller<Port> {

    abstract protected Port createPort();

    protected init() {
        Port port = createPort();
        // heavy use of introspection/reflection on **port** object.
        // Results are used later by **call** method.
    }

    public call() {
        // Reflection based on data collected by **init** method.
    }
}

public class ConcreteCaller extends AbstractCaller<ConcretePort> {

    private ConcreteService service = new ConcreteService();

    @Override
    protected ConcretePort createPort() {
        return service.getPort();
    }

    private static class Holder {
        public static ConcreteCaller INSTANCE;
        static {
            INSTANCE = new ConcreteCaller();
            INSTANCE.init();
        }
    }

    public static Caller getInstance() {
        return Holder.INSTANCE;
    }
}

抽象 class 具有通用 init 方法,只能在完全初始化的具体 class 上运行。内部静态 class 用于延迟实例化并执行 init 调用。

无法从 superclass 构造函数应用 init 方法来避免在每个实现中调用 init