public class Logger { private FileWriter writer; public Logger() { File file = new File("/Users/wangzheng/log.txt"); writer = new FileWriter(file, true); //true表示追加写入 } public void log(String message) { writer.write(mesasge); } }
// Logger类的应用示例: public class UserController { private Logger logger = new Logger(); public void login(String username, String password) { // ...省略业务逻辑代码... logger.log(username + " logined!"); } }
public class OrderController { private Logger logger = new Logger(); public void create(OrderVo order) { // ...省略业务逻辑代码... logger.log("Created an order: " + order.toString()); } }
public class Logger { private FileWriter writer; private static final Logger instance = new Logger();
private Logger() { File file = new File("/Users/wangzheng/log.txt"); writer = new FileWriter(file, true); //true表示追加写入 } public static Logger getInstance() { return instance; } public void log(String message) { writer.write(mesasge); } }
// Logger类的应用示例: public class UserController { public void login(String username, String password) { // ...省略业务逻辑代码... Logger.getInstance().log(username + " logined!"); } }
public class OrderController { public void create(OrderVo order) { // ...省略业务逻辑代码... Logger.getInstance().log("Created a order: " + order.toString()); } }
表示全局唯一类
从业务概念上,有些数据在系统中只应该保存一份,就比较适合设计为单例类。比如,系统的配置信息类。
案例:实现一个唯一递增 ID 号码生成器
如果程序中有两个对象,那就会存在生成重复 ID 的情况,所以,我们应该将 ID 生成器类设计为单例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
import java.util.concurrent.atomic.AtomicLong; public class IdGenerator { // AtomicLong是一个Java并发库中提供的一个原子变量类型, // 它将一些线程不安全需要加锁的复合操作封装为了线程安全的原子操作, // 比如下面会用到的incrementAndGet(). private AtomicLong id = new AtomicLong(0); private static final IdGenerator instance = new IdGenerator(); private IdGenerator() {} public static IdGenerator getInstance() { return instance; } public long getId() { return id.incrementAndGet(); } }
// IdGenerator使用举例 long id = IdGenerator.getInstance().getId();
public class IdGenerator { private AtomicLong id = new AtomicLong(0); private static final IdGenerator instance = new IdGenerator(); private IdGenerator() {} public static IdGenerator getInstance() { return instance; } public long getId() { return id.incrementAndGet(); } }
public class IdGenerator { private AtomicLong id = new AtomicLong(0); private static IdGenerator instance; private IdGenerator() {} public static synchronized IdGenerator getInstance() { if (instance == null) { instance = new IdGenerator(); } return instance; } public long getId() { return id.incrementAndGet(); } }
public class IdGenerator { private AtomicLong id = new AtomicLong(0); private IdGenerator() {}
private static class SingletonHolder{ private static final IdGenerator instance = new IdGenerator(); } public static IdGenerator getInstance() { return SingletonHolder.instance; } public long getId() { return id.incrementAndGet(); } }
Using localRef, we are reducing the access of volatile variable to just one for positive usecase. If we do not use localRef, then we would have to access volatile variable twice - once for checking null and then at method return time. Accessing volatile memory is quite an expensive affair because it involves reaching out to main memory. 参考链接:https://www.javacodemonk.com/threadsafe-singleton-design-pattern-java-806ad7e6
单例存在哪些问题?
单例对 OOP 特性的支持不友好
单例会隐藏类之间的依赖关系
单例对代码的扩展性不友好
单例对代码的可测试性不友好
单例不支持有参数的构造函数
有何替代的解决方案?
为了保证全局唯一,除了使用单例,我们还可以用静态方法来实现。不过,静态方法这种实现思路,并不能解决我们之前提到的问题。如果要完全解决这些问题,我们可能要从根上,寻找其他方式来实现全局唯一类了。比如,通过工厂模式、IOC 容器(比如 Spring IOC 容器)来保证,由程序员自己来保证(自己在编写代码的时候自己保证不要创建两个类对象)。
如果单例类并没有后续扩展的需求,并且不依赖外部系统,那设计成单例类就没有太大问题。对于一些全局的类,我们在其他地方 new 的话,还要在类之间传来传去,不如直接做成单例类,使用起来简洁方便。
public class IdGenerator { private AtomicLong id = new AtomicLong(0);
private static final ConcurrentHashMap<Long, IdGenerator> instances = new ConcurrentHashMap<>();
private IdGenerator() {}
public static IdGenerator getInstance() { Long currentThreadId = Thread.currentThread().getId(); instances.putIfAbsent(currentThreadId, new IdGenerator()); return instances.get(currentThreadId); }
public long getId() { return id.incrementAndGet(); } }
public class BackendServer { private long serverNo; private String serverAddress;
private static final int SERVER_COUNT = 3; private static final Map<Long, BackendServer> serverInstances = new HashMap<>();
static { serverInstances.put(1L, new BackendServer(1L, "192.134.22.138:8080")); serverInstances.put(2L, new BackendServer(2L, "192.134.22.139:8080")); serverInstances.put(3L, new BackendServer(3L, "192.134.22.140:8080")); }
public class RuleConfigSource { public RuleConfig load(String ruleConfigFilePath) { String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath); IRuleConfigParser parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension); if (parser == null) { throw new InvalidRuleConfigException( "Rule config file format is not supported: " + ruleConfigFilePath); }
public class RuleConfigParserFactory { public static IRuleConfigParser createParser(String configFormat) { IRuleConfigParser parser = null; if ("json".equalsIgnoreCase(configFormat)) { parser = new JsonRuleConfigParser(); } else if ("xml".equalsIgnoreCase(configFormat)) { parser = new XmlRuleConfigParser(); } else if ("yaml".equalsIgnoreCase(configFormat)) { parser = new YamlRuleConfigParser(); } else if ("properties".equalsIgnoreCase(configFormat)) { parser = new PropertiesRuleConfigParser(); } return parser; } }
public class RuleConfigParserFactory { private static final Map<String, RuleConfigParser> cachedParsers = new HashMap<>();
static { cachedParsers.put("json", new JsonRuleConfigParser()); cachedParsers.put("xml", new XmlRuleConfigParser()); cachedParsers.put("yaml", new YamlRuleConfigParser()); cachedParsers.put("properties", new PropertiesRuleConfigParser()); }
public interface IRuleConfigParserFactory { IRuleConfigParser createParser(); }
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory { @Override public IRuleConfigParser createParser() { return new JsonRuleConfigParser(); } }
public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory { @Override public IRuleConfigParser createParser() { return new XmlRuleConfigParser(); } }
public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory { @Override public IRuleConfigParser createParser() { return new YamlRuleConfigParser(); } }
public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory { @Override public IRuleConfigParser createParser() { return new PropertiesRuleConfigParser(); } }
public class RuleConfigSource { public RuleConfig load(String ruleConfigFilePath) { String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
IRuleConfigParserFactory parserFactory = RuleConfigParserFactoryMap.getParserFactory(ruleConfigFileExtension); if (parserFactory == null) { throw new InvalidRuleConfigException("Rule config file format is not supported: " + ruleConfigFilePath); } IRuleConfigParser parser = parserFactory.createParser();
//因为工厂类只包含方法,不包含成员变量,完全可以复用, //不需要每次都创建新的工厂类对象,所以,简单工厂模式的第二种实现思路更加合适。 public class RuleConfigParserFactoryMap { //工厂的工厂 private static final Map<String, IRuleConfigParserFactory> cachedFactories = new HashMap<>();
static { cachedFactories.put("json", new JsonRuleConfigParserFactory()); cachedFactories.put("xml", new XmlRuleConfigParserFactory()); cachedFactories.put("yaml", new YamlRuleConfigParserFactory()); cachedFactories.put("properties", new PropertiesRuleConfigParserFactory()); }
public interface IConfigParserFactory { IRuleConfigParser createRuleParser(); ISystemConfigParser createSystemParser(); //此处可以扩展新的parser类型,比如IBizConfigParser }
public class JsonConfigParserFactory implements IConfigParserFactory { @Override public IRuleConfigParser createRuleParser() { return new JsonRuleConfigParser(); }
@Override public ISystemConfigParser createSystemParser() { return new JsonSystemConfigParser(); } }
public class XmlConfigParserFactory implements IConfigParserFactory { @Override public IRuleConfigParser createRuleParser() { return new XmlRuleConfigParser(); }
@Override public ISystemConfigParser createSystemParser() { return new XmlSystemConfigParser(); } }
public interface ApplicationContext { Object getBean(String beanId); }
public class ClassPathXmlApplicationContext implements ApplicationContext { private BeansFactory beansFactory; private BeanConfigParser beanConfigParser;
public ClassPathXmlApplicationContext(String configLocation) { this.beansFactory = new BeansFactory(); this.beanConfigParser = new XmlBeanConfigParser(); loadBeanDefinitions(configLocation); }
private void loadBeanDefinitions(String configLocation) { InputStream in = null; try { in = this.getClass().getResourceAsStream("/" + configLocation); if (in == null) { throw new RuntimeException("Can not find config file: " + configLocation); } List<BeanDefinition> beanDefinitions = beanConfigParser.parse(in); beansFactory.addBeanDefinitions(beanDefinitions); } finally { if (in != null) { try { in.close(); } catch (IOException e) { // TODO: log error } } } }
@Override public Object getBean(String beanId) { return beansFactory.getBean(beanId); } }
public class BeansFactory { private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, BeanDefinition> beanDefinitions = new ConcurrentHashMap<>();
public void addBeanDefinitions(List<BeanDefinition> beanDefinitionList) { for (BeanDefinition beanDefinition : beanDefinitionList) { this.beanDefinitions.putIfAbsent(beanDefinition.getId(), beanDefinition); }
for (BeanDefinition beanDefinition : beanDefinitionList) { if (beanDefinition.isLazyInit() == false && beanDefinition.isSingleton()) { createBean(beanDefinition); } } }
public Object getBean(String beanId) { BeanDefinition beanDefinition = beanDefinitions.get(beanId); if (beanDefinition == null) { throw new NoSuchBeanDefinitionException("Bean is not defined: " + beanId); } return createBean(beanDefinition); }
//我们将Builder类设计成了ResourcePoolConfig的内部类。 //我们也可以将Builder类设计成独立的非内部类ResourcePoolConfigBuilder。 public static class Builder { private static final int DEFAULT_MAX_TOTAL = 8; private static final int DEFAULT_MAX_IDLE = 8; private static final int DEFAULT_MIN_IDLE = 0;
private String name; private int maxTotal = DEFAULT_MAX_TOTAL; private int maxIdle = DEFAULT_MAX_IDLE; private int minIdle = DEFAULT_MIN_IDLE;
public ResourcePoolConfig build() { // 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等 if (StringUtils.isBlank(name)) { throw new IllegalArgumentException("..."); } if (maxIdle > maxTotal) { throw new IllegalArgumentException("..."); } if (minIdle > maxTotal || minIdle > maxIdle) { throw new IllegalArgumentException("..."); }
return new ResourcePoolConfig(this); }
public Builder setName(String name) { if (StringUtils.isBlank(name)) { throw new IllegalArgumentException("..."); } this.name = name; return this; }
public Builder setMaxTotal(int maxTotal) { if (maxTotal <= 0) { throw new IllegalArgumentException("..."); } this.maxTotal = maxTotal; return this; }
public Builder setMaxIdle(int maxIdle) { if (maxIdle < 0) { throw new IllegalArgumentException("..."); } this.maxIdle = maxIdle; return this; }
public Builder setMinIdle(int minIdle) { if (minIdle < 0) { throw new IllegalArgumentException("..."); } this.minIdle = minIdle; return this; } } }
public Object deepCopy(Object object) { ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(object); ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); return oi.readObject(); }