Singleton是一种需要注入身份验证和配置数据的服务。我以课程结束:
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()
方法和单例类签名。填写该课程的设计很糟糕,但不明白错误。
是否可以从getSingleton()
和synchronized
移除初始化并在初始化期间保留对异常的控制?
答案 0 :(得分:3)
这就是我编写它的方式,这样你就可以在需要时抛出异常,但仍然有一个线程安全的单例。
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.
}
}
答案 1 :(得分:2)
SingleService ss1 = SingleService.getInstance();
SingleService.init();
SingleService ss2 = SingleService.getInstance();
所以ss1
是一个与ss2
不同的对象,而不是Singleton的设计目标。如果ss1
随时被ss2
修改,{{1}}将不受影响。
答案 2 :(得分:1)
所有你不应该暴露对象创建方法的拳头。如果要检查某些内容,请使用断言或任何不会损坏实例对象的操作。
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;
}
答案 3 :(得分:0)
init()方法应该是私有的......
答案 4 :(得分:0)
我寻求解决的生产代码:
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;
}
}
抽象类具有通用的init
方法,该方法只能对完全初始化的具体类进行操作。内部静态类用于延迟实例化并执行init
调用。
无法通过超类构造函数应用init
方法来避免在每个实现中都需要调用init
。